dce
[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 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 + 10];
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                    "%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   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
392                    "tcp",
393                    "Creating new session for peer `%4s'\n",
394                    GNUNET_i2s (target));
395   ret = GNUNET_malloc (sizeof (struct Session));
396   ret->last_activity = GNUNET_TIME_absolute_get ();
397   ret->plugin = plugin;
398   ret->next = plugin->sessions;
399   plugin->sessions = ret;
400   ret->client = client;
401   ret->target = *target;
402   ret->expecting_welcome = GNUNET_YES;
403   pm = GNUNET_malloc (sizeof (struct PendingMessage) + sizeof (struct WelcomeMessage));
404   pm->msg = (const char*) &pm[1];
405   pm->message_size = sizeof (struct WelcomeMessage);
406   welcome.header.size = htons (sizeof (struct WelcomeMessage));
407   welcome.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME);
408   welcome.clientIdentity = *plugin->env->my_identity;
409   memcpy (&pm[1], &welcome, sizeof (welcome));
410   pm->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
411   GNUNET_STATISTICS_update (plugin->env->stats,
412                             gettext_noop ("# bytes currently in TCP buffers"),
413                             pm->message_size,
414                             GNUNET_NO);      
415   GNUNET_CONTAINER_DLL_insert (ret->pending_messages_head,
416                                ret->pending_messages_tail,
417                                pm);
418   GNUNET_STATISTICS_update (plugin->env->stats,
419                             gettext_noop ("# TCP sessions active"),
420                             1,
421                             GNUNET_NO);      
422   return ret;
423 }
424
425
426 /**
427  * If we have pending messages, ask the server to
428  * transmit them (schedule the respective tasks, etc.)
429  *
430  * @param session for which session should we do this
431  */
432 static void process_pending_messages (struct Session *session);
433
434
435 /**
436  * Function called to notify a client about the socket
437  * being ready to queue more data.  "buf" will be
438  * NULL and "size" zero if the socket was closed for
439  * writing in the meantime.
440  *
441  * @param cls closure
442  * @param size number of bytes available in buf
443  * @param buf where the callee should write the message
444  * @return number of bytes written to buf
445  */
446 static size_t
447 do_transmit (void *cls, size_t size, void *buf)
448 {
449   struct Session *session = cls;
450   struct GNUNET_PeerIdentity pid;
451   struct Plugin *plugin;
452   struct PendingMessage *pos;
453   struct PendingMessage *hd;
454   struct PendingMessage *tl;
455   struct GNUNET_TIME_Absolute now;
456   char *cbuf;
457   size_t ret;
458
459   session->transmit_handle = NULL;
460   plugin = session->plugin;
461   if (buf == NULL)
462     {
463 #if DEBUG_TCP
464       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
465                        "tcp",
466                        "Timeout trying to transmit to peer `%4s', discarding message queue.\n",
467                        GNUNET_i2s (&session->target));
468 #endif
469       /* timeout; cancel all messages that have already expired */
470       hd = NULL;
471       tl = NULL;
472       ret = 0;
473       now = GNUNET_TIME_absolute_get ();
474       while ( (NULL != (pos = session->pending_messages_head)) &&
475               (pos->timeout.value <= now.value) )
476         {
477           GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
478                                        session->pending_messages_tail,
479                                        pos);
480 #if DEBUG_TCP
481           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
482                            "tcp",
483                            "Failed to transmit %u byte message to `%4s'.\n",
484                            pos->message_size,
485                            GNUNET_i2s (&session->target));
486 #endif
487           ret += pos->message_size;
488           GNUNET_CONTAINER_DLL_insert_after (hd, tl, tl, pos);
489         }
490       /* do this call before callbacks (so that if callbacks destroy
491          session, they have a chance to cancel actions done by this
492          call) */
493       process_pending_messages (session);  
494       pid = session->target;
495       /* no do callbacks and do not use session again since
496          the callbacks may abort the session */
497       while (NULL != (pos = hd))
498         {
499           GNUNET_CONTAINER_DLL_remove (hd, tl, pos);
500           if (pos->transmit_cont != NULL)
501             pos->transmit_cont (pos->transmit_cont_cls,
502                                 &pid, GNUNET_SYSERR);
503           GNUNET_free (pos);
504         }
505       GNUNET_STATISTICS_update (plugin->env->stats,
506                                 gettext_noop ("# bytes currently in TCP buffers"),
507                                 - (int64_t) ret,
508                                 GNUNET_NO); 
509       GNUNET_STATISTICS_update (plugin->env->stats,
510                                 gettext_noop ("# bytes discarded by TCP (timeout)"),
511                                 ret,
512                                 GNUNET_NO);      
513       return 0;
514     }
515   /* copy all pending messages that would fit */
516   ret = 0;
517   cbuf = buf;
518   hd = NULL;
519   tl = NULL;
520   while (NULL != (pos = session->pending_messages_head)) 
521     {
522       if (ret + pos->message_size > size) 
523         break;
524       GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
525                                    session->pending_messages_tail,
526                                    pos);
527       GNUNET_assert (size >= pos->message_size);
528       memcpy (cbuf, pos->msg, pos->message_size);
529       cbuf += pos->message_size;
530       ret += pos->message_size;
531       size -= pos->message_size;
532       GNUNET_CONTAINER_DLL_insert_after (hd, tl, tl, pos);
533     }
534   /* schedule 'continuation' before callbacks so that callbacks that
535      cancel everything don't cause us to use a session that no longer
536      exists... */
537   process_pending_messages (session);  
538   session->last_activity = GNUNET_TIME_absolute_get ();
539   pid = session->target;
540   /* we'll now call callbacks that may cancel the session; hence
541      we should not use 'session' after this point */
542   while (NULL != (pos = hd))
543     {
544       GNUNET_CONTAINER_DLL_remove (hd, tl, pos);
545       if (pos->transmit_cont != NULL)
546         pos->transmit_cont (pos->transmit_cont_cls,
547                             &pid, GNUNET_OK);
548       GNUNET_free (pos);
549     }
550   GNUNET_assert (hd == NULL);
551   GNUNET_assert (tl == NULL);
552 #if DEBUG_TCP > 1
553   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
554                    "tcp", "Transmitting %u bytes\n", ret);
555 #endif
556   GNUNET_STATISTICS_update (plugin->env->stats,
557                             gettext_noop ("# bytes currently in TCP buffers"),
558                             - (int64_t) ret,
559                             GNUNET_NO);       
560   GNUNET_STATISTICS_update (plugin->env->stats,
561                             gettext_noop ("# bytes transmitted via TCP"),
562                             ret,
563                             GNUNET_NO);      
564   return ret;
565 }
566
567
568 /**
569  * If we have pending messages, ask the server to
570  * transmit them (schedule the respective tasks, etc.)
571  *
572  * @param session for which session should we do this
573  */
574 static void
575 process_pending_messages (struct Session *session)
576 {
577   struct PendingMessage *pm;
578
579   GNUNET_assert (session->client != NULL);
580   if (session->transmit_handle != NULL)
581     return;
582   if (NULL == (pm = session->pending_messages_head))
583     return;
584   session->transmit_handle
585     = GNUNET_SERVER_notify_transmit_ready (session->client,
586                                            pm->message_size,
587                                            GNUNET_TIME_absolute_get_remaining
588                                            (pm->timeout),
589                                            &do_transmit, session);
590 }
591
592
593 /**
594  * Functions with this signature are called whenever we need
595  * to close a session due to a disconnect or failure to
596  * establish a connection.
597  *
598  * @param session session to close down
599  */
600 static void
601 disconnect_session (struct Session *session)
602 {
603   struct Session *prev;
604   struct Session *pos;
605   struct PendingMessage *pm;
606
607 #if DEBUG_TCP
608   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
609                    "tcp",
610                    "Disconnecting from `%4s' at %s (session %p).\n",
611                    GNUNET_i2s (&session->target),
612                    (session->connect_addr != NULL) ?
613                    tcp_address_to_string (session->plugin,
614                                           session->connect_addr,
615                                           session->connect_alen) : "*", 
616                    session);
617 #endif
618   /* remove from session list */
619   prev = NULL;
620   pos = session->plugin->sessions;
621   while (pos != session)
622     {
623       prev = pos;
624       pos = pos->next;
625     }
626   if (prev == NULL)
627     session->plugin->sessions = session->next;
628   else
629     prev->next = session->next;
630   session->plugin->env->session_end (session->plugin->env->cls,
631                             &session->target,
632                             session);
633   /* clean up state */
634   if (session->transmit_handle != NULL)
635     {
636       GNUNET_CONNECTION_notify_transmit_ready_cancel
637         (session->transmit_handle);
638       session->transmit_handle = NULL;
639     }
640   while (NULL != (pm = session->pending_messages_head))
641     {
642 #if DEBUG_TCP
643       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
644                        "tcp",
645                        pm->transmit_cont != NULL
646                        ? "Could not deliver message to `%4s'.\n"
647                        :
648                        "Could not deliver message to `%4s', notifying.\n",
649                        GNUNET_i2s (&session->target));
650 #endif
651       GNUNET_STATISTICS_update (session->plugin->env->stats,
652                                 gettext_noop ("# bytes currently in TCP buffers"),
653                                 - (int64_t) pm->message_size,
654                                 GNUNET_NO);      
655       GNUNET_STATISTICS_update (session->plugin->env->stats,
656                                 gettext_noop ("# bytes discarded by TCP (disconnect)"),
657                                 pm->message_size,
658                                 GNUNET_NO);      
659       GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
660                                    session->pending_messages_tail,
661                                    pm);
662       if (NULL != pm->transmit_cont)
663         pm->transmit_cont (pm->transmit_cont_cls,
664                            &session->target, GNUNET_SYSERR);
665       GNUNET_free (pm);
666     }
667   GNUNET_break (session->client != NULL);
668   if (session->receive_delay_task != GNUNET_SCHEDULER_NO_TASK)
669     {
670       GNUNET_SCHEDULER_cancel (session->plugin->env->sched,
671                                session->receive_delay_task);
672       GNUNET_SERVER_receive_done (session->client, 
673                                   GNUNET_SYSERR);       
674     }
675   GNUNET_SERVER_client_drop (session->client);
676   GNUNET_STATISTICS_update (session->plugin->env->stats,
677                             gettext_noop ("# TCP sessions active"),
678                             -1,
679                             GNUNET_NO);      
680   GNUNET_free_non_null (session->connect_addr);
681   GNUNET_free (session);
682 }
683
684
685 /**
686  * Given two otherwise equivalent sessions, pick the better one.
687  * 
688  * @param s1 one session (also default)
689  * @param s2 other session
690  * @return "better" session (more active)
691  */
692 static struct Session *
693 select_better_session (struct Session *s1,
694                        struct Session *s2)
695 {
696   if (s1 == NULL)
697     return s2;
698   if (s2 == NULL)
699     return s1;
700   if ( (s1->expecting_welcome == GNUNET_NO) &&
701        (s2->expecting_welcome == GNUNET_YES) )
702     return s1;
703   if ( (s1->expecting_welcome == GNUNET_YES) &&
704        (s2->expecting_welcome == GNUNET_NO) )
705     return s2;
706   if (s1->last_activity.value < s2->last_activity.value)
707     return s2;
708   if (s1->last_activity.value > s2->last_activity.value)
709     return s1;
710   if ( (GNUNET_YES == s1->inbound) &&
711        (GNUNET_NO  == s2->inbound) )
712     return s1;
713   if ( (GNUNET_NO  == s1->inbound) &&
714        (GNUNET_YES == s2->inbound) )
715     return s2;
716   return s1;
717 }
718
719
720 /**
721  * Function that can be used by the transport service to transmit
722  * a message using the plugin.   Note that in the case of a
723  * peer disconnecting, the continuation MUST be called
724  * prior to the disconnect notification itself.  This function
725  * will be called with this peer's HELLO message to initiate
726  * a fresh connection to another peer.
727  *
728  * @param cls closure
729  * @param target who should receive this message
730  * @param msg the message to transmit
731  * @param msgbuf_size number of bytes in 'msg'
732  * @param priority how important is the message (most plugins will
733  *                 ignore message priority and just FIFO)
734  * @param timeout how long to wait at most for the transmission (does not
735  *                require plugins to discard the message after the timeout,
736  *                just advisory for the desired delay; most plugins will ignore
737  *                this as well)
738  * @param session which session must be used (or NULL for "any")
739  * @param addr the address to use (can be NULL if the plugin
740  *                is "on its own" (i.e. re-use existing TCP connection))
741  * @param addrlen length of the address in bytes
742  * @param force_address GNUNET_YES if the plugin MUST use the given address,
743  *                GNUNET_NO means the plugin may use any other address and
744  *                GNUNET_SYSERR means that only reliable existing
745  *                bi-directional connections should be used (regardless
746  *                of address)
747  * @param cont continuation to call once the message has
748  *        been transmitted (or if the transport is ready
749  *        for the next transmission call; or if the
750  *        peer disconnected...); can be NULL
751  * @param cont_cls closure for cont
752  * @return number of bytes used (on the physical network, with overheads);
753  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
754  *         and does NOT mean that the message was not transmitted (DV)
755  */
756 static ssize_t
757 tcp_plugin_send (void *cls,
758                  const struct GNUNET_PeerIdentity *target,
759                  const char *msg,
760                  size_t msgbuf_size,
761                  uint32_t priority,
762                  struct GNUNET_TIME_Relative timeout,
763                  struct Session *session,
764                  const void *addr,
765                  size_t addrlen,
766                  int force_address,
767                  GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
768 {
769   struct Plugin *plugin = cls;
770   struct Session *cand_session;
771   struct Session *next;
772   struct PendingMessage *pm;
773   struct GNUNET_CONNECTION_Handle *sa;
774   int af;
775   const void *sb;
776   size_t sbs;
777   struct sockaddr_in a4;
778   struct sockaddr_in6 a6;
779   const struct IPv4TcpAddress *t4;
780   const struct IPv6TcpAddress *t6;
781
782   GNUNET_STATISTICS_update (plugin->env->stats,
783                             gettext_noop ("# bytes TCP was asked to transmit"),
784                             msgbuf_size,
785                             GNUNET_NO);      
786   /* FIXME: we could do this cheaper with a hash table
787      where we could restrict the iteration to entries that match
788      the target peer... */
789   if (session == NULL)
790     {
791       cand_session = NULL;
792       next = plugin->sessions;
793       while (NULL != (session = next)) 
794         {
795           next = session->next;
796           GNUNET_assert (session->client != NULL);
797           if (0 != memcmp (target,
798                            &session->target, 
799                            sizeof (struct GNUNET_PeerIdentity)))
800             continue;
801           if ( ( (GNUNET_SYSERR == force_address) &&
802                  (session->expecting_welcome == GNUNET_NO) ) ||
803                (GNUNET_NO == force_address) )   
804             {
805               cand_session = select_better_session (cand_session,
806                                                     session);
807               continue;
808             }
809           if (GNUNET_SYSERR == force_address)
810             continue;
811           GNUNET_break (GNUNET_YES == force_address);
812           if (addr == NULL)
813             {
814               GNUNET_break (0);
815               break;
816             }
817           if (session->inbound == GNUNET_YES) 
818             continue;
819           if (addrlen != session->connect_alen)
820             continue;
821           if (0 != memcmp (session->connect_addr,
822                            addr,
823                            addrlen))
824             continue;
825           cand_session = select_better_session (cand_session,
826                                                 session);             
827         }
828       session = cand_session;
829     }
830   if ( (session == NULL) &&
831        (addr == NULL) )
832     {
833 #if DEBUG_TCP
834       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
835                        "tcp",
836                        "Asked to transmit to `%4s' without address and I have no existing connection (failing).\n",
837                        GNUNET_i2s (target));
838 #endif
839       GNUNET_STATISTICS_update (plugin->env->stats,
840                                 gettext_noop ("# bytes discarded by TCP (no address and no connection)"),
841                                 msgbuf_size,
842                                 GNUNET_NO);      
843       return -1;
844     }
845   if (session == NULL)
846     {
847       if (addrlen == sizeof (struct IPv6TcpAddress))
848         {
849           t6 = addr;
850           af = AF_INET6;
851           memset (&a6, 0, sizeof (a6));
852 #if HAVE_SOCKADDR_IN_SIN_LEN
853           a6.sin6_len = sizeof (a6);
854 #endif
855           a6.sin6_family = AF_INET6;
856           a6.sin6_port = t6->t6_port;
857           memcpy (a6.sin6_addr.s6_addr,
858                   t6->ipv6_addr,
859                   16);      
860           sb = &a6;
861           sbs = sizeof (a6);
862         }
863       else if (addrlen == sizeof (struct IPv4TcpAddress))
864         {
865           t4 = addr;
866           af = AF_INET;
867           memset (&a4, 0, sizeof (a4));
868 #if HAVE_SOCKADDR_IN_SIN_LEN
869           a4.sin_len = sizeof (a4);
870 #endif
871           a4.sin_family = AF_INET;
872           a4.sin_port = t4->t_port;
873           a4.sin_addr.s_addr = t4->ipv4_addr;
874           sb = &a4;
875           sbs = sizeof (a4);
876         }
877       else
878         {
879           GNUNET_break_op (0);
880           return -1;
881         }
882       sa = GNUNET_CONNECTION_create_from_sockaddr (plugin->env->sched,
883                                                    af, sb, sbs,
884                                                    GNUNET_SERVER_MAX_MESSAGE_SIZE);
885       if (sa == NULL)
886         {
887 #if DEBUG_TCP
888           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
889                            "tcp",
890                            "Failed to create connection to `%4s' at `%s'\n",
891                            GNUNET_i2s (target),
892                            GNUNET_a2s (sb, sbs));
893 #endif
894           GNUNET_STATISTICS_update (plugin->env->stats,
895                                     gettext_noop ("# bytes discarded by TCP (failed to connect)"),
896                                     msgbuf_size,
897                                     GNUNET_NO);      
898           return -1;
899         }
900 #if DEBUG_TCP
901       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
902                        "tcp",
903                        "Asked to transmit to `%4s', creating fresh session using address `%s'.\n",
904                        GNUNET_i2s (target),
905                        GNUNET_a2s (sb, sbs));
906 #endif
907       session = create_session (plugin,
908                                 target,
909                                 GNUNET_SERVER_connect_socket (plugin->server,
910                                                               sa));
911       session->connect_addr = GNUNET_malloc (addrlen);
912       memcpy (session->connect_addr,
913               addr,
914               addrlen);
915       session->connect_alen = addrlen;
916     }
917   GNUNET_assert (session != NULL);
918   GNUNET_assert (session->client != NULL);
919   GNUNET_STATISTICS_update (plugin->env->stats,
920                             gettext_noop ("# bytes currently in TCP buffers"),
921                             msgbuf_size,
922                             GNUNET_NO);      
923   /* create new message entry */
924   pm = GNUNET_malloc (sizeof (struct PendingMessage) + msgbuf_size);
925   pm->msg = (const char*) &pm[1];
926   memcpy (&pm[1], msg, msgbuf_size);
927   pm->message_size = msgbuf_size;
928   pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
929   pm->transmit_cont = cont;
930   pm->transmit_cont_cls = cont_cls;
931
932   /* append pm to pending_messages list */
933   GNUNET_CONTAINER_DLL_insert_after (session->pending_messages_head,
934                                      session->pending_messages_tail,
935                                      session->pending_messages_tail,
936                                      pm);
937 #if DEBUG_TCP
938   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
939                    "tcp",
940                    "Asked to transmit %u bytes to `%s', added message to list.\n",
941                    msgbuf_size,
942                    GNUNET_i2s (target));
943 #endif
944   process_pending_messages (session);
945   return msgbuf_size;
946 }
947
948
949 /**
950  * Function that can be called to force a disconnect from the
951  * specified neighbour.  This should also cancel all previously
952  * scheduled transmissions.  Obviously the transmission may have been
953  * partially completed already, which is OK.  The plugin is supposed
954  * to close the connection (if applicable) and no longer call the
955  * transmit continuation(s).
956  *
957  * Finally, plugin MUST NOT call the services's receive function to
958  * notify the service that the connection to the specified target was
959  * closed after a getting this call.
960  *
961  * @param cls closure
962  * @param target peer for which the last transmission is
963  *        to be cancelled
964  */
965 static void
966 tcp_plugin_disconnect (void *cls,
967                        const struct GNUNET_PeerIdentity *target)
968 {
969   struct Plugin *plugin = cls;
970   struct Session *session;
971   struct Session *next;
972   struct PendingMessage *pm;
973
974 #if DEBUG_TCP
975   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
976                    "tcp",
977                    "Asked to cancel session with `%4s'\n",
978                    GNUNET_i2s (target));
979 #endif
980   next = plugin->sessions;
981   while (NULL != (session = next))
982     {
983       next = session->next;
984       if (0 != memcmp (target,
985                        &session->target,
986                        sizeof (struct GNUNET_PeerIdentity)))
987         continue;
988       pm = session->pending_messages_head;
989       while (pm != NULL)
990         {
991           pm->transmit_cont = NULL;
992           pm->transmit_cont_cls = NULL;
993           pm = pm->next;
994         }
995       disconnect_session (session);
996     }
997 }
998
999
1000 /**
1001  * Context for address to string conversion.
1002  */
1003 struct PrettyPrinterContext
1004 {
1005   /**
1006    * Function to call with the result.
1007    */
1008   GNUNET_TRANSPORT_AddressStringCallback asc;
1009
1010   /**
1011    * Clsoure for 'asc'.
1012    */
1013   void *asc_cls;
1014
1015   /**
1016    * Port to add after the IP address.
1017    */
1018   uint16_t port;
1019 };
1020
1021
1022 /**
1023  * Append our port and forward the result.
1024  *
1025  * @param cls the 'struct PrettyPrinterContext*'
1026  * @param hostname hostname part of the address
1027  */
1028 static void
1029 append_port (void *cls, const char *hostname)
1030 {
1031   struct PrettyPrinterContext *ppc = cls;
1032   char *ret;
1033
1034   if (hostname == NULL)
1035     {
1036       ppc->asc (ppc->asc_cls, NULL);
1037       GNUNET_free (ppc);
1038       return;
1039     }
1040   GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
1041   ppc->asc (ppc->asc_cls, ret);
1042   GNUNET_free (ret);
1043 }
1044
1045
1046 /**
1047  * Convert the transports address to a nice, human-readable
1048  * format.
1049  *
1050  * @param cls closure
1051  * @param type name of the transport that generated the address
1052  * @param addr one of the addresses of the host, NULL for the last address
1053  *        the specific address format depends on the transport
1054  * @param addrlen length of the address
1055  * @param numeric should (IP) addresses be displayed in numeric form?
1056  * @param timeout after how long should we give up?
1057  * @param asc function to call on each string
1058  * @param asc_cls closure for asc
1059  */
1060 static void
1061 tcp_plugin_address_pretty_printer (void *cls,
1062                                    const char *type,
1063                                    const void *addr,
1064                                    size_t addrlen,
1065                                    int numeric,
1066                                    struct GNUNET_TIME_Relative timeout,
1067                                    GNUNET_TRANSPORT_AddressStringCallback asc,
1068                                    void *asc_cls)
1069 {
1070   struct Plugin *plugin = cls;
1071   struct PrettyPrinterContext *ppc;
1072   const void *sb;
1073   size_t sbs;
1074   struct sockaddr_in a4;
1075   struct sockaddr_in6 a6;
1076   const struct IPv4TcpAddress *t4;
1077   const struct IPv6TcpAddress *t6;
1078   uint16_t port;
1079
1080   if (addrlen == sizeof (struct IPv6TcpAddress))
1081     {
1082       t6 = addr;
1083       memset (&a6, 0, sizeof (a6));
1084       a6.sin6_family = AF_INET6;
1085       a6.sin6_port = t6->t6_port;
1086       memcpy (a6.sin6_addr.s6_addr,
1087               t6->ipv6_addr,
1088               16);      
1089       port = ntohs (t6->t6_port);
1090       sb = &a6;
1091       sbs = sizeof (a6);
1092     }
1093   else if (addrlen == sizeof (struct IPv4TcpAddress))
1094     {
1095       t4 = addr;
1096       memset (&a4, 0, sizeof (a4));
1097       a4.sin_family = AF_INET;
1098       a4.sin_port = t4->t_port;
1099       a4.sin_addr.s_addr = t4->ipv4_addr;
1100       port = ntohs (t4->t_port);
1101       sb = &a4;
1102       sbs = sizeof (a4);
1103     }
1104   else
1105     {
1106       /* invalid address */
1107       GNUNET_break_op (0);
1108       asc (asc_cls, NULL);
1109       return;
1110     }
1111   ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
1112   ppc->asc = asc;
1113   ppc->asc_cls = asc_cls;
1114   ppc->port = port;
1115   GNUNET_RESOLVER_hostname_get (plugin->env->sched,
1116                                 plugin->env->cfg,
1117                                 sb,
1118                                 sbs,
1119                                 !numeric, timeout, &append_port, ppc);
1120 }
1121
1122
1123 /**
1124  * Check if the given port is plausible (must be either
1125  * our listen port or our advertised port).  If it is
1126  * neither, we return one of these two ports at random.
1127  *
1128  * @param plugin global variables
1129  * @param in_port port number to check
1130  * @return either in_port or a more plausible port
1131  */
1132 static uint16_t
1133 check_port (struct Plugin *plugin, uint16_t in_port)
1134 {
1135   if ((in_port == plugin->adv_port) || (in_port == plugin->open_port))
1136     return in_port;
1137   return (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1138                                     2) == 0)
1139     ? plugin->open_port : plugin->adv_port;
1140 }
1141
1142
1143 /**
1144  * Another peer has suggested an address for this peer and transport
1145  * plugin.  Check that this could be a valid address. This function
1146  * is not expected to 'validate' the address in the sense of trying to
1147  * connect to it but simply to see if the binary format is technically
1148  * legal for establishing a connection.
1149  *
1150  * @param cls closure, our 'struct Plugin*'
1151  * @param addr pointer to the address
1152  * @param addrlen length of addr
1153  * @return GNUNET_OK if this is a plausible address for this peer
1154  *         and transport, GNUNET_SYSERR if not
1155  */
1156 static int
1157 tcp_plugin_check_address (void *cls, void *addr, size_t addrlen)
1158 {
1159   struct Plugin *plugin = cls;
1160   struct IPv4TcpAddress *v4;
1161   struct IPv6TcpAddress *v6;
1162
1163   if ((addrlen != sizeof (struct IPv4TcpAddress)) &&
1164       (addrlen != sizeof (struct IPv6TcpAddress)))
1165     {
1166       GNUNET_break_op (0);
1167       return GNUNET_SYSERR;
1168     }
1169   if (addrlen == sizeof (struct IPv4TcpAddress))
1170     {
1171       v4 = (struct IPv4TcpAddress *) addr;
1172       v4->t_port = htons (check_port (plugin, ntohs (v4->t_port)));
1173     }
1174   else
1175     {
1176       v6 = (struct IPv6TcpAddress *) addr;
1177       v6->t6_port = htons (check_port (plugin, ntohs (v6->t6_port)));
1178     }
1179   return GNUNET_OK;
1180 }
1181
1182
1183 /**
1184  * We've received a welcome from this peer via TCP.  Possibly create a
1185  * fresh client record and send back our welcome.
1186  *
1187  * @param cls closure
1188  * @param client identification of the client
1189  * @param message the actual message
1190  */
1191 static void
1192 handle_tcp_welcome (void *cls,
1193                     struct GNUNET_SERVER_Client *client,
1194                     const struct GNUNET_MessageHeader *message)
1195 {
1196   struct Plugin *plugin = cls;
1197   const struct WelcomeMessage *wm = (const struct WelcomeMessage *) message;
1198   struct Session *session;
1199   size_t alen;
1200   void *vaddr;
1201   struct IPv4TcpAddress *t4;
1202   struct IPv6TcpAddress *t6;
1203   const struct sockaddr_in *s4;
1204   const struct sockaddr_in6 *s6;
1205
1206
1207 #if DEBUG_TCP
1208   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1209                    "tcp",
1210                    "Received %s message from a `%4s/%p'.\n", 
1211                    "WELCOME",
1212                    GNUNET_i2s (&wm->clientIdentity), client);
1213 #endif
1214   GNUNET_STATISTICS_update (plugin->env->stats,
1215                             gettext_noop ("# TCP WELCOME messages received"),
1216                             1,
1217                             GNUNET_NO);      
1218   session = find_session_by_client (plugin, client);
1219   if (session == NULL)
1220     {
1221       GNUNET_SERVER_client_keep (client);
1222       session = create_session (plugin,
1223                                 &wm->clientIdentity, client);
1224       session->inbound = GNUNET_YES;
1225       if (GNUNET_OK ==
1226           GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
1227         {
1228 #if DEBUG_TCP
1229           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1230                            "tcp",
1231                            "Found address `%s' for incoming connection %p\n",
1232                            GNUNET_a2s (vaddr, alen),
1233                            client);
1234 #endif
1235           if (alen == sizeof (struct sockaddr_in))
1236             {
1237               s4 = vaddr;
1238               t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress));
1239               t4->t_port = s4->sin_port;
1240               t4->ipv4_addr = s4->sin_addr.s_addr;
1241               session->connect_addr = t4;
1242               session->connect_alen = sizeof (struct IPv4TcpAddress);
1243             }
1244           else if (alen == sizeof (struct sockaddr_in6))
1245             {
1246               s6 = vaddr;
1247               t6 = GNUNET_malloc (sizeof (struct IPv6TcpAddress));
1248               t6->t6_port = s6->sin6_port;
1249               memcpy (t6->ipv6_addr,
1250                       s6->sin6_addr.s6_addr,
1251                       16);
1252               session->connect_addr = t6;
1253               session->connect_alen = sizeof (struct IPv6TcpAddress);
1254             }
1255           GNUNET_free (vaddr);
1256         }
1257       else
1258         {
1259 #if DEBUG_TCP
1260           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1261                            "tcp",
1262                            "Did not obtain TCP socket address for incoming connection\n");
1263 #endif
1264         }
1265 #if DEBUG_TCP
1266       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1267                        "tcp",
1268                        "Creating new session %p for connection %p\n",
1269                        session, client);
1270 #endif
1271       process_pending_messages (session);
1272     }
1273   if (session->expecting_welcome != GNUNET_YES)
1274     {
1275       GNUNET_break_op (0);
1276       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1277       return;
1278     }
1279   session->last_activity = GNUNET_TIME_absolute_get ();
1280   session->expecting_welcome = GNUNET_NO;
1281   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1282 }
1283
1284
1285 /**
1286  * Task to signal the server that we can continue
1287  * receiving from the TCP client now.
1288  *
1289  * @param cls the 'struct Session*'
1290  * @param tc task context (unused)
1291  */
1292 static void
1293 delayed_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1294 {
1295   struct Session *session = cls;
1296   struct GNUNET_TIME_Relative delay;
1297
1298   session->receive_delay_task = GNUNET_SCHEDULER_NO_TASK;
1299   delay = session->plugin->env->receive (session->plugin->env->cls,
1300                                          &session->target,
1301                                          NULL, 0, 
1302                                          session,
1303                                          NULL, 0);
1304   if (delay.value == 0)
1305     GNUNET_SERVER_receive_done (session->client, GNUNET_OK);
1306   else
1307     session->receive_delay_task = 
1308       GNUNET_SCHEDULER_add_delayed (session->plugin->env->sched,
1309                                     delay, &delayed_done, session);
1310 }
1311
1312
1313 /**
1314  * We've received data for this peer via TCP.  Unbox,
1315  * compute latency and forward.
1316  *
1317  * @param cls closure
1318  * @param client identification of the client
1319  * @param message the actual message
1320  */
1321 static void
1322 handle_tcp_data (void *cls,
1323                  struct GNUNET_SERVER_Client *client,
1324                  const struct GNUNET_MessageHeader *message)
1325 {
1326   struct Plugin *plugin = cls;
1327   struct Session *session;
1328   struct GNUNET_TIME_Relative delay;
1329
1330   if (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME == ntohs(message->type))
1331     {
1332       /* We don't want to propagate WELCOME messages up! */
1333       GNUNET_SERVER_receive_done (client, GNUNET_OK);
1334       return; 
1335     }    
1336   session = find_session_by_client (plugin, client);
1337   if ( (NULL == session) || (GNUNET_NO != session->expecting_welcome))
1338     {
1339       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1340       return;
1341     }
1342   session->last_activity = GNUNET_TIME_absolute_get ();
1343 #if DEBUG_TCP
1344   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1345                    "tcp", 
1346                    "Passing %u bytes of type %u from `%4s' to transport service.\n",
1347                    (unsigned int) ntohs (message->size), 
1348                    (unsigned int) ntohs (message->type),
1349                    GNUNET_i2s (&session->target));
1350 #endif
1351   GNUNET_STATISTICS_update (plugin->env->stats,
1352                             gettext_noop ("# bytes received via TCP"),
1353                             ntohs (message->size),
1354                             GNUNET_NO); 
1355   delay = plugin->env->receive (plugin->env->cls, &session->target, message, 1,
1356                                 session, 
1357                                 (GNUNET_YES == session->inbound) ? NULL : session->connect_addr,
1358                                 (GNUNET_YES == session->inbound) ? 0 : session->connect_alen);
1359   if (delay.value == 0)
1360     GNUNET_SERVER_receive_done (client, GNUNET_OK);
1361   else
1362     session->receive_delay_task = 
1363       GNUNET_SCHEDULER_add_delayed (session->plugin->env->sched,
1364                                     delay, &delayed_done, session);
1365 }
1366
1367
1368 /**
1369  * Handlers for the various TCP messages.
1370  */
1371 static struct GNUNET_SERVER_MessageHandler my_handlers[] = {
1372   {&handle_tcp_welcome, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME,
1373    sizeof (struct WelcomeMessage)},
1374   {&handle_tcp_data, NULL, GNUNET_MESSAGE_TYPE_ALL, 0},
1375   {NULL, NULL, 0, 0}
1376 };
1377
1378
1379 /**
1380  * Functions with this signature are called whenever a peer
1381  * is disconnected on the network level.
1382  *
1383  * @param cls closure
1384  * @param client identification of the client
1385  */
1386 static void
1387 disconnect_notify (void *cls, 
1388                    struct GNUNET_SERVER_Client *client)
1389 {
1390   struct Plugin *plugin = cls;
1391   struct Session *session;
1392
1393   if (client == NULL)
1394     return;
1395   session = find_session_by_client (plugin, client);
1396   if (session == NULL)
1397     return;                     /* unknown, nothing to do */
1398 #if DEBUG_TCP
1399   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1400                    "tcp",
1401                    "Destroying session of `%4s' with %s (%p) due to network-level disconnect.\n",
1402                    GNUNET_i2s (&session->target),
1403                    (session->connect_addr != NULL) ?
1404                    tcp_address_to_string (session->plugin,
1405                                           session->connect_addr,
1406                                           session->connect_alen) : "*",
1407                    client);
1408 #endif
1409   disconnect_session (session);
1410 }
1411
1412
1413 /**
1414  * Add the IP of our network interface to the list of
1415  * our external IP addresses.
1416  *
1417  * @param cls the 'struct Plugin*'
1418  * @param name name of the interface
1419  * @param isDefault do we think this may be our default interface
1420  * @param addr address of the interface
1421  * @param addrlen number of bytes in addr
1422  * @return GNUNET_OK to continue iterating
1423  */
1424 static int
1425 process_interfaces (void *cls,
1426                     const char *name,
1427                     int isDefault,
1428                     const struct sockaddr *addr, socklen_t addrlen)
1429 {
1430   struct Plugin *plugin = cls;
1431   int af;
1432   struct IPv4TcpAddress t4;
1433   struct IPv6TcpAddress t6;
1434   void *arg;
1435   uint16_t args;
1436
1437   af = addr->sa_family;
1438   if (af == AF_INET)
1439     {
1440       t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
1441       t4.t_port = htons (plugin->adv_port);
1442       arg = &t4;
1443       args = sizeof (t4);
1444     }
1445   else if (af == AF_INET6)
1446     {
1447       memcpy (t6.ipv6_addr,
1448               ((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
1449               16);
1450       t6.t6_port = htons (plugin->adv_port);
1451       arg = &t6;
1452       args = sizeof (t6);
1453     }
1454   else
1455     {
1456       GNUNET_break (0);
1457       return GNUNET_OK;
1458     }
1459   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO |
1460                    GNUNET_ERROR_TYPE_BULK,
1461                    "tcp", 
1462                    _("Found address `%s' (%s)\n"),
1463                    GNUNET_a2s (addr, addrlen), name);
1464   plugin->env->notify_address (plugin->env->cls,
1465                                "tcp",
1466                                arg, args, GNUNET_TIME_UNIT_FOREVER_REL);
1467   return GNUNET_OK;
1468 }
1469
1470
1471 /**
1472  * Function called by the resolver for each address obtained from DNS
1473  * for our own hostname.  Add the addresses to the list of our
1474  * external IP addresses.
1475  *
1476  * @param cls closure
1477  * @param addr one of the addresses of the host, NULL for the last address
1478  * @param addrlen length of the address
1479  */
1480 static void
1481 process_hostname_ips (void *cls,
1482                       const struct sockaddr *addr, socklen_t addrlen)
1483 {
1484   struct Plugin *plugin = cls;
1485
1486   if (addr == NULL)
1487     {
1488       plugin->hostname_dns = NULL;
1489       return;
1490     }
1491   process_interfaces (plugin, "<hostname>", GNUNET_YES, addr, addrlen);
1492 }
1493
1494
1495 /**
1496  * Entry point for the plugin.
1497  *
1498  * @param cls closure, the 'struct GNUNET_TRANSPORT_PluginEnvironment*'
1499  * @return the 'struct GNUNET_TRANSPORT_PluginFunctions*' or NULL on error
1500  */
1501 void *
1502 libgnunet_plugin_transport_tcp_init (void *cls)
1503 {
1504   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1505   struct GNUNET_TRANSPORT_PluginFunctions *api;
1506   struct Plugin *plugin;
1507   struct GNUNET_SERVICE_Context *service;
1508   unsigned long long aport;
1509   unsigned long long bport;
1510   unsigned int i;
1511
1512   service = GNUNET_SERVICE_start ("transport-tcp", env->sched, env->cfg);
1513   if (service == NULL)
1514     {
1515       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
1516                        "tcp",
1517                        _
1518                        ("Failed to start service for `%s' transport plugin.\n"),
1519                        "tcp");
1520       return NULL;
1521     }
1522   aport = 0;
1523   if ((GNUNET_OK !=
1524        GNUNET_CONFIGURATION_get_value_number (env->cfg,
1525                                               "transport-tcp",
1526                                               "PORT",
1527                                               &bport)) ||
1528       (bport > 65535) ||
1529       ((GNUNET_OK ==
1530         GNUNET_CONFIGURATION_get_value_number (env->cfg,
1531                                                "transport-tcp",
1532                                                "ADVERTISED-PORT",
1533                                                &aport)) && (aport > 65535)))
1534     {
1535       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1536                        "tcp",
1537                        _
1538                        ("Require valid port number for service `%s' in configuration!\n"),
1539                        "transport-tcp");
1540       GNUNET_SERVICE_stop (service);
1541       return NULL;
1542     }
1543   if (aport == 0)
1544     aport = bport;
1545   plugin = GNUNET_malloc (sizeof (struct Plugin));
1546   plugin->open_port = bport;
1547   plugin->adv_port = aport;
1548   plugin->env = env;
1549   plugin->lsock = NULL;
1550   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1551   api->cls = plugin;
1552   api->send = &tcp_plugin_send;
1553   api->disconnect = &tcp_plugin_disconnect;
1554   api->address_pretty_printer = &tcp_plugin_address_pretty_printer;
1555   api->check_address = &tcp_plugin_check_address;
1556   api->address_to_string = &tcp_address_to_string;
1557   plugin->service = service;
1558   plugin->server = GNUNET_SERVICE_get_server (service);
1559   plugin->handlers = GNUNET_malloc (sizeof (my_handlers));
1560   memcpy (plugin->handlers, my_handlers, sizeof (my_handlers));
1561   for (i = 0;
1562        i <
1563        sizeof (my_handlers) / sizeof (struct GNUNET_SERVER_MessageHandler);
1564        i++)
1565     plugin->handlers[i].callback_cls = plugin;
1566   GNUNET_SERVER_add_handlers (plugin->server, plugin->handlers);
1567
1568   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1569                    "tcp", _("TCP transport listening on port %llu\n"), bport);
1570   if (aport != bport)
1571     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1572                      "tcp",
1573                      _("TCP transport advertises itself as being on port %llu\n"),
1574                      aport);
1575   GNUNET_SERVER_disconnect_notify (plugin->server, 
1576                                    &disconnect_notify,
1577                                    plugin);
1578   /* FIXME: do the two calls below periodically again and
1579      not just once (since the info we get might change...) */
1580   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
1581   plugin->hostname_dns = GNUNET_RESOLVER_hostname_resolve (env->sched,
1582                                                            env->cfg,
1583                                                            AF_UNSPEC,
1584                                                            HOSTNAME_RESOLVE_TIMEOUT,
1585                                                            &process_hostname_ips,
1586                                                            plugin);
1587   return api;
1588 }
1589
1590
1591 /**
1592  * Exit point from the plugin.
1593  */
1594 void *
1595 libgnunet_plugin_transport_tcp_done (void *cls)
1596 {
1597   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1598   struct Plugin *plugin = api->cls;
1599   struct Session *session;
1600
1601   while (NULL != (session = plugin->sessions))
1602     disconnect_session (session);
1603   if (NULL != plugin->hostname_dns)
1604     {
1605       GNUNET_RESOLVER_request_cancel (plugin->hostname_dns);
1606       plugin->hostname_dns = NULL;
1607     }
1608   GNUNET_SERVICE_stop (plugin->service);
1609   GNUNET_free (plugin->handlers);
1610   GNUNET_free (plugin);
1611   GNUNET_free (api);
1612   return NULL;
1613 }
1614
1615 /* end of plugin_transport_tcp.c */