inet_ntop
[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_YES
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   unsigned char ipv6_addr[16];
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 for the statistics service.
258    */
259   struct GNUNET_STATISTICS_Handle *statistics;
260
261   /**
262    * Handle to the network service.
263    */
264   struct GNUNET_SERVICE_Context *service;
265
266   /**
267    * Handle to the server for this service.
268    */
269   struct GNUNET_SERVER_Handle *server;
270
271   /**
272    * Copy of the handler array where the closures are
273    * set to this struct's instance.
274    */
275   struct GNUNET_SERVER_MessageHandler *handlers;
276
277   /**
278    * Handle for request of hostname resolution, non-NULL if pending.
279    */
280   struct GNUNET_RESOLVER_RequestHandle *hostname_dns;
281
282   /**
283    * ID of task used to update our addresses when one expires.
284    */
285   GNUNET_SCHEDULER_TaskIdentifier address_update_task;
286
287   /**
288    * Port that we are actually listening on.
289    */
290   uint16_t open_port;
291
292   /**
293    * Port that the user said we would have visible to the
294    * rest of the world.
295    */
296   uint16_t adv_port;
297
298 };
299
300
301
302
303 /**
304  * Function called for a quick conversion of the binary address to
305  * a numeric address.  Note that the caller must not free the 
306  * address and that the next call to this function is allowed
307  * to override the address again.
308  *
309  * @param cls closure ('struct Plugin*')
310  * @param addr binary address
311  * @param addrlen length of the address
312  * @return string representing the same address 
313  */
314 static const char* 
315 tcp_address_to_string (void *cls,
316                        const void *addr,
317                        size_t addrlen)
318 {
319   static char rbuf[INET6_ADDRSTRLEN + 10];
320   char buf[INET6_ADDRSTRLEN];
321   const void *sb;
322   struct in_addr a4;
323   struct in6_addr a6;
324   const struct IPv4TcpAddress *t4;
325   const struct IPv6TcpAddress *t6;
326   int af;
327   uint16_t port;
328
329   if (addrlen == sizeof (struct IPv6TcpAddress))
330     {
331       t6 = addr;
332       af = AF_INET6;
333       port = ntohs (t6->t6_port);
334       memcpy (&a6, t6->ipv6_addr, sizeof (a6));
335       sb = &a6;
336     }
337   else if (addrlen == sizeof (struct IPv4TcpAddress))
338     {
339       t4 = addr;
340       af = AF_INET;
341       port = ntohs (t4->t_port);
342       memcpy (&a4, &t4->ipv4_addr, sizeof (a4));
343       sb = &a4;
344     }
345   else
346     return NULL;
347   inet_ntop (af, sb, buf, INET6_ADDRSTRLEN);
348   GNUNET_snprintf (rbuf,
349                    sizeof (rbuf),
350                    "%s:%u",
351                    buf,
352                    port);
353   return rbuf;
354 }
355
356
357 /**
358  * Find the session handle for the given client.
359  *
360  * @return NULL if no matching session exists
361  */
362 static struct Session *
363 find_session_by_client (struct Plugin *plugin,
364                         const struct GNUNET_SERVER_Client *client)
365 {
366   struct Session *ret;
367
368   ret = plugin->sessions;
369   while ((ret != NULL) && (client != ret->client))
370     ret = ret->next;
371   return ret;
372 }
373
374
375 /**
376  * Create a new session.  Also queues a welcome message.
377  *
378  * @param plugin us
379  * @param target peer to connect to
380  * @param client client to use
381  * @return new session object
382  */
383 static struct Session *
384 create_session (struct Plugin *plugin,
385                 const struct GNUNET_PeerIdentity *target,
386                 struct GNUNET_SERVER_Client *client)
387 {
388   struct Session *ret;
389   struct PendingMessage *pm;
390   struct WelcomeMessage welcome;
391
392   GNUNET_assert (client != NULL);
393   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
394                    "tcp",
395                    "Creating new session for peer `%4s'\n",
396                    GNUNET_i2s (target));
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   int af;
1081   uint16_t port;
1082
1083   if (addrlen == sizeof (struct IPv6TcpAddress))
1084     {
1085       t6 = addr;
1086       af = AF_INET6;
1087       memset (&a6, 0, sizeof (a6));
1088       a6.sin6_family = AF_INET6;
1089       a6.sin6_port = t6->t6_port;
1090       memcpy (a6.sin6_addr.s6_addr,
1091               t6->ipv6_addr,
1092               16);      
1093       port = ntohs (t6->t6_port);
1094       sb = &a6;
1095       sbs = sizeof (a6);
1096     }
1097   else if (addrlen == sizeof (struct IPv4TcpAddress))
1098     {
1099       t4 = addr;
1100       af = AF_INET;
1101       memset (&a4, 0, sizeof (a4));
1102       a4.sin_family = AF_INET;
1103       a4.sin_port = t4->t_port;
1104       a4.sin_addr.s_addr = t4->ipv4_addr;
1105       port = ntohs (t4->t_port);
1106       sb = &a4;
1107       sbs = sizeof (a4);
1108     }
1109   else
1110     {
1111       /* invalid address */
1112       GNUNET_break_op (0);
1113       asc (asc_cls, NULL);
1114       return;
1115     }
1116   ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
1117   ppc->asc = asc;
1118   ppc->asc_cls = asc_cls;
1119   ppc->port = port;
1120   GNUNET_RESOLVER_hostname_get (plugin->env->sched,
1121                                 plugin->env->cfg,
1122                                 sb,
1123                                 sbs,
1124                                 !numeric, timeout, &append_port, ppc);
1125 }
1126
1127
1128 /**
1129  * Check if the given port is plausible (must be either
1130  * our listen port or our advertised port).  If it is
1131  * neither, we return one of these two ports at random.
1132  *
1133  * @param plugin global variables
1134  * @param in_port port number to check
1135  * @return either in_port or a more plausible port
1136  */
1137 static uint16_t
1138 check_port (struct Plugin *plugin, uint16_t in_port)
1139 {
1140   if ((in_port == plugin->adv_port) || (in_port == plugin->open_port))
1141     return in_port;
1142   return (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1143                                     2) == 0)
1144     ? plugin->open_port : plugin->adv_port;
1145 }
1146
1147
1148 /**
1149  * Another peer has suggested an address for this peer and transport
1150  * plugin.  Check that this could be a valid address. This function
1151  * is not expected to 'validate' the address in the sense of trying to
1152  * connect to it but simply to see if the binary format is technically
1153  * legal for establishing a connection.
1154  *
1155  * @param cls closure, our 'struct Plugin*'
1156  * @param addr pointer to the address
1157  * @param addrlen length of addr
1158  * @return GNUNET_OK if this is a plausible address for this peer
1159  *         and transport, GNUNET_SYSERR if not
1160  */
1161 static int
1162 tcp_plugin_check_address (void *cls, void *addr, size_t addrlen)
1163 {
1164   struct Plugin *plugin = cls;
1165   struct IPv4TcpAddress *v4;
1166   struct IPv6TcpAddress *v6;
1167
1168   if ((addrlen != sizeof (struct IPv4TcpAddress)) &&
1169       (addrlen != sizeof (struct IPv6TcpAddress)))
1170     {
1171       GNUNET_break_op (0);
1172       return GNUNET_SYSERR;
1173     }
1174   if (addrlen == sizeof (struct IPv4TcpAddress))
1175     {
1176       v4 = (struct IPv4TcpAddress *) addr;
1177       v4->t_port = htons (check_port (plugin, ntohs (v4->t_port)));
1178     }
1179   else
1180     {
1181       v6 = (struct IPv6TcpAddress *) addr;
1182       v6->t6_port = htons (check_port (plugin, ntohs (v6->t6_port)));
1183     }
1184   return GNUNET_OK;
1185 }
1186
1187
1188 /**
1189  * We've received a welcome from this peer via TCP.  Possibly create a
1190  * fresh client record and send back our welcome.
1191  *
1192  * @param cls closure
1193  * @param client identification of the client
1194  * @param message the actual message
1195  */
1196 static void
1197 handle_tcp_welcome (void *cls,
1198                     struct GNUNET_SERVER_Client *client,
1199                     const struct GNUNET_MessageHeader *message)
1200 {
1201   struct Plugin *plugin = cls;
1202   const struct WelcomeMessage *wm = (const struct WelcomeMessage *) message;
1203   struct Session *session;
1204   size_t alen;
1205   void *vaddr;
1206   struct IPv4TcpAddress *t4;
1207   struct IPv6TcpAddress *t6;
1208   const struct sockaddr_in *s4;
1209   const struct sockaddr_in6 *s6;
1210
1211
1212 #if DEBUG_TCP
1213   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1214                    "tcp",
1215                    "Received %s message from a `%4s/%p'.\n", 
1216                    "WELCOME",
1217                    GNUNET_i2s (&wm->clientIdentity), client);
1218 #endif
1219   GNUNET_STATISTICS_update (plugin->env->stats,
1220                             gettext_noop ("# TCP WELCOME messages received"),
1221                             1,
1222                             GNUNET_NO);      
1223   session = find_session_by_client (plugin, client);
1224   if (session == NULL)
1225     {
1226       GNUNET_SERVER_client_keep (client);
1227       session = create_session (plugin,
1228                                 &wm->clientIdentity, client);
1229       session->inbound = GNUNET_YES;
1230       if (GNUNET_OK ==
1231           GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
1232         {
1233 #if DEBUG_TCP
1234           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1235                            "tcp",
1236                            "Found address `%s' for incoming connection %p\n",
1237                            GNUNET_a2s (vaddr, alen),
1238                            client);
1239 #endif
1240           if (alen == sizeof (struct sockaddr_in))
1241             {
1242               s4 = vaddr;
1243               t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress));
1244               t4->t_port = s4->sin_port;
1245               t4->ipv4_addr = s4->sin_addr.s_addr;
1246               session->connect_addr = t4;
1247               session->connect_alen = sizeof (struct IPv4TcpAddress);
1248             }
1249           else if (alen == sizeof (struct sockaddr_in6))
1250             {
1251               s6 = vaddr;
1252               t6 = GNUNET_malloc (sizeof (struct IPv6TcpAddress));
1253               t6->t6_port = s6->sin6_port;
1254               memcpy (t6->ipv6_addr,
1255                       s6->sin6_addr.s6_addr,
1256                       16);
1257               session->connect_addr = t6;
1258               session->connect_alen = sizeof (struct IPv6TcpAddress);
1259             }
1260           GNUNET_free (vaddr);
1261         }
1262       else
1263         {
1264 #if DEBUG_TCP
1265           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1266                            "tcp",
1267                            "Did not obtain TCP socket address for incoming connection\n");
1268 #endif
1269         }
1270 #if DEBUG_TCP
1271       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1272                        "tcp",
1273                        "Creating new session %p for connection %p\n",
1274                        session, client);
1275 #endif
1276       process_pending_messages (session);
1277     }
1278   if (session->expecting_welcome != GNUNET_YES)
1279     {
1280       GNUNET_break_op (0);
1281       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1282       return;
1283     }
1284   session->last_activity = GNUNET_TIME_absolute_get ();
1285   session->expecting_welcome = GNUNET_NO;
1286   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1287 }
1288
1289
1290 /**
1291  * Task to signal the server that we can continue
1292  * receiving from the TCP client now.
1293  *
1294  * @param cls the 'struct Session*'
1295  * @param tc task context (unused)
1296  */
1297 static void
1298 delayed_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1299 {
1300   struct Session *session = cls;
1301   struct GNUNET_TIME_Relative delay;
1302
1303   session->receive_delay_task = GNUNET_SCHEDULER_NO_TASK;
1304   delay = session->plugin->env->receive (session->plugin->env->cls,
1305                                          &session->target,
1306                                          NULL, 0, 
1307                                          session,
1308                                          NULL, 0);
1309   if (delay.value == 0)
1310     GNUNET_SERVER_receive_done (session->client, GNUNET_OK);
1311   else
1312     session->receive_delay_task = 
1313       GNUNET_SCHEDULER_add_delayed (session->plugin->env->sched,
1314                                     delay, &delayed_done, session);
1315 }
1316
1317
1318 /**
1319  * We've received data for this peer via TCP.  Unbox,
1320  * compute latency and forward.
1321  *
1322  * @param cls closure
1323  * @param client identification of the client
1324  * @param message the actual message
1325  */
1326 static void
1327 handle_tcp_data (void *cls,
1328                  struct GNUNET_SERVER_Client *client,
1329                  const struct GNUNET_MessageHeader *message)
1330 {
1331   struct Plugin *plugin = cls;
1332   struct Session *session;
1333   struct GNUNET_TIME_Relative delay;
1334
1335   if (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME == ntohs(message->type))
1336     {
1337       /* We don't want to propagate WELCOME messages up! */
1338       GNUNET_SERVER_receive_done (client, GNUNET_OK);
1339       return; 
1340     }    
1341   session = find_session_by_client (plugin, client);
1342   if ( (NULL == session) || (GNUNET_NO != session->expecting_welcome))
1343     {
1344       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1345       return;
1346     }
1347   session->last_activity = GNUNET_TIME_absolute_get ();
1348 #if DEBUG_TCP
1349   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1350                    "tcp", 
1351                    "Passing %u bytes of type %u from `%4s' to transport service.\n",
1352                    (unsigned int) ntohs (message->size), 
1353                    (unsigned int) ntohs (message->type),
1354                    GNUNET_i2s (&session->target));
1355 #endif
1356   GNUNET_STATISTICS_update (plugin->env->stats,
1357                             gettext_noop ("# bytes received via TCP"),
1358                             ntohs (message->size),
1359                             GNUNET_NO); 
1360   delay = plugin->env->receive (plugin->env->cls, &session->target, message, 1,
1361                                 session, 
1362                                 (GNUNET_YES == session->inbound) ? NULL : session->connect_addr,
1363                                 (GNUNET_YES == session->inbound) ? 0 : session->connect_alen);
1364   if (delay.value == 0)
1365     GNUNET_SERVER_receive_done (client, GNUNET_OK);
1366   else
1367     session->receive_delay_task = 
1368       GNUNET_SCHEDULER_add_delayed (session->plugin->env->sched,
1369                                     delay, &delayed_done, session);
1370 }
1371
1372
1373 /**
1374  * Handlers for the various TCP messages.
1375  */
1376 static struct GNUNET_SERVER_MessageHandler my_handlers[] = {
1377   {&handle_tcp_welcome, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME,
1378    sizeof (struct WelcomeMessage)},
1379   {&handle_tcp_data, NULL, GNUNET_MESSAGE_TYPE_ALL, 0},
1380   {NULL, NULL, 0, 0}
1381 };
1382
1383
1384 /**
1385  * Functions with this signature are called whenever a peer
1386  * is disconnected on the network level.
1387  *
1388  * @param cls closure
1389  * @param client identification of the client
1390  */
1391 static void
1392 disconnect_notify (void *cls, 
1393                    struct GNUNET_SERVER_Client *client)
1394 {
1395   struct Plugin *plugin = cls;
1396   struct Session *session;
1397
1398   if (client == NULL)
1399     return;
1400   session = find_session_by_client (plugin, client);
1401   if (session == NULL)
1402     return;                     /* unknown, nothing to do */
1403 #if DEBUG_TCP
1404   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1405                    "tcp",
1406                    "Destroying session of `%4s' with %s (%p) due to network-level disconnect.\n",
1407                    GNUNET_i2s (&session->target),
1408                    (session->connect_addr != NULL) ?
1409                    tcp_address_to_string (session->plugin,
1410                                           session->connect_addr,
1411                                           session->connect_alen) : "*",
1412                    client);
1413 #endif
1414   disconnect_session (session);
1415 }
1416
1417
1418 /**
1419  * Add the IP of our network interface to the list of
1420  * our external IP addresses.
1421  *
1422  * @param cls the 'struct Plugin*'
1423  * @param name name of the interface
1424  * @param isDefault do we think this may be our default interface
1425  * @param addr address of the interface
1426  * @param addrlen number of bytes in addr
1427  * @return GNUNET_OK to continue iterating
1428  */
1429 static int
1430 process_interfaces (void *cls,
1431                     const char *name,
1432                     int isDefault,
1433                     const struct sockaddr *addr, socklen_t addrlen)
1434 {
1435   struct Plugin *plugin = cls;
1436   int af;
1437   struct IPv4TcpAddress t4;
1438   struct IPv6TcpAddress t6;
1439   void *arg;
1440   uint16_t args;
1441
1442   af = addr->sa_family;
1443   if (af == AF_INET)
1444     {
1445       t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
1446       t4.t_port = htons (plugin->adv_port);
1447       arg = &t4;
1448       args = sizeof (t4);
1449     }
1450   else if (af == AF_INET6)
1451     {
1452       memcpy (t6.ipv6_addr,
1453               ((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
1454               16);
1455       t6.t6_port = htons (plugin->adv_port);
1456       arg = &t6;
1457       args = sizeof (t6);
1458     }
1459   else
1460     {
1461       GNUNET_break (0);
1462       return GNUNET_OK;
1463     }
1464   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO |
1465                    GNUNET_ERROR_TYPE_BULK,
1466                    "tcp", 
1467                    _("Found address `%s' (%s)\n"),
1468                    GNUNET_a2s (addr, addrlen), name);
1469   plugin->env->notify_address (plugin->env->cls,
1470                                "tcp",
1471                                arg, args, GNUNET_TIME_UNIT_FOREVER_REL);
1472   return GNUNET_OK;
1473 }
1474
1475
1476 /**
1477  * Function called by the resolver for each address obtained from DNS
1478  * for our own hostname.  Add the addresses to the list of our
1479  * external IP addresses.
1480  *
1481  * @param cls closure
1482  * @param addr one of the addresses of the host, NULL for the last address
1483  * @param addrlen length of the address
1484  */
1485 static void
1486 process_hostname_ips (void *cls,
1487                       const struct sockaddr *addr, socklen_t addrlen)
1488 {
1489   struct Plugin *plugin = cls;
1490
1491   if (addr == NULL)
1492     {
1493       plugin->hostname_dns = NULL;
1494       return;
1495     }
1496   process_interfaces (plugin, "<hostname>", GNUNET_YES, addr, addrlen);
1497 }
1498
1499
1500 /**
1501  * Entry point for the plugin.
1502  *
1503  * @param cls closure, the 'struct GNUNET_TRANSPORT_PluginEnvironment*'
1504  * @return the 'struct GNUNET_TRANSPORT_PluginFunctions*' or NULL on error
1505  */
1506 void *
1507 libgnunet_plugin_transport_tcp_init (void *cls)
1508 {
1509   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1510   struct GNUNET_TRANSPORT_PluginFunctions *api;
1511   struct Plugin *plugin;
1512   struct GNUNET_SERVICE_Context *service;
1513   unsigned long long aport;
1514   unsigned long long bport;
1515   unsigned int i;
1516
1517   service = GNUNET_SERVICE_start ("transport-tcp", env->sched, env->cfg);
1518   if (service == NULL)
1519     {
1520       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
1521                        "tcp",
1522                        _
1523                        ("Failed to start service for `%s' transport plugin.\n"),
1524                        "tcp");
1525       return NULL;
1526     }
1527   aport = 0;
1528   if ((GNUNET_OK !=
1529        GNUNET_CONFIGURATION_get_value_number (env->cfg,
1530                                               "transport-tcp",
1531                                               "PORT",
1532                                               &bport)) ||
1533       (bport > 65535) ||
1534       ((GNUNET_OK ==
1535         GNUNET_CONFIGURATION_get_value_number (env->cfg,
1536                                                "transport-tcp",
1537                                                "ADVERTISED-PORT",
1538                                                &aport)) && (aport > 65535)))
1539     {
1540       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1541                        "tcp",
1542                        _
1543                        ("Require valid port number for service `%s' in configuration!\n"),
1544                        "transport-tcp");
1545       GNUNET_SERVICE_stop (service);
1546       return NULL;
1547     }
1548   if (aport == 0)
1549     aport = bport;
1550   plugin = GNUNET_malloc (sizeof (struct Plugin));
1551   plugin->open_port = bport;
1552   plugin->adv_port = aport;
1553   plugin->env = env;
1554   plugin->lsock = NULL;
1555   plugin->statistics = NULL;
1556   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1557   api->cls = plugin;
1558   api->send = &tcp_plugin_send;
1559   api->disconnect = &tcp_plugin_disconnect;
1560   api->address_pretty_printer = &tcp_plugin_address_pretty_printer;
1561   api->check_address = &tcp_plugin_check_address;
1562   api->address_to_string = &tcp_address_to_string;
1563   plugin->service = service;
1564   plugin->server = GNUNET_SERVICE_get_server (service);
1565   plugin->handlers = GNUNET_malloc (sizeof (my_handlers));
1566   memcpy (plugin->handlers, my_handlers, sizeof (my_handlers));
1567   for (i = 0;
1568        i <
1569        sizeof (my_handlers) / sizeof (struct GNUNET_SERVER_MessageHandler);
1570        i++)
1571     plugin->handlers[i].callback_cls = plugin;
1572   GNUNET_SERVER_add_handlers (plugin->server, plugin->handlers);
1573
1574   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1575                    "tcp", _("TCP transport listening on port %llu\n"), bport);
1576   if (aport != bport)
1577     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1578                      "tcp",
1579                      _("TCP transport advertises itself as being on port %llu\n"),
1580                      aport);
1581   GNUNET_SERVER_disconnect_notify (plugin->server, 
1582                                    &disconnect_notify,
1583                                    plugin);
1584   /* FIXME: do the two calls below periodically again and
1585      not just once (since the info we get might change...) */
1586   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
1587   plugin->hostname_dns = GNUNET_RESOLVER_hostname_resolve (env->sched,
1588                                                            env->cfg,
1589                                                            AF_UNSPEC,
1590                                                            HOSTNAME_RESOLVE_TIMEOUT,
1591                                                            &process_hostname_ips,
1592                                                            plugin);
1593   return api;
1594 }
1595
1596
1597 /**
1598  * Exit point from the plugin.
1599  */
1600 void *
1601 libgnunet_plugin_transport_tcp_done (void *cls)
1602 {
1603   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1604   struct Plugin *plugin = api->cls;
1605   struct Session *session;
1606
1607   while (NULL != (session = plugin->sessions))
1608     disconnect_session (session);
1609   if (NULL != plugin->hostname_dns)
1610     {
1611       GNUNET_RESOLVER_request_cancel (plugin->hostname_dns);
1612       plugin->hostname_dns = NULL;
1613     }
1614   GNUNET_SERVICE_stop (plugin->service);
1615   GNUNET_free (plugin->handlers);
1616   GNUNET_free (plugin);
1617   GNUNET_free (api);
1618   return NULL;
1619 }
1620
1621 /* end of plugin_transport_tcp.c */