fix compile error on FBSD
[oweals/gnunet.git] / src / transport / plugin_transport_tcp.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file transport/plugin_transport_tcp.c
22  * @brief Implementation of the TCP transport service
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_hello_lib.h"
27 #include "gnunet_connection_lib.h"
28 #include "gnunet_container_lib.h"
29 #include "gnunet_os_lib.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_resolver_service.h"
32 #include "gnunet_server_lib.h"
33 #include "gnunet_service_lib.h"
34 #include "gnunet_signatures.h"
35 #include "gnunet_statistics_service.h"
36 #include "gnunet_transport_service.h"
37 #include "plugin_transport.h"
38 #include "transport.h"
39
40 #define DEBUG_TCP GNUNET_NO
41
42 /**
43  * How long until we give up on transmitting the welcome message?
44  */
45 #define HOSTNAME_RESOLVE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
46
47
48 /**
49  * Initial handshake message for a session.
50  */
51 struct WelcomeMessage
52 {
53   /**
54    * Type is GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME.
55    */
56   struct GNUNET_MessageHeader header;
57
58   /**
59    * Identity of the node connecting (TCP client)
60    */
61   struct GNUNET_PeerIdentity clientIdentity;
62
63 };
64
65
66 /**
67  * Network format for IPv4 addresses.
68  */
69 struct IPv4TcpAddress
70 {
71   /**
72    * IPv4 address, in network byte order.
73    */
74   uint32_t ipv4_addr;
75
76   /**
77    * Port number, in network byte order.
78    */
79   uint16_t t_port;
80
81 };
82
83
84 /**
85  * Network format for IPv6 addresses.
86  */
87 struct IPv6TcpAddress
88 {
89   /**
90    * IPv6 address.
91    */
92   struct in6_addr ipv6_addr;
93
94   /**
95    * Port number, in network byte order.
96    */
97   uint16_t t6_port;
98
99 };
100
101
102 /**
103  * Encapsulation of all of the state of the plugin.
104  */
105 struct Plugin;
106
107
108 /**
109  * Information kept for each message that is yet to
110  * be transmitted.
111  */
112 struct PendingMessage
113 {
114
115   /**
116    * This is a doubly-linked list.
117    */
118   struct PendingMessage *next;
119
120   /**
121    * This is a doubly-linked list.
122    */
123   struct PendingMessage *prev;
124
125   /**
126    * The pending message
127    */
128   const char *msg;
129
130   /**
131    * Continuation function to call once the message
132    * has been sent.  Can be NULL if there is no
133    * continuation to call.
134    */
135   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
136
137   /**
138    * Closure for transmit_cont.
139    */
140   void *transmit_cont_cls;
141
142   /**
143    * Timeout value for the pending message.
144    */
145   struct GNUNET_TIME_Absolute timeout;
146
147   /**
148    * So that the gnunet-service-transport can group messages together,
149    * these pending messages need to accept a message buffer and size
150    * instead of just a GNUNET_MessageHeader.
151    */
152   size_t message_size;
153
154 };
155
156
157 /**
158  * Session handle for TCP connections.
159  */
160 struct Session
161 {
162
163   /**
164    * Stored in a linked list.
165    */
166   struct Session *next;
167
168   /**
169    * Pointer to the global plugin struct.
170    */
171   struct Plugin *plugin;
172
173   /**
174    * The client (used to identify this connection)
175    */
176   struct GNUNET_SERVER_Client *client;
177
178   /**
179    * Messages currently pending for transmission
180    * to this peer, if any.
181    */
182   struct PendingMessage *pending_messages_head;
183
184   /**
185    * Messages currently pending for transmission
186    * to this peer, if any.
187    */
188   struct PendingMessage *pending_messages_tail;
189
190   /**
191    * Handle for pending transmission request.
192    */
193   struct GNUNET_CONNECTION_TransmitHandle *transmit_handle;
194
195   /**
196    * To whom are we talking to (set to our identity
197    * if we are still waiting for the welcome message)
198    */
199   struct GNUNET_PeerIdentity target;
200
201   /**
202    * ID of task used to delay receiving more to throttle sender.
203    */
204   GNUNET_SCHEDULER_TaskIdentifier receive_delay_task;
205
206   /**
207    * Address of the other peer (either based on our 'connect'
208    * call or on our 'accept' call).
209    */
210   void *connect_addr;
211
212   /**
213    * Last activity on this connection.  Used to select preferred
214    * connection.
215    */
216   struct GNUNET_TIME_Absolute last_activity;
217
218   /**
219    * Length of connect_addr.
220    */
221   size_t connect_alen;
222
223   /**
224    * Are we still expecting the welcome message? (GNUNET_YES/GNUNET_NO)
225    */
226   int expecting_welcome;
227
228   /**
229    * Was this a connection that was inbound (we accepted)? (GNUNET_YES/GNUNET_NO)
230    */
231   int inbound;
232
233 };
234
235
236 /**
237  * Encapsulation of all of the state of the plugin.
238  */
239 struct Plugin
240 {
241   /**
242    * Our environment.
243    */
244   struct GNUNET_TRANSPORT_PluginEnvironment *env;
245
246   /**
247    * The listen socket.
248    */
249   struct GNUNET_CONNECTION_Handle *lsock;
250
251   /**
252    * List of open TCP sessions.
253    */
254   struct Session *sessions;
255
256   /**
257    * Handle to the network service.
258    */
259   struct GNUNET_SERVICE_Context *service;
260
261   /**
262    * Handle to the server for this service.
263    */
264   struct GNUNET_SERVER_Handle *server;
265
266   /**
267    * Copy of the handler array where the closures are
268    * set to this struct's instance.
269    */
270   struct GNUNET_SERVER_MessageHandler *handlers;
271
272   /**
273    * Handle for request of hostname resolution, non-NULL if pending.
274    */
275   struct GNUNET_RESOLVER_RequestHandle *hostname_dns;
276
277   /**
278    * ID of task used to update our addresses when one expires.
279    */
280   GNUNET_SCHEDULER_TaskIdentifier address_update_task;
281
282   /**
283    * Port that we are actually listening on.
284    */
285   uint16_t open_port;
286
287   /**
288    * Port that the user said we would have visible to the
289    * rest of the world.
290    */
291   uint16_t adv_port;
292
293 };
294
295
296
297
298 /**
299  * Function called for a quick conversion of the binary address to
300  * a numeric address.  Note that the caller must not free the 
301  * address and that the next call to this function is allowed
302  * to override the address again.
303  *
304  * @param cls closure ('struct Plugin*')
305  * @param addr binary address
306  * @param addrlen length of the address
307  * @return string representing the same address 
308  */
309 static const char* 
310 tcp_address_to_string (void *cls,
311                        const void *addr,
312                        size_t addrlen)
313 {
314   static char rbuf[INET6_ADDRSTRLEN + 12];
315   char buf[INET6_ADDRSTRLEN];
316   const void *sb;
317   struct in_addr a4;
318   struct in6_addr a6;
319   const struct IPv4TcpAddress *t4;
320   const struct IPv6TcpAddress *t6;
321   int af;
322   uint16_t port;
323
324   if (addrlen == sizeof (struct IPv6TcpAddress))
325     {
326       t6 = addr;
327       af = AF_INET6;
328       port = ntohs (t6->t6_port);
329       memcpy (&a6, &t6->ipv6_addr, sizeof (a6));
330       sb = &a6;
331     }
332   else if (addrlen == sizeof (struct IPv4TcpAddress))
333     {
334       t4 = addr;
335       af = AF_INET;
336       port = ntohs (t4->t_port);
337       memcpy (&a4, &t4->ipv4_addr, sizeof (a4));
338       sb = &a4;
339     }
340   else
341     {
342       GNUNET_break_op (0);
343       return NULL;
344     }
345   inet_ntop (af, sb, buf, INET6_ADDRSTRLEN);
346   GNUNET_snprintf (rbuf,
347                    sizeof (rbuf),
348                    (af == AF_INET6) ? "[%s]:%u" : "%s:%u",
349                    buf,
350                    port);
351   return rbuf;
352 }
353
354
355 /**
356  * Find the session handle for the given client.
357  *
358  * @return NULL if no matching session exists
359  */
360 static struct Session *
361 find_session_by_client (struct Plugin *plugin,
362                         const struct GNUNET_SERVER_Client *client)
363 {
364   struct Session *ret;
365
366   ret = plugin->sessions;
367   while ((ret != NULL) && (client != ret->client))
368     ret = ret->next;
369   return ret;
370 }
371
372
373 /**
374  * Create a new session.  Also queues a welcome message.
375  *
376  * @param plugin us
377  * @param target peer to connect to
378  * @param client client to use
379  * @return new session object
380  */
381 static struct Session *
382 create_session (struct Plugin *plugin,
383                 const struct GNUNET_PeerIdentity *target,
384                 struct GNUNET_SERVER_Client *client)
385 {
386   struct Session *ret;
387   struct PendingMessage *pm;
388   struct WelcomeMessage welcome;
389
390   GNUNET_assert (client != NULL);
391 #if DEBUG_TCP
392   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
393                    "tcp",
394                    "Creating new session for peer `%4s'\n",
395                    GNUNET_i2s (target));
396 #endif
397   ret = GNUNET_malloc (sizeof (struct Session));
398   ret->last_activity = GNUNET_TIME_absolute_get ();
399   ret->plugin = plugin;
400   ret->next = plugin->sessions;
401   plugin->sessions = ret;
402   ret->client = client;
403   ret->target = *target;
404   ret->expecting_welcome = GNUNET_YES;
405   pm = GNUNET_malloc (sizeof (struct PendingMessage) + sizeof (struct WelcomeMessage));
406   pm->msg = (const char*) &pm[1];
407   pm->message_size = sizeof (struct WelcomeMessage);
408   welcome.header.size = htons (sizeof (struct WelcomeMessage));
409   welcome.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME);
410   welcome.clientIdentity = *plugin->env->my_identity;
411   memcpy (&pm[1], &welcome, sizeof (welcome));
412   pm->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
413   GNUNET_STATISTICS_update (plugin->env->stats,
414                             gettext_noop ("# bytes currently in TCP buffers"),
415                             pm->message_size,
416                             GNUNET_NO);      
417   GNUNET_CONTAINER_DLL_insert (ret->pending_messages_head,
418                                ret->pending_messages_tail,
419                                pm);
420   GNUNET_STATISTICS_update (plugin->env->stats,
421                             gettext_noop ("# TCP sessions active"),
422                             1,
423                             GNUNET_NO);      
424   return ret;
425 }
426
427
428 /**
429  * If we have pending messages, ask the server to
430  * transmit them (schedule the respective tasks, etc.)
431  *
432  * @param session for which session should we do this
433  */
434 static void process_pending_messages (struct Session *session);
435
436
437 /**
438  * Function called to notify a client about the socket
439  * being ready to queue more data.  "buf" will be
440  * NULL and "size" zero if the socket was closed for
441  * writing in the meantime.
442  *
443  * @param cls closure
444  * @param size number of bytes available in buf
445  * @param buf where the callee should write the message
446  * @return number of bytes written to buf
447  */
448 static size_t
449 do_transmit (void *cls, size_t size, void *buf)
450 {
451   struct Session *session = cls;
452   struct GNUNET_PeerIdentity pid;
453   struct Plugin *plugin;
454   struct PendingMessage *pos;
455   struct PendingMessage *hd;
456   struct PendingMessage *tl;
457   struct GNUNET_TIME_Absolute now;
458   char *cbuf;
459   size_t ret;
460
461   session->transmit_handle = NULL;
462   plugin = session->plugin;
463   if (buf == NULL)
464     {
465 #if DEBUG_TCP
466       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
467                        "tcp",
468                        "Timeout trying to transmit to peer `%4s', discarding message queue.\n",
469                        GNUNET_i2s (&session->target));
470 #endif
471       /* timeout; cancel all messages that have already expired */
472       hd = NULL;
473       tl = NULL;
474       ret = 0;
475       now = GNUNET_TIME_absolute_get ();
476       while ( (NULL != (pos = session->pending_messages_head)) &&
477               (pos->timeout.value <= now.value) )
478         {
479           GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
480                                        session->pending_messages_tail,
481                                        pos);
482 #if DEBUG_TCP
483           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
484                            "tcp",
485                            "Failed to transmit %u byte message to `%4s'.\n",
486                            pos->message_size,
487                            GNUNET_i2s (&session->target));
488 #endif
489           ret += pos->message_size;
490           GNUNET_CONTAINER_DLL_insert_after (hd, tl, tl, pos);
491         }
492       /* do this call before callbacks (so that if callbacks destroy
493          session, they have a chance to cancel actions done by this
494          call) */
495       process_pending_messages (session);  
496       pid = session->target;
497       /* no do callbacks and do not use session again since
498          the callbacks may abort the session */
499       while (NULL != (pos = hd))
500         {
501           GNUNET_CONTAINER_DLL_remove (hd, tl, pos);
502           if (pos->transmit_cont != NULL)
503             pos->transmit_cont (pos->transmit_cont_cls,
504                                 &pid, GNUNET_SYSERR);
505           GNUNET_free (pos);
506         }
507       GNUNET_STATISTICS_update (plugin->env->stats,
508                                 gettext_noop ("# bytes currently in TCP buffers"),
509                                 - (int64_t) ret,
510                                 GNUNET_NO); 
511       GNUNET_STATISTICS_update (plugin->env->stats,
512                                 gettext_noop ("# bytes discarded by TCP (timeout)"),
513                                 ret,
514                                 GNUNET_NO);      
515       return 0;
516     }
517   /* copy all pending messages that would fit */
518   ret = 0;
519   cbuf = buf;
520   hd = NULL;
521   tl = NULL;
522   while (NULL != (pos = session->pending_messages_head)) 
523     {
524       if (ret + pos->message_size > size) 
525         break;
526       GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
527                                    session->pending_messages_tail,
528                                    pos);
529       GNUNET_assert (size >= pos->message_size);
530       memcpy (cbuf, pos->msg, pos->message_size);
531       cbuf += pos->message_size;
532       ret += pos->message_size;
533       size -= pos->message_size;
534       GNUNET_CONTAINER_DLL_insert_after (hd, tl, tl, pos);
535     }
536   /* schedule 'continuation' before callbacks so that callbacks that
537      cancel everything don't cause us to use a session that no longer
538      exists... */
539   process_pending_messages (session);  
540   session->last_activity = GNUNET_TIME_absolute_get ();
541   pid = session->target;
542   /* we'll now call callbacks that may cancel the session; hence
543      we should not use 'session' after this point */
544   while (NULL != (pos = hd))
545     {
546       GNUNET_CONTAINER_DLL_remove (hd, tl, pos);
547       if (pos->transmit_cont != NULL)
548         pos->transmit_cont (pos->transmit_cont_cls,
549                             &pid, GNUNET_OK);
550       GNUNET_free (pos);
551     }
552   GNUNET_assert (hd == NULL);
553   GNUNET_assert (tl == NULL);
554 #if DEBUG_TCP > 1
555   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
556                    "tcp", "Transmitting %u bytes\n", ret);
557 #endif
558   GNUNET_STATISTICS_update (plugin->env->stats,
559                             gettext_noop ("# bytes currently in TCP buffers"),
560                             - (int64_t) ret,
561                             GNUNET_NO);       
562   GNUNET_STATISTICS_update (plugin->env->stats,
563                             gettext_noop ("# bytes transmitted via TCP"),
564                             ret,
565                             GNUNET_NO);      
566   return ret;
567 }
568
569
570 /**
571  * If we have pending messages, ask the server to
572  * transmit them (schedule the respective tasks, etc.)
573  *
574  * @param session for which session should we do this
575  */
576 static void
577 process_pending_messages (struct Session *session)
578 {
579   struct PendingMessage *pm;
580
581   GNUNET_assert (session->client != NULL);
582   if (session->transmit_handle != NULL)
583     return;
584   if (NULL == (pm = session->pending_messages_head))
585     return;
586   session->transmit_handle
587     = GNUNET_SERVER_notify_transmit_ready (session->client,
588                                            pm->message_size,
589                                            GNUNET_TIME_absolute_get_remaining
590                                            (pm->timeout),
591                                            &do_transmit, session);
592 }
593
594
595 /**
596  * Functions with this signature are called whenever we need
597  * to close a session due to a disconnect or failure to
598  * establish a connection.
599  *
600  * @param session session to close down
601  */
602 static void
603 disconnect_session (struct Session *session)
604 {
605   struct Session *prev;
606   struct Session *pos;
607   struct PendingMessage *pm;
608
609 #if DEBUG_TCP
610   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
611                    "tcp",
612                    "Disconnecting from `%4s' at %s (session %p).\n",
613                    GNUNET_i2s (&session->target),
614                    (session->connect_addr != NULL) ?
615                    tcp_address_to_string (session->plugin,
616                                           session->connect_addr,
617                                           session->connect_alen) : "*", 
618                    session);
619 #endif
620   /* remove from session list */
621   prev = NULL;
622   pos = session->plugin->sessions;
623   while (pos != session)
624     {
625       prev = pos;
626       pos = pos->next;
627     }
628   if (prev == NULL)
629     session->plugin->sessions = session->next;
630   else
631     prev->next = session->next;
632   session->plugin->env->session_end (session->plugin->env->cls,
633                             &session->target,
634                             session);
635   /* clean up state */
636   if (session->transmit_handle != NULL)
637     {
638       GNUNET_CONNECTION_notify_transmit_ready_cancel
639         (session->transmit_handle);
640       session->transmit_handle = NULL;
641     }
642   while (NULL != (pm = session->pending_messages_head))
643     {
644 #if DEBUG_TCP
645       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
646                        "tcp",
647                        pm->transmit_cont != NULL
648                        ? "Could not deliver message to `%4s'.\n"
649                        :
650                        "Could not deliver message to `%4s', notifying.\n",
651                        GNUNET_i2s (&session->target));
652 #endif
653       GNUNET_STATISTICS_update (session->plugin->env->stats,
654                                 gettext_noop ("# bytes currently in TCP buffers"),
655                                 - (int64_t) pm->message_size,
656                                 GNUNET_NO);      
657       GNUNET_STATISTICS_update (session->plugin->env->stats,
658                                 gettext_noop ("# bytes discarded by TCP (disconnect)"),
659                                 pm->message_size,
660                                 GNUNET_NO);      
661       GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
662                                    session->pending_messages_tail,
663                                    pm);
664       if (NULL != pm->transmit_cont)
665         pm->transmit_cont (pm->transmit_cont_cls,
666                            &session->target, GNUNET_SYSERR);
667       GNUNET_free (pm);
668     }
669   GNUNET_break (session->client != NULL);
670   if (session->receive_delay_task != GNUNET_SCHEDULER_NO_TASK)
671     {
672       GNUNET_SCHEDULER_cancel (session->plugin->env->sched,
673                                session->receive_delay_task);
674       GNUNET_SERVER_receive_done (session->client, 
675                                   GNUNET_SYSERR);       
676     }
677   GNUNET_SERVER_client_drop (session->client);
678   GNUNET_STATISTICS_update (session->plugin->env->stats,
679                             gettext_noop ("# TCP sessions active"),
680                             -1,
681                             GNUNET_NO);      
682   GNUNET_free_non_null (session->connect_addr);
683   GNUNET_free (session);
684 }
685
686
687 /**
688  * Given two otherwise equivalent sessions, pick the better one.
689  * 
690  * @param s1 one session (also default)
691  * @param s2 other session
692  * @return "better" session (more active)
693  */
694 static struct Session *
695 select_better_session (struct Session *s1,
696                        struct Session *s2)
697 {
698   if (s1 == NULL)
699     return s2;
700   if (s2 == NULL)
701     return s1;
702   if ( (s1->expecting_welcome == GNUNET_NO) &&
703        (s2->expecting_welcome == GNUNET_YES) )
704     return s1;
705   if ( (s1->expecting_welcome == GNUNET_YES) &&
706        (s2->expecting_welcome == GNUNET_NO) )
707     return s2;
708   if (s1->last_activity.value < s2->last_activity.value)
709     return s2;
710   if (s1->last_activity.value > s2->last_activity.value)
711     return s1;
712   if ( (GNUNET_YES == s1->inbound) &&
713        (GNUNET_NO  == s2->inbound) )
714     return s1;
715   if ( (GNUNET_NO  == s1->inbound) &&
716        (GNUNET_YES == s2->inbound) )
717     return s2;
718   return s1;
719 }
720
721
722 /**
723  * Function that can be used by the transport service to transmit
724  * a message using the plugin.   Note that in the case of a
725  * peer disconnecting, the continuation MUST be called
726  * prior to the disconnect notification itself.  This function
727  * will be called with this peer's HELLO message to initiate
728  * a fresh connection to another peer.
729  *
730  * @param cls closure
731  * @param target who should receive this message
732  * @param msg the message to transmit
733  * @param msgbuf_size number of bytes in 'msg'
734  * @param priority how important is the message (most plugins will
735  *                 ignore message priority and just FIFO)
736  * @param timeout how long to wait at most for the transmission (does not
737  *                require plugins to discard the message after the timeout,
738  *                just advisory for the desired delay; most plugins will ignore
739  *                this as well)
740  * @param session which session must be used (or NULL for "any")
741  * @param addr the address to use (can be NULL if the plugin
742  *                is "on its own" (i.e. re-use existing TCP connection))
743  * @param addrlen length of the address in bytes
744  * @param force_address GNUNET_YES if the plugin MUST use the given address,
745  *                GNUNET_NO means the plugin may use any other address and
746  *                GNUNET_SYSERR means that only reliable existing
747  *                bi-directional connections should be used (regardless
748  *                of address)
749  * @param cont continuation to call once the message has
750  *        been transmitted (or if the transport is ready
751  *        for the next transmission call; or if the
752  *        peer disconnected...); can be NULL
753  * @param cont_cls closure for cont
754  * @return number of bytes used (on the physical network, with overheads);
755  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
756  *         and does NOT mean that the message was not transmitted (DV)
757  */
758 static ssize_t
759 tcp_plugin_send (void *cls,
760                  const struct GNUNET_PeerIdentity *target,
761                  const char *msg,
762                  size_t msgbuf_size,
763                  uint32_t priority,
764                  struct GNUNET_TIME_Relative timeout,
765                  struct Session *session,
766                  const void *addr,
767                  size_t addrlen,
768                  int force_address,
769                  GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
770 {
771   struct Plugin *plugin = cls;
772   struct Session *cand_session;
773   struct Session *next;
774   struct PendingMessage *pm;
775   struct GNUNET_CONNECTION_Handle *sa;
776   int af;
777   const void *sb;
778   size_t sbs;
779   struct sockaddr_in a4;
780   struct sockaddr_in6 a6;
781   const struct IPv4TcpAddress *t4;
782   const struct IPv6TcpAddress *t6;
783
784   GNUNET_STATISTICS_update (plugin->env->stats,
785                             gettext_noop ("# bytes TCP was asked to transmit"),
786                             msgbuf_size,
787                             GNUNET_NO);      
788   /* FIXME: we could do this cheaper with a hash table
789      where we could restrict the iteration to entries that match
790      the target peer... */
791   if (session == NULL)
792     {
793       cand_session = NULL;
794       next = plugin->sessions;
795       while (NULL != (session = next)) 
796         {
797           next = session->next;
798           GNUNET_assert (session->client != NULL);
799           if (0 != memcmp (target,
800                            &session->target, 
801                            sizeof (struct GNUNET_PeerIdentity)))
802             continue;
803           if ( ( (GNUNET_SYSERR == force_address) &&
804                  (session->expecting_welcome == GNUNET_NO) ) ||
805                (GNUNET_NO == force_address) )   
806             {
807               cand_session = select_better_session (cand_session,
808                                                     session);
809               continue;
810             }
811           if (GNUNET_SYSERR == force_address)
812             continue;
813           GNUNET_break (GNUNET_YES == force_address);
814           if (addr == NULL)
815             {
816               GNUNET_break (0);
817               break;
818             }
819           if (session->inbound == GNUNET_YES) 
820             continue;
821           if (addrlen != session->connect_alen)
822             continue;
823           if (0 != memcmp (session->connect_addr,
824                            addr,
825                            addrlen))
826             continue;
827           cand_session = select_better_session (cand_session,
828                                                 session);             
829         }
830       session = cand_session;
831     }
832   if ( (session == NULL) &&
833        (addr == NULL) )
834     {
835 #if DEBUG_TCP
836       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
837                        "tcp",
838                        "Asked to transmit to `%4s' without address and I have no existing connection (failing).\n",
839                        GNUNET_i2s (target));
840 #endif
841       GNUNET_STATISTICS_update (plugin->env->stats,
842                                 gettext_noop ("# bytes discarded by TCP (no address and no connection)"),
843                                 msgbuf_size,
844                                 GNUNET_NO);      
845       return -1;
846     }
847   if (session == NULL)
848     {
849       if (addrlen == sizeof (struct IPv6TcpAddress))
850         {
851           t6 = addr;
852           af = AF_INET6;
853           memset (&a6, 0, sizeof (a6));
854 #if HAVE_SOCKADDR_IN_SIN_LEN
855           a6.sin6_len = sizeof (a6);
856 #endif
857           a6.sin6_family = AF_INET6;
858           a6.sin6_port = t6->t6_port;
859           memcpy (a6.sin6_addr.s6_addr,
860                   &t6->ipv6_addr,
861                   16);      
862           sb = &a6;
863           sbs = sizeof (a6);
864         }
865       else if (addrlen == sizeof (struct IPv4TcpAddress))
866         {
867           t4 = addr;
868           af = AF_INET;
869           memset (&a4, 0, sizeof (a4));
870 #if HAVE_SOCKADDR_IN_SIN_LEN
871           a4.sin_len = sizeof (a4);
872 #endif
873           a4.sin_family = AF_INET;
874           a4.sin_port = t4->t_port;
875           a4.sin_addr.s_addr = t4->ipv4_addr;
876           sb = &a4;
877           sbs = sizeof (a4);
878         }
879       else
880         {
881           GNUNET_break_op (0);
882           return -1;
883         }
884       sa = GNUNET_CONNECTION_create_from_sockaddr (plugin->env->sched,
885                                                    af, sb, sbs,
886                                                    GNUNET_SERVER_MAX_MESSAGE_SIZE);
887       if (sa == NULL)
888         {
889 #if DEBUG_TCP
890           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
891                            "tcp",
892                            "Failed to create connection to `%4s' at `%s'\n",
893                            GNUNET_i2s (target),
894                            GNUNET_a2s (sb, sbs));
895 #endif
896           GNUNET_STATISTICS_update (plugin->env->stats,
897                                     gettext_noop ("# bytes discarded by TCP (failed to connect)"),
898                                     msgbuf_size,
899                                     GNUNET_NO);      
900           return -1;
901         }
902 #if DEBUG_TCP
903       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
904                        "tcp",
905                        "Asked to transmit to `%4s', creating fresh session using address `%s'.\n",
906                        GNUNET_i2s (target),
907                        GNUNET_a2s (sb, sbs));
908 #endif
909       session = create_session (plugin,
910                                 target,
911                                 GNUNET_SERVER_connect_socket (plugin->server,
912                                                               sa));
913       session->connect_addr = GNUNET_malloc (addrlen);
914       memcpy (session->connect_addr,
915               addr,
916               addrlen);
917       session->connect_alen = addrlen;
918     }
919   GNUNET_assert (session != NULL);
920   GNUNET_assert (session->client != NULL);
921   GNUNET_STATISTICS_update (plugin->env->stats,
922                             gettext_noop ("# bytes currently in TCP buffers"),
923                             msgbuf_size,
924                             GNUNET_NO);      
925   /* create new message entry */
926   pm = GNUNET_malloc (sizeof (struct PendingMessage) + msgbuf_size);
927   pm->msg = (const char*) &pm[1];
928   memcpy (&pm[1], msg, msgbuf_size);
929   pm->message_size = msgbuf_size;
930   pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
931   pm->transmit_cont = cont;
932   pm->transmit_cont_cls = cont_cls;
933
934   /* append pm to pending_messages list */
935   GNUNET_CONTAINER_DLL_insert_after (session->pending_messages_head,
936                                      session->pending_messages_tail,
937                                      session->pending_messages_tail,
938                                      pm);
939 #if DEBUG_TCP
940   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
941                    "tcp",
942                    "Asked to transmit %u bytes to `%s', added message to list.\n",
943                    msgbuf_size,
944                    GNUNET_i2s (target));
945 #endif
946   process_pending_messages (session);
947   return msgbuf_size;
948 }
949
950
951 /**
952  * Function that can be called to force a disconnect from the
953  * specified neighbour.  This should also cancel all previously
954  * scheduled transmissions.  Obviously the transmission may have been
955  * partially completed already, which is OK.  The plugin is supposed
956  * to close the connection (if applicable) and no longer call the
957  * transmit continuation(s).
958  *
959  * Finally, plugin MUST NOT call the services's receive function to
960  * notify the service that the connection to the specified target was
961  * closed after a getting this call.
962  *
963  * @param cls closure
964  * @param target peer for which the last transmission is
965  *        to be cancelled
966  */
967 static void
968 tcp_plugin_disconnect (void *cls,
969                        const struct GNUNET_PeerIdentity *target)
970 {
971   struct Plugin *plugin = cls;
972   struct Session *session;
973   struct Session *next;
974   struct PendingMessage *pm;
975
976 #if DEBUG_TCP
977   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
978                    "tcp",
979                    "Asked to cancel session with `%4s'\n",
980                    GNUNET_i2s (target));
981 #endif
982   next = plugin->sessions;
983   while (NULL != (session = next))
984     {
985       next = session->next;
986       if (0 != memcmp (target,
987                        &session->target,
988                        sizeof (struct GNUNET_PeerIdentity)))
989         continue;
990       pm = session->pending_messages_head;
991       while (pm != NULL)
992         {
993           pm->transmit_cont = NULL;
994           pm->transmit_cont_cls = NULL;
995           pm = pm->next;
996         }
997       disconnect_session (session);
998     }
999 }
1000
1001
1002 /**
1003  * Context for address to string conversion.
1004  */
1005 struct PrettyPrinterContext
1006 {
1007   /**
1008    * Function to call with the result.
1009    */
1010   GNUNET_TRANSPORT_AddressStringCallback asc;
1011
1012   /**
1013    * Clsoure for 'asc'.
1014    */
1015   void *asc_cls;
1016
1017   /**
1018    * Port to add after the IP address.
1019    */
1020   uint16_t port;
1021 };
1022
1023
1024 /**
1025  * Append our port and forward the result.
1026  *
1027  * @param cls the 'struct PrettyPrinterContext*'
1028  * @param hostname hostname part of the address
1029  */
1030 static void
1031 append_port (void *cls, const char *hostname)
1032 {
1033   struct PrettyPrinterContext *ppc = cls;
1034   char *ret;
1035
1036   if (hostname == NULL)
1037     {
1038       ppc->asc (ppc->asc_cls, NULL);
1039       GNUNET_free (ppc);
1040       return;
1041     }
1042   GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
1043   ppc->asc (ppc->asc_cls, ret);
1044   GNUNET_free (ret);
1045 }
1046
1047
1048 /**
1049  * Convert the transports address to a nice, human-readable
1050  * format.
1051  *
1052  * @param cls closure
1053  * @param type name of the transport that generated the address
1054  * @param addr one of the addresses of the host, NULL for the last address
1055  *        the specific address format depends on the transport
1056  * @param addrlen length of the address
1057  * @param numeric should (IP) addresses be displayed in numeric form?
1058  * @param timeout after how long should we give up?
1059  * @param asc function to call on each string
1060  * @param asc_cls closure for asc
1061  */
1062 static void
1063 tcp_plugin_address_pretty_printer (void *cls,
1064                                    const char *type,
1065                                    const void *addr,
1066                                    size_t addrlen,
1067                                    int numeric,
1068                                    struct GNUNET_TIME_Relative timeout,
1069                                    GNUNET_TRANSPORT_AddressStringCallback asc,
1070                                    void *asc_cls)
1071 {
1072   struct Plugin *plugin = cls;
1073   struct PrettyPrinterContext *ppc;
1074   const void *sb;
1075   size_t sbs;
1076   struct sockaddr_in a4;
1077   struct sockaddr_in6 a6;
1078   const struct IPv4TcpAddress *t4;
1079   const struct IPv6TcpAddress *t6;
1080   uint16_t port;
1081
1082   if (addrlen == sizeof (struct IPv6TcpAddress))
1083     {
1084       t6 = addr;
1085       memset (&a6, 0, sizeof (a6));
1086       a6.sin6_family = AF_INET6;
1087       a6.sin6_port = t6->t6_port;
1088       memcpy (a6.sin6_addr.s6_addr,
1089               &t6->ipv6_addr,
1090               16);      
1091       port = ntohs (t6->t6_port);
1092       sb = &a6;
1093       sbs = sizeof (a6);
1094     }
1095   else if (addrlen == sizeof (struct IPv4TcpAddress))
1096     {
1097       t4 = addr;
1098       memset (&a4, 0, sizeof (a4));
1099       a4.sin_family = AF_INET;
1100       a4.sin_port = t4->t_port;
1101       a4.sin_addr.s_addr = t4->ipv4_addr;
1102       port = ntohs (t4->t_port);
1103       sb = &a4;
1104       sbs = sizeof (a4);
1105     }
1106   else
1107     {
1108       /* invalid address */
1109       GNUNET_break_op (0);
1110       asc (asc_cls, NULL);
1111       return;
1112     }
1113   ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
1114   ppc->asc = asc;
1115   ppc->asc_cls = asc_cls;
1116   ppc->port = port;
1117   GNUNET_RESOLVER_hostname_get (plugin->env->sched,
1118                                 plugin->env->cfg,
1119                                 sb,
1120                                 sbs,
1121                                 !numeric, timeout, &append_port, ppc);
1122 }
1123
1124
1125 /**
1126  * Check if the given port is plausible (must be either
1127  * our listen port or our advertised port).  If it is
1128  * neither, we return one of these two ports at random.
1129  *
1130  * @param plugin global variables
1131  * @param in_port port number to check
1132  * @return either in_port or a more plausible port
1133  */
1134 static uint16_t
1135 check_port (struct Plugin *plugin, uint16_t in_port)
1136 {
1137   if ((in_port == plugin->adv_port) || (in_port == plugin->open_port))
1138     return in_port;
1139   return (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1140                                     2) == 0)
1141     ? plugin->open_port : plugin->adv_port;
1142 }
1143
1144
1145 /**
1146  * Another peer has suggested an address for this peer and transport
1147  * plugin.  Check that this could be a valid address. This function
1148  * is not expected to 'validate' the address in the sense of trying to
1149  * connect to it but simply to see if the binary format is technically
1150  * legal for establishing a connection.
1151  *
1152  * @param cls closure, our 'struct Plugin*'
1153  * @param addr pointer to the address
1154  * @param addrlen length of addr
1155  * @return GNUNET_OK if this is a plausible address for this peer
1156  *         and transport, GNUNET_SYSERR if not
1157  */
1158 static int
1159 tcp_plugin_check_address (void *cls, void *addr, size_t addrlen)
1160 {
1161   struct Plugin *plugin = cls;
1162   struct IPv4TcpAddress *v4;
1163   struct IPv6TcpAddress *v6;
1164
1165   if ((addrlen != sizeof (struct IPv4TcpAddress)) &&
1166       (addrlen != sizeof (struct IPv6TcpAddress)))
1167     {
1168       GNUNET_break_op (0);
1169       return GNUNET_SYSERR;
1170     }
1171   if (addrlen == sizeof (struct IPv4TcpAddress))
1172     {
1173       v4 = (struct IPv4TcpAddress *) addr;
1174       v4->t_port = htons (check_port (plugin, ntohs (v4->t_port)));
1175     }
1176   else
1177     {
1178       v6 = (struct IPv6TcpAddress *) addr;
1179       if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
1180         {
1181           GNUNET_break_op (0);
1182           return GNUNET_SYSERR;
1183         }
1184       v6->t6_port = htons (check_port (plugin, ntohs (v6->t6_port)));
1185     }
1186   return GNUNET_OK;
1187 }
1188
1189
1190 /**
1191  * We've received a welcome from this peer via TCP.  Possibly create a
1192  * fresh client record and send back our welcome.
1193  *
1194  * @param cls closure
1195  * @param client identification of the client
1196  * @param message the actual message
1197  */
1198 static void
1199 handle_tcp_welcome (void *cls,
1200                     struct GNUNET_SERVER_Client *client,
1201                     const struct GNUNET_MessageHeader *message)
1202 {
1203   struct Plugin *plugin = cls;
1204   const struct WelcomeMessage *wm = (const struct WelcomeMessage *) message;
1205   struct Session *session;
1206   size_t alen;
1207   void *vaddr;
1208   struct IPv4TcpAddress *t4;
1209   struct IPv6TcpAddress *t6;
1210   const struct sockaddr_in *s4;
1211   const struct sockaddr_in6 *s6;
1212
1213
1214 #if DEBUG_TCP
1215   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1216                    "tcp",
1217                    "Received %s message from a `%4s/%p'.\n", 
1218                    "WELCOME",
1219                    GNUNET_i2s (&wm->clientIdentity), client);
1220 #endif
1221   GNUNET_STATISTICS_update (plugin->env->stats,
1222                             gettext_noop ("# TCP WELCOME messages received"),
1223                             1,
1224                             GNUNET_NO);      
1225   session = find_session_by_client (plugin, client);
1226   if (session == NULL)
1227     {
1228       GNUNET_SERVER_client_keep (client);
1229       session = create_session (plugin,
1230                                 &wm->clientIdentity, client);
1231       session->inbound = GNUNET_YES;
1232       if (GNUNET_OK ==
1233           GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
1234         {
1235 #if DEBUG_TCP
1236           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1237                            "tcp",
1238                            "Found address `%s' for incoming connection %p\n",
1239                            GNUNET_a2s (vaddr, alen),
1240                            client);
1241 #endif
1242           if (alen == sizeof (struct sockaddr_in))
1243             {
1244               s4 = vaddr;
1245               t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress));
1246               t4->t_port = s4->sin_port;
1247               t4->ipv4_addr = s4->sin_addr.s_addr;
1248               session->connect_addr = t4;
1249               session->connect_alen = sizeof (struct IPv4TcpAddress);
1250             }
1251           else if (alen == sizeof (struct sockaddr_in6))
1252             {
1253               s6 = vaddr;
1254               t6 = GNUNET_malloc (sizeof (struct IPv6TcpAddress));
1255               t6->t6_port = s6->sin6_port;
1256               memcpy (&t6->ipv6_addr,
1257                       s6->sin6_addr.s6_addr,
1258                       16);
1259               session->connect_addr = t6;
1260               session->connect_alen = sizeof (struct IPv6TcpAddress);
1261             }
1262           GNUNET_free (vaddr);
1263         }
1264       else
1265         {
1266 #if DEBUG_TCP
1267           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1268                            "tcp",
1269                            "Did not obtain TCP socket address for incoming connection\n");
1270 #endif
1271         }
1272 #if DEBUG_TCP
1273       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1274                        "tcp",
1275                        "Creating new session %p for connection %p\n",
1276                        session, client);
1277 #endif
1278       process_pending_messages (session);
1279     }
1280   if (session->expecting_welcome != GNUNET_YES)
1281     {
1282       GNUNET_break_op (0);
1283       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1284       return;
1285     }
1286   session->last_activity = GNUNET_TIME_absolute_get ();
1287   session->expecting_welcome = GNUNET_NO;
1288   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1289 }
1290
1291
1292 /**
1293  * Task to signal the server that we can continue
1294  * receiving from the TCP client now.
1295  *
1296  * @param cls the 'struct Session*'
1297  * @param tc task context (unused)
1298  */
1299 static void
1300 delayed_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1301 {
1302   struct Session *session = cls;
1303   struct GNUNET_TIME_Relative delay;
1304
1305   session->receive_delay_task = GNUNET_SCHEDULER_NO_TASK;
1306   delay = session->plugin->env->receive (session->plugin->env->cls,
1307                                          &session->target,
1308                                          NULL, 0, 
1309                                          session,
1310                                          NULL, 0);
1311   if (delay.value == 0)
1312     GNUNET_SERVER_receive_done (session->client, GNUNET_OK);
1313   else
1314     session->receive_delay_task = 
1315       GNUNET_SCHEDULER_add_delayed (session->plugin->env->sched,
1316                                     delay, &delayed_done, session);
1317 }
1318
1319
1320 /**
1321  * We've received data for this peer via TCP.  Unbox,
1322  * compute latency and forward.
1323  *
1324  * @param cls closure
1325  * @param client identification of the client
1326  * @param message the actual message
1327  */
1328 static void
1329 handle_tcp_data (void *cls,
1330                  struct GNUNET_SERVER_Client *client,
1331                  const struct GNUNET_MessageHeader *message)
1332 {
1333   struct Plugin *plugin = cls;
1334   struct Session *session;
1335   struct GNUNET_TIME_Relative delay;
1336
1337   if (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME == ntohs(message->type))
1338     {
1339       /* We don't want to propagate WELCOME messages up! */
1340       GNUNET_SERVER_receive_done (client, GNUNET_OK);
1341       return; 
1342     }    
1343   session = find_session_by_client (plugin, client);
1344   if ( (NULL == session) || (GNUNET_NO != session->expecting_welcome))
1345     {
1346       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1347       return;
1348     }
1349   session->last_activity = GNUNET_TIME_absolute_get ();
1350 #if DEBUG_TCP
1351   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1352                    "tcp", 
1353                    "Passing %u bytes of type %u from `%4s' to transport service.\n",
1354                    (unsigned int) ntohs (message->size), 
1355                    (unsigned int) ntohs (message->type),
1356                    GNUNET_i2s (&session->target));
1357 #endif
1358   GNUNET_STATISTICS_update (plugin->env->stats,
1359                             gettext_noop ("# bytes received via TCP"),
1360                             ntohs (message->size),
1361                             GNUNET_NO); 
1362   delay = plugin->env->receive (plugin->env->cls, &session->target, message, 1,
1363                                 session, 
1364                                 (GNUNET_YES == session->inbound) ? NULL : session->connect_addr,
1365                                 (GNUNET_YES == session->inbound) ? 0 : session->connect_alen);
1366   if (delay.value == 0)
1367     GNUNET_SERVER_receive_done (client, GNUNET_OK);
1368   else
1369     session->receive_delay_task = 
1370       GNUNET_SCHEDULER_add_delayed (session->plugin->env->sched,
1371                                     delay, &delayed_done, session);
1372 }
1373
1374
1375 /**
1376  * Handlers for the various TCP messages.
1377  */
1378 static struct GNUNET_SERVER_MessageHandler my_handlers[] = {
1379   {&handle_tcp_welcome, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME,
1380    sizeof (struct WelcomeMessage)},
1381   {&handle_tcp_data, NULL, GNUNET_MESSAGE_TYPE_ALL, 0},
1382   {NULL, NULL, 0, 0}
1383 };
1384
1385
1386 /**
1387  * Functions with this signature are called whenever a peer
1388  * is disconnected on the network level.
1389  *
1390  * @param cls closure
1391  * @param client identification of the client
1392  */
1393 static void
1394 disconnect_notify (void *cls, 
1395                    struct GNUNET_SERVER_Client *client)
1396 {
1397   struct Plugin *plugin = cls;
1398   struct Session *session;
1399
1400   if (client == NULL)
1401     return;
1402   session = find_session_by_client (plugin, client);
1403   if (session == NULL)
1404     return;                     /* unknown, nothing to do */
1405 #if DEBUG_TCP
1406   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1407                    "tcp",
1408                    "Destroying session of `%4s' with %s (%p) due to network-level disconnect.\n",
1409                    GNUNET_i2s (&session->target),
1410                    (session->connect_addr != NULL) ?
1411                    tcp_address_to_string (session->plugin,
1412                                           session->connect_addr,
1413                                           session->connect_alen) : "*",
1414                    client);
1415 #endif
1416   disconnect_session (session);
1417 }
1418
1419
1420 /**
1421  * Add the IP of our network interface to the list of
1422  * our external IP addresses.
1423  *
1424  * @param cls the 'struct Plugin*'
1425  * @param name name of the interface
1426  * @param isDefault do we think this may be our default interface
1427  * @param addr address of the interface
1428  * @param addrlen number of bytes in addr
1429  * @return GNUNET_OK to continue iterating
1430  */
1431 static int
1432 process_interfaces (void *cls,
1433                     const char *name,
1434                     int isDefault,
1435                     const struct sockaddr *addr, socklen_t addrlen)
1436 {
1437   struct Plugin *plugin = cls;
1438   int af;
1439   struct IPv4TcpAddress t4;
1440   struct IPv6TcpAddress t6;
1441   void *arg;
1442   uint16_t args;
1443
1444   af = addr->sa_family;
1445   if (af == AF_INET)
1446     {
1447       t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
1448       t4.t_port = htons (plugin->adv_port);
1449       arg = &t4;
1450       args = sizeof (t4);
1451     }
1452   else if (af == AF_INET6)
1453     {
1454       if (IN6_IS_ADDR_LINKLOCAL (((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr))
1455         {
1456           /* skip link local addresses */
1457           return GNUNET_OK;
1458         }
1459       memcpy (&t6.ipv6_addr,
1460               ((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
1461               16);
1462       t6.t6_port = htons (plugin->adv_port);
1463       arg = &t6;
1464       args = sizeof (t6);
1465     }
1466   else
1467     {
1468       GNUNET_break (0);
1469       return GNUNET_OK;
1470     }
1471   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO |
1472                    GNUNET_ERROR_TYPE_BULK,
1473                    "tcp", 
1474                    _("Found address `%s' (%s)\n"),
1475                    GNUNET_a2s (addr, addrlen), name);
1476   plugin->env->notify_address (plugin->env->cls,
1477                                "tcp",
1478                                arg, args, GNUNET_TIME_UNIT_FOREVER_REL);
1479   return GNUNET_OK;
1480 }
1481
1482
1483 /**
1484  * Function called by the resolver for each address obtained from DNS
1485  * for our own hostname.  Add the addresses to the list of our
1486  * external IP addresses.
1487  *
1488  * @param cls closure
1489  * @param addr one of the addresses of the host, NULL for the last address
1490  * @param addrlen length of the address
1491  */
1492 static void
1493 process_hostname_ips (void *cls,
1494                       const struct sockaddr *addr, socklen_t addrlen)
1495 {
1496   struct Plugin *plugin = cls;
1497
1498   if (addr == NULL)
1499     {
1500       plugin->hostname_dns = NULL;
1501       return;
1502     }
1503   process_interfaces (plugin, "<hostname>", GNUNET_YES, addr, addrlen);
1504 }
1505
1506
1507 /**
1508  * Entry point for the plugin.
1509  *
1510  * @param cls closure, the 'struct GNUNET_TRANSPORT_PluginEnvironment*'
1511  * @return the 'struct GNUNET_TRANSPORT_PluginFunctions*' or NULL on error
1512  */
1513 void *
1514 libgnunet_plugin_transport_tcp_init (void *cls)
1515 {
1516   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1517   struct GNUNET_TRANSPORT_PluginFunctions *api;
1518   struct Plugin *plugin;
1519   struct GNUNET_SERVICE_Context *service;
1520   unsigned long long aport;
1521   unsigned long long bport;
1522   unsigned int i;
1523
1524   service = GNUNET_SERVICE_start ("transport-tcp", env->sched, env->cfg);
1525   if (service == NULL)
1526     {
1527       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
1528                        "tcp",
1529                        _
1530                        ("Failed to start service for `%s' transport plugin.\n"),
1531                        "tcp");
1532       return NULL;
1533     }
1534   aport = 0;
1535   if ((GNUNET_OK !=
1536        GNUNET_CONFIGURATION_get_value_number (env->cfg,
1537                                               "transport-tcp",
1538                                               "PORT",
1539                                               &bport)) ||
1540       (bport > 65535) ||
1541       ((GNUNET_OK ==
1542         GNUNET_CONFIGURATION_get_value_number (env->cfg,
1543                                                "transport-tcp",
1544                                                "ADVERTISED-PORT",
1545                                                &aport)) && (aport > 65535)))
1546     {
1547       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1548                        "tcp",
1549                        _
1550                        ("Require valid port number for service `%s' in configuration!\n"),
1551                        "transport-tcp");
1552       GNUNET_SERVICE_stop (service);
1553       return NULL;
1554     }
1555   if (aport == 0)
1556     aport = bport;
1557   plugin = GNUNET_malloc (sizeof (struct Plugin));
1558   plugin->open_port = bport;
1559   plugin->adv_port = aport;
1560   plugin->env = env;
1561   plugin->lsock = NULL;
1562   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1563   api->cls = plugin;
1564   api->send = &tcp_plugin_send;
1565   api->disconnect = &tcp_plugin_disconnect;
1566   api->address_pretty_printer = &tcp_plugin_address_pretty_printer;
1567   api->check_address = &tcp_plugin_check_address;
1568   api->address_to_string = &tcp_address_to_string;
1569   plugin->service = service;
1570   plugin->server = GNUNET_SERVICE_get_server (service);
1571   plugin->handlers = GNUNET_malloc (sizeof (my_handlers));
1572   memcpy (plugin->handlers, my_handlers, sizeof (my_handlers));
1573   for (i = 0;
1574        i <
1575        sizeof (my_handlers) / sizeof (struct GNUNET_SERVER_MessageHandler);
1576        i++)
1577     plugin->handlers[i].callback_cls = plugin;
1578   GNUNET_SERVER_add_handlers (plugin->server, plugin->handlers);
1579
1580   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1581                    "tcp", _("TCP transport listening on port %llu\n"), bport);
1582   if (aport != bport)
1583     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1584                      "tcp",
1585                      _("TCP transport advertises itself as being on port %llu\n"),
1586                      aport);
1587   GNUNET_SERVER_disconnect_notify (plugin->server, 
1588                                    &disconnect_notify,
1589                                    plugin);
1590   /* FIXME: do the two calls below periodically again and
1591      not just once (since the info we get might change...) */
1592   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
1593   plugin->hostname_dns = GNUNET_RESOLVER_hostname_resolve (env->sched,
1594                                                            env->cfg,
1595                                                            AF_UNSPEC,
1596                                                            HOSTNAME_RESOLVE_TIMEOUT,
1597                                                            &process_hostname_ips,
1598                                                            plugin);
1599   return api;
1600 }
1601
1602
1603 /**
1604  * Exit point from the plugin.
1605  */
1606 void *
1607 libgnunet_plugin_transport_tcp_done (void *cls)
1608 {
1609   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1610   struct Plugin *plugin = api->cls;
1611   struct Session *session;
1612
1613   while (NULL != (session = plugin->sessions))
1614     disconnect_session (session);
1615   if (NULL != plugin->hostname_dns)
1616     {
1617       GNUNET_RESOLVER_request_cancel (plugin->hostname_dns);
1618       plugin->hostname_dns = NULL;
1619     }
1620   GNUNET_SERVICE_stop (plugin->service);
1621   GNUNET_free (plugin->handlers);
1622   GNUNET_free (plugin);
1623   GNUNET_free (api);
1624   return NULL;
1625 }
1626
1627 /* end of plugin_transport_tcp.c */