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