fixing mess with search update serialization and parenting
[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     return NULL;
342   inet_ntop (af, sb, buf, INET6_ADDRSTRLEN);
343   GNUNET_snprintf (rbuf,
344                    sizeof (rbuf),
345                    "%s:%u",
346                    buf,
347                    port);
348   return rbuf;
349 }
350
351
352 /**
353  * Find the session handle for the given client.
354  *
355  * @return NULL if no matching session exists
356  */
357 static struct Session *
358 find_session_by_client (struct Plugin *plugin,
359                         const struct GNUNET_SERVER_Client *client)
360 {
361   struct Session *ret;
362
363   ret = plugin->sessions;
364   while ((ret != NULL) && (client != ret->client))
365     ret = ret->next;
366   return ret;
367 }
368
369
370 /**
371  * Create a new session.  Also queues a welcome message.
372  *
373  * @param plugin us
374  * @param target peer to connect to
375  * @param client client to use
376  * @return new session object
377  */
378 static struct Session *
379 create_session (struct Plugin *plugin,
380                 const struct GNUNET_PeerIdentity *target,
381                 struct GNUNET_SERVER_Client *client)
382 {
383   struct Session *ret;
384   struct PendingMessage *pm;
385   struct WelcomeMessage welcome;
386
387   GNUNET_assert (client != NULL);
388   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
389                    "tcp",
390                    "Creating new session for peer `%4s'\n",
391                    GNUNET_i2s (target));
392   ret = GNUNET_malloc (sizeof (struct Session));
393   ret->last_activity = GNUNET_TIME_absolute_get ();
394   ret->plugin = plugin;
395   ret->next = plugin->sessions;
396   plugin->sessions = ret;
397   ret->client = client;
398   ret->target = *target;
399   ret->expecting_welcome = GNUNET_YES;
400   pm = GNUNET_malloc (sizeof (struct PendingMessage) + sizeof (struct WelcomeMessage));
401   pm->msg = (const char*) &pm[1];
402   pm->message_size = sizeof (struct WelcomeMessage);
403   welcome.header.size = htons (sizeof (struct WelcomeMessage));
404   welcome.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME);
405   welcome.clientIdentity = *plugin->env->my_identity;
406   memcpy (&pm[1], &welcome, sizeof (welcome));
407   pm->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
408   GNUNET_STATISTICS_update (plugin->env->stats,
409                             gettext_noop ("# bytes currently in TCP buffers"),
410                             pm->message_size,
411                             GNUNET_NO);      
412   GNUNET_CONTAINER_DLL_insert (ret->pending_messages_head,
413                                ret->pending_messages_tail,
414                                pm);
415   GNUNET_STATISTICS_update (plugin->env->stats,
416                             gettext_noop ("# TCP sessions active"),
417                             1,
418                             GNUNET_NO);      
419   return ret;
420 }
421
422
423 /**
424  * If we have pending messages, ask the server to
425  * transmit them (schedule the respective tasks, etc.)
426  *
427  * @param session for which session should we do this
428  */
429 static void process_pending_messages (struct Session *session);
430
431
432 /**
433  * Function called to notify a client about the socket
434  * being ready to queue more data.  "buf" will be
435  * NULL and "size" zero if the socket was closed for
436  * writing in the meantime.
437  *
438  * @param cls closure
439  * @param size number of bytes available in buf
440  * @param buf where the callee should write the message
441  * @return number of bytes written to buf
442  */
443 static size_t
444 do_transmit (void *cls, size_t size, void *buf)
445 {
446   struct Session *session = cls;
447   struct GNUNET_PeerIdentity pid;
448   struct Plugin *plugin;
449   struct PendingMessage *pos;
450   struct PendingMessage *hd;
451   struct PendingMessage *tl;
452   struct GNUNET_TIME_Absolute now;
453   char *cbuf;
454   size_t ret;
455
456   session->transmit_handle = NULL;
457   plugin = session->plugin;
458   if (buf == NULL)
459     {
460 #if DEBUG_TCP
461       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
462                        "tcp",
463                        "Timeout trying to transmit to peer `%4s', discarding message queue.\n",
464                        GNUNET_i2s (&session->target));
465 #endif
466       /* timeout; cancel all messages that have already expired */
467       hd = NULL;
468       tl = NULL;
469       ret = 0;
470       now = GNUNET_TIME_absolute_get ();
471       while ( (NULL != (pos = session->pending_messages_head)) &&
472               (pos->timeout.value <= now.value) )
473         {
474           GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
475                                        session->pending_messages_tail,
476                                        pos);
477 #if DEBUG_TCP
478           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
479                            "tcp",
480                            "Failed to transmit %u byte message to `%4s'.\n",
481                            pos->message_size,
482                            GNUNET_i2s (&session->target));
483 #endif
484           ret += pos->message_size;
485           GNUNET_CONTAINER_DLL_insert_after (hd, tl, tl, pos);
486         }
487       /* do this call before callbacks (so that if callbacks destroy
488          session, they have a chance to cancel actions done by this
489          call) */
490       process_pending_messages (session);  
491       pid = session->target;
492       /* no do callbacks and do not use session again since
493          the callbacks may abort the session */
494       while (NULL != (pos = hd))
495         {
496           GNUNET_CONTAINER_DLL_remove (hd, tl, pos);
497           if (pos->transmit_cont != NULL)
498             pos->transmit_cont (pos->transmit_cont_cls,
499                                 &pid, GNUNET_SYSERR);
500           GNUNET_free (pos);
501         }
502       GNUNET_STATISTICS_update (plugin->env->stats,
503                                 gettext_noop ("# bytes currently in TCP buffers"),
504                                 - (int64_t) ret,
505                                 GNUNET_NO); 
506       GNUNET_STATISTICS_update (plugin->env->stats,
507                                 gettext_noop ("# bytes discarded by TCP (timeout)"),
508                                 ret,
509                                 GNUNET_NO);      
510       return 0;
511     }
512   /* copy all pending messages that would fit */
513   ret = 0;
514   cbuf = buf;
515   hd = NULL;
516   tl = NULL;
517   while (NULL != (pos = session->pending_messages_head)) 
518     {
519       if (ret + pos->message_size > size) 
520         break;
521       GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
522                                    session->pending_messages_tail,
523                                    pos);
524       GNUNET_assert (size >= pos->message_size);
525       memcpy (cbuf, pos->msg, pos->message_size);
526       cbuf += pos->message_size;
527       ret += pos->message_size;
528       size -= pos->message_size;
529       GNUNET_CONTAINER_DLL_insert_after (hd, tl, tl, pos);
530     }
531   /* schedule 'continuation' before callbacks so that callbacks that
532      cancel everything don't cause us to use a session that no longer
533      exists... */
534   process_pending_messages (session);  
535   session->last_activity = GNUNET_TIME_absolute_get ();
536   pid = session->target;
537   /* we'll now call callbacks that may cancel the session; hence
538      we should not use 'session' after this point */
539   while (NULL != (pos = hd))
540     {
541       GNUNET_CONTAINER_DLL_remove (hd, tl, pos);
542       if (pos->transmit_cont != NULL)
543         pos->transmit_cont (pos->transmit_cont_cls,
544                             &pid, GNUNET_OK);
545       GNUNET_free (pos);
546     }
547   GNUNET_assert (hd == NULL);
548   GNUNET_assert (tl == NULL);
549 #if DEBUG_TCP > 1
550   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
551                    "tcp", "Transmitting %u bytes\n", ret);
552 #endif
553   GNUNET_STATISTICS_update (plugin->env->stats,
554                             gettext_noop ("# bytes currently in TCP buffers"),
555                             - (int64_t) ret,
556                             GNUNET_NO);       
557   GNUNET_STATISTICS_update (plugin->env->stats,
558                             gettext_noop ("# bytes transmitted via TCP"),
559                             ret,
560                             GNUNET_NO);      
561   return ret;
562 }
563
564
565 /**
566  * If we have pending messages, ask the server to
567  * transmit them (schedule the respective tasks, etc.)
568  *
569  * @param session for which session should we do this
570  */
571 static void
572 process_pending_messages (struct Session *session)
573 {
574   struct PendingMessage *pm;
575
576   GNUNET_assert (session->client != NULL);
577   if (session->transmit_handle != NULL)
578     return;
579   if (NULL == (pm = session->pending_messages_head))
580     return;
581   session->transmit_handle
582     = GNUNET_SERVER_notify_transmit_ready (session->client,
583                                            pm->message_size,
584                                            GNUNET_TIME_absolute_get_remaining
585                                            (pm->timeout),
586                                            &do_transmit, session);
587 }
588
589
590 /**
591  * Functions with this signature are called whenever we need
592  * to close a session due to a disconnect or failure to
593  * establish a connection.
594  *
595  * @param session session to close down
596  */
597 static void
598 disconnect_session (struct Session *session)
599 {
600   struct Session *prev;
601   struct Session *pos;
602   struct PendingMessage *pm;
603
604 #if DEBUG_TCP
605   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
606                    "tcp",
607                    "Disconnecting from `%4s' at %s (session %p).\n",
608                    GNUNET_i2s (&session->target),
609                    (session->connect_addr != NULL) ?
610                    tcp_address_to_string (session->plugin,
611                                           session->connect_addr,
612                                           session->connect_alen) : "*", 
613                    session);
614 #endif
615   /* remove from session list */
616   prev = NULL;
617   pos = session->plugin->sessions;
618   while (pos != session)
619     {
620       prev = pos;
621       pos = pos->next;
622     }
623   if (prev == NULL)
624     session->plugin->sessions = session->next;
625   else
626     prev->next = session->next;
627   session->plugin->env->session_end (session->plugin->env->cls,
628                             &session->target,
629                             session);
630   /* clean up state */
631   if (session->transmit_handle != NULL)
632     {
633       GNUNET_CONNECTION_notify_transmit_ready_cancel
634         (session->transmit_handle);
635       session->transmit_handle = NULL;
636     }
637   while (NULL != (pm = session->pending_messages_head))
638     {
639 #if DEBUG_TCP
640       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
641                        "tcp",
642                        pm->transmit_cont != NULL
643                        ? "Could not deliver message to `%4s'.\n"
644                        :
645                        "Could not deliver message to `%4s', notifying.\n",
646                        GNUNET_i2s (&session->target));
647 #endif
648       GNUNET_STATISTICS_update (session->plugin->env->stats,
649                                 gettext_noop ("# bytes currently in TCP buffers"),
650                                 - (int64_t) pm->message_size,
651                                 GNUNET_NO);      
652       GNUNET_STATISTICS_update (session->plugin->env->stats,
653                                 gettext_noop ("# bytes discarded by TCP (disconnect)"),
654                                 pm->message_size,
655                                 GNUNET_NO);      
656       GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
657                                    session->pending_messages_tail,
658                                    pm);
659       if (NULL != pm->transmit_cont)
660         pm->transmit_cont (pm->transmit_cont_cls,
661                            &session->target, GNUNET_SYSERR);
662       GNUNET_free (pm);
663     }
664   GNUNET_break (session->client != NULL);
665   if (session->receive_delay_task != GNUNET_SCHEDULER_NO_TASK)
666     {
667       GNUNET_SCHEDULER_cancel (session->plugin->env->sched,
668                                session->receive_delay_task);
669       GNUNET_SERVER_receive_done (session->client, 
670                                   GNUNET_SYSERR);       
671     }
672   GNUNET_SERVER_client_drop (session->client);
673   GNUNET_STATISTICS_update (session->plugin->env->stats,
674                             gettext_noop ("# TCP sessions active"),
675                             -1,
676                             GNUNET_NO);      
677   GNUNET_free_non_null (session->connect_addr);
678   GNUNET_free (session);
679 }
680
681
682 /**
683  * Given two otherwise equivalent sessions, pick the better one.
684  * 
685  * @param s1 one session (also default)
686  * @param s2 other session
687  * @return "better" session (more active)
688  */
689 static struct Session *
690 select_better_session (struct Session *s1,
691                        struct Session *s2)
692 {
693   if (s1 == NULL)
694     return s2;
695   if (s2 == NULL)
696     return s1;
697   if ( (s1->expecting_welcome == GNUNET_NO) &&
698        (s2->expecting_welcome == GNUNET_YES) )
699     return s1;
700   if ( (s1->expecting_welcome == GNUNET_YES) &&
701        (s2->expecting_welcome == GNUNET_NO) )
702     return s2;
703   if (s1->last_activity.value < s2->last_activity.value)
704     return s2;
705   if (s1->last_activity.value > s2->last_activity.value)
706     return s1;
707   if ( (GNUNET_YES == s1->inbound) &&
708        (GNUNET_NO  == s2->inbound) )
709     return s1;
710   if ( (GNUNET_NO  == s1->inbound) &&
711        (GNUNET_YES == s2->inbound) )
712     return s2;
713   return s1;
714 }
715
716
717 /**
718  * Function that can be used by the transport service to transmit
719  * a message using the plugin.   Note that in the case of a
720  * peer disconnecting, the continuation MUST be called
721  * prior to the disconnect notification itself.  This function
722  * will be called with this peer's HELLO message to initiate
723  * a fresh connection to another peer.
724  *
725  * @param cls closure
726  * @param target who should receive this message
727  * @param msg the message to transmit
728  * @param msgbuf_size number of bytes in 'msg'
729  * @param priority how important is the message (most plugins will
730  *                 ignore message priority and just FIFO)
731  * @param timeout how long to wait at most for the transmission (does not
732  *                require plugins to discard the message after the timeout,
733  *                just advisory for the desired delay; most plugins will ignore
734  *                this as well)
735  * @param session which session must be used (or NULL for "any")
736  * @param addr the address to use (can be NULL if the plugin
737  *                is "on its own" (i.e. re-use existing TCP connection))
738  * @param addrlen length of the address in bytes
739  * @param force_address GNUNET_YES if the plugin MUST use the given address,
740  *                GNUNET_NO means the plugin may use any other address and
741  *                GNUNET_SYSERR means that only reliable existing
742  *                bi-directional connections should be used (regardless
743  *                of address)
744  * @param cont continuation to call once the message has
745  *        been transmitted (or if the transport is ready
746  *        for the next transmission call; or if the
747  *        peer disconnected...); can be NULL
748  * @param cont_cls closure for cont
749  * @return number of bytes used (on the physical network, with overheads);
750  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
751  *         and does NOT mean that the message was not transmitted (DV)
752  */
753 static ssize_t
754 tcp_plugin_send (void *cls,
755                  const struct GNUNET_PeerIdentity *target,
756                  const char *msg,
757                  size_t msgbuf_size,
758                  uint32_t priority,
759                  struct GNUNET_TIME_Relative timeout,
760                  struct Session *session,
761                  const void *addr,
762                  size_t addrlen,
763                  int force_address,
764                  GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
765 {
766   struct Plugin *plugin = cls;
767   struct Session *cand_session;
768   struct Session *next;
769   struct PendingMessage *pm;
770   struct GNUNET_CONNECTION_Handle *sa;
771   int af;
772   const void *sb;
773   size_t sbs;
774   struct sockaddr_in a4;
775   struct sockaddr_in6 a6;
776   const struct IPv4TcpAddress *t4;
777   const struct IPv6TcpAddress *t6;
778
779   GNUNET_STATISTICS_update (plugin->env->stats,
780                             gettext_noop ("# bytes TCP was asked to transmit"),
781                             msgbuf_size,
782                             GNUNET_NO);      
783   /* FIXME: we could do this cheaper with a hash table
784      where we could restrict the iteration to entries that match
785      the target peer... */
786   if (session == NULL)
787     {
788       cand_session = NULL;
789       next = plugin->sessions;
790       while (NULL != (session = next)) 
791         {
792           next = session->next;
793           GNUNET_assert (session->client != NULL);
794           if (0 != memcmp (target,
795                            &session->target, 
796                            sizeof (struct GNUNET_PeerIdentity)))
797             continue;
798           if ( ( (GNUNET_SYSERR == force_address) &&
799                  (session->expecting_welcome == GNUNET_NO) ) ||
800                (GNUNET_NO == force_address) )   
801             {
802               cand_session = select_better_session (cand_session,
803                                                     session);
804               continue;
805             }
806           if (GNUNET_SYSERR == force_address)
807             continue;
808           GNUNET_break (GNUNET_YES == force_address);
809           if (addr == NULL)
810             {
811               GNUNET_break (0);
812               break;
813             }
814           if (session->inbound == GNUNET_YES) 
815             continue;
816           if (addrlen != session->connect_alen)
817             continue;
818           if (0 != memcmp (session->connect_addr,
819                            addr,
820                            addrlen))
821             continue;
822           cand_session = select_better_session (cand_session,
823                                                 session);             
824         }
825       session = cand_session;
826     }
827   if ( (session == NULL) &&
828        (addr == NULL) )
829     {
830 #if DEBUG_TCP
831       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
832                        "tcp",
833                        "Asked to transmit to `%4s' without address and I have no existing connection (failing).\n",
834                        GNUNET_i2s (target));
835 #endif
836       GNUNET_STATISTICS_update (plugin->env->stats,
837                                 gettext_noop ("# bytes discarded by TCP (no address and no connection)"),
838                                 msgbuf_size,
839                                 GNUNET_NO);      
840       return -1;
841     }
842   if (session == NULL)
843     {
844       if (addrlen == sizeof (struct IPv6TcpAddress))
845         {
846           t6 = addr;
847           af = AF_INET6;
848           memset (&a6, 0, sizeof (a6));
849 #if HAVE_SOCKADDR_IN_SIN_LEN
850           a6.sin6_len = sizeof (a6);
851 #endif
852           a6.sin6_family = AF_INET6;
853           a6.sin6_port = t6->t6_port;
854           memcpy (a6.sin6_addr.s6_addr,
855                   t6->ipv6_addr,
856                   16);      
857           sb = &a6;
858           sbs = sizeof (a6);
859         }
860       else if (addrlen == sizeof (struct IPv4TcpAddress))
861         {
862           t4 = addr;
863           af = AF_INET;
864           memset (&a4, 0, sizeof (a4));
865 #if HAVE_SOCKADDR_IN_SIN_LEN
866           a4.sin_len = sizeof (a4);
867 #endif
868           a4.sin_family = AF_INET;
869           a4.sin_port = t4->t_port;
870           a4.sin_addr.s_addr = t4->ipv4_addr;
871           sb = &a4;
872           sbs = sizeof (a4);
873         }
874       else
875         {
876           GNUNET_break_op (0);
877           return -1;
878         }
879       sa = GNUNET_CONNECTION_create_from_sockaddr (plugin->env->sched,
880                                                    af, sb, sbs,
881                                                    GNUNET_SERVER_MAX_MESSAGE_SIZE);
882       if (sa == NULL)
883         {
884 #if DEBUG_TCP
885           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
886                            "tcp",
887                            "Failed to create connection to `%4s' at `%s'\n",
888                            GNUNET_i2s (target),
889                            GNUNET_a2s (sb, sbs));
890 #endif
891           GNUNET_STATISTICS_update (plugin->env->stats,
892                                     gettext_noop ("# bytes discarded by TCP (failed to connect)"),
893                                     msgbuf_size,
894                                     GNUNET_NO);      
895           return -1;
896         }
897 #if DEBUG_TCP
898       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
899                        "tcp",
900                        "Asked to transmit to `%4s', creating fresh session using address `%s'.\n",
901                        GNUNET_i2s (target),
902                        GNUNET_a2s (sb, sbs));
903 #endif
904       session = create_session (plugin,
905                                 target,
906                                 GNUNET_SERVER_connect_socket (plugin->server,
907                                                               sa));
908       session->connect_addr = GNUNET_malloc (addrlen);
909       memcpy (session->connect_addr,
910               addr,
911               addrlen);
912       session->connect_alen = addrlen;
913     }
914   GNUNET_assert (session != NULL);
915   GNUNET_assert (session->client != NULL);
916   GNUNET_STATISTICS_update (plugin->env->stats,
917                             gettext_noop ("# bytes currently in TCP buffers"),
918                             msgbuf_size,
919                             GNUNET_NO);      
920   /* create new message entry */
921   pm = GNUNET_malloc (sizeof (struct PendingMessage) + msgbuf_size);
922   pm->msg = (const char*) &pm[1];
923   memcpy (&pm[1], msg, msgbuf_size);
924   pm->message_size = msgbuf_size;
925   pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
926   pm->transmit_cont = cont;
927   pm->transmit_cont_cls = cont_cls;
928
929   /* append pm to pending_messages list */
930   GNUNET_CONTAINER_DLL_insert_after (session->pending_messages_head,
931                                      session->pending_messages_tail,
932                                      session->pending_messages_tail,
933                                      pm);
934 #if DEBUG_TCP
935   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
936                    "tcp",
937                    "Asked to transmit %u bytes to `%s', added message to list.\n",
938                    msgbuf_size,
939                    GNUNET_i2s (target));
940 #endif
941   process_pending_messages (session);
942   return msgbuf_size;
943 }
944
945
946 /**
947  * Function that can be called to force a disconnect from the
948  * specified neighbour.  This should also cancel all previously
949  * scheduled transmissions.  Obviously the transmission may have been
950  * partially completed already, which is OK.  The plugin is supposed
951  * to close the connection (if applicable) and no longer call the
952  * transmit continuation(s).
953  *
954  * Finally, plugin MUST NOT call the services's receive function to
955  * notify the service that the connection to the specified target was
956  * closed after a getting this call.
957  *
958  * @param cls closure
959  * @param target peer for which the last transmission is
960  *        to be cancelled
961  */
962 static void
963 tcp_plugin_disconnect (void *cls,
964                        const struct GNUNET_PeerIdentity *target)
965 {
966   struct Plugin *plugin = cls;
967   struct Session *session;
968   struct Session *next;
969   struct PendingMessage *pm;
970
971 #if DEBUG_TCP
972   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
973                    "tcp",
974                    "Asked to cancel session with `%4s'\n",
975                    GNUNET_i2s (target));
976 #endif
977   next = plugin->sessions;
978   while (NULL != (session = next))
979     {
980       next = session->next;
981       if (0 != memcmp (target,
982                        &session->target,
983                        sizeof (struct GNUNET_PeerIdentity)))
984         continue;
985       pm = session->pending_messages_head;
986       while (pm != NULL)
987         {
988           pm->transmit_cont = NULL;
989           pm->transmit_cont_cls = NULL;
990           pm = pm->next;
991         }
992       disconnect_session (session);
993     }
994 }
995
996
997 /**
998  * Context for address to string conversion.
999  */
1000 struct PrettyPrinterContext
1001 {
1002   /**
1003    * Function to call with the result.
1004    */
1005   GNUNET_TRANSPORT_AddressStringCallback asc;
1006
1007   /**
1008    * Clsoure for 'asc'.
1009    */
1010   void *asc_cls;
1011
1012   /**
1013    * Port to add after the IP address.
1014    */
1015   uint16_t port;
1016 };
1017
1018
1019 /**
1020  * Append our port and forward the result.
1021  *
1022  * @param cls the 'struct PrettyPrinterContext*'
1023  * @param hostname hostname part of the address
1024  */
1025 static void
1026 append_port (void *cls, const char *hostname)
1027 {
1028   struct PrettyPrinterContext *ppc = cls;
1029   char *ret;
1030
1031   if (hostname == NULL)
1032     {
1033       ppc->asc (ppc->asc_cls, NULL);
1034       GNUNET_free (ppc);
1035       return;
1036     }
1037   GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
1038   ppc->asc (ppc->asc_cls, ret);
1039   GNUNET_free (ret);
1040 }
1041
1042
1043 /**
1044  * Convert the transports address to a nice, human-readable
1045  * format.
1046  *
1047  * @param cls closure
1048  * @param type name of the transport that generated the address
1049  * @param addr one of the addresses of the host, NULL for the last address
1050  *        the specific address format depends on the transport
1051  * @param addrlen length of the address
1052  * @param numeric should (IP) addresses be displayed in numeric form?
1053  * @param timeout after how long should we give up?
1054  * @param asc function to call on each string
1055  * @param asc_cls closure for asc
1056  */
1057 static void
1058 tcp_plugin_address_pretty_printer (void *cls,
1059                                    const char *type,
1060                                    const void *addr,
1061                                    size_t addrlen,
1062                                    int numeric,
1063                                    struct GNUNET_TIME_Relative timeout,
1064                                    GNUNET_TRANSPORT_AddressStringCallback asc,
1065                                    void *asc_cls)
1066 {
1067   struct Plugin *plugin = cls;
1068   struct PrettyPrinterContext *ppc;
1069   const void *sb;
1070   size_t sbs;
1071   struct sockaddr_in a4;
1072   struct sockaddr_in6 a6;
1073   const struct IPv4TcpAddress *t4;
1074   const struct IPv6TcpAddress *t6;
1075   int af;
1076   uint16_t port;
1077
1078   if (addrlen == sizeof (struct IPv6TcpAddress))
1079     {
1080       t6 = addr;
1081       af = AF_INET6;
1082       memset (&a6, 0, sizeof (a6));
1083       a6.sin6_family = AF_INET6;
1084       a6.sin6_port = t6->t6_port;
1085       memcpy (a6.sin6_addr.s6_addr,
1086               t6->ipv6_addr,
1087               16);      
1088       port = ntohs (t6->t6_port);
1089       sb = &a6;
1090       sbs = sizeof (a6);
1091     }
1092   else if (addrlen == sizeof (struct IPv4TcpAddress))
1093     {
1094       t4 = addr;
1095       af = AF_INET;
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 */