doxygen
[oweals/gnunet.git] / src / transport / plugin_transport_tcp.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file transport/plugin_transport_tcp.c
22  * @brief Implementation of the TCP transport service
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_hello_lib.h"
27 #include "gnunet_connection_lib.h"
28 #include "gnunet_container_lib.h"
29 #include "gnunet_os_lib.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_resolver_service.h"
32 #include "gnunet_server_lib.h"
33 #include "gnunet_service_lib.h"
34 #include "gnunet_signatures.h"
35 #include "gnunet_statistics_service.h"
36 #include "gnunet_transport_service.h"
37 #include "plugin_transport.h"
38 #include "transport.h"
39
40 #define DEBUG_TCP GNUNET_YES
41
42 /**
43  * How long until we give up on transmitting the welcome message?
44  */
45 #define HOSTNAME_RESOLVE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
46
47
48 /**
49  * Initial handshake message for a session.
50  */
51 struct WelcomeMessage
52 {
53   /**
54    * Type is GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME.
55    */
56   struct GNUNET_MessageHeader header;
57
58   /**
59    * Identity of the node connecting (TCP client)
60    */
61   struct GNUNET_PeerIdentity clientIdentity;
62
63 };
64
65
66 /**
67  * Network format for IPv4 addresses.
68  */
69 struct IPv4TcpAddress
70 {
71   /**
72    * IPv4 address, in network byte order.
73    */
74   uint32_t ipv4_addr;
75
76   /**
77    * Port number, in network byte order.
78    */
79   uint16_t t_port;
80
81 };
82
83
84 /**
85  * Network format for IPv6 addresses.
86  */
87 struct IPv6TcpAddress
88 {
89   /**
90    * IPv6 address.
91    */
92   unsigned char ipv6_addr[16];
93
94   /**
95    * Port number, in network byte order.
96    */
97   uint16_t t6_port;
98
99 };
100
101
102 /**
103  * Encapsulation of all of the state of the plugin.
104  */
105 struct Plugin;
106
107
108 /**
109  * Information kept for each message that is yet to
110  * be transmitted.
111  */
112 struct PendingMessage
113 {
114
115   /**
116    * This is a doubly-linked list.
117    */
118   struct PendingMessage *next;
119
120   /**
121    * This is a doubly-linked list.
122    */
123   struct PendingMessage *prev;
124
125   /**
126    * The pending message
127    */
128   const char *msg;
129
130   /**
131    * Continuation function to call once the message
132    * has been sent.  Can be NULL if there is no
133    * continuation to call.
134    */
135   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
136
137   /**
138    * Closure for transmit_cont.
139    */
140   void *transmit_cont_cls;
141
142   /**
143    * Timeout value for the pending message.
144    */
145   struct GNUNET_TIME_Absolute timeout;
146
147   /**
148    * So that the gnunet-service-transport can group messages together,
149    * these pending messages need to accept a message buffer and size
150    * instead of just a GNUNET_MessageHeader.
151    */
152   size_t message_size;
153
154 };
155
156
157 /**
158  * Session handle for TCP connections.
159  */
160 struct Session
161 {
162
163   /**
164    * Stored in a linked list.
165    */
166   struct Session *next;
167
168   /**
169    * Pointer to the global plugin struct.
170    */
171   struct Plugin *plugin;
172
173   /**
174    * The client (used to identify this connection)
175    */
176   struct GNUNET_SERVER_Client *client;
177
178   /**
179    * Messages currently pending for transmission
180    * to this peer, if any.
181    */
182   struct PendingMessage *pending_messages_head;
183
184   /**
185    * Messages currently pending for transmission
186    * to this peer, if any.
187    */
188   struct PendingMessage *pending_messages_tail;
189
190   /**
191    * Handle for pending transmission request.
192    */
193   struct GNUNET_CONNECTION_TransmitHandle *transmit_handle;
194
195   /**
196    * To whom are we talking to (set to our identity
197    * if we are still waiting for the welcome message)
198    */
199   struct GNUNET_PeerIdentity target;
200
201   /**
202    * ID of task used to delay receiving more to throttle sender.
203    */
204   GNUNET_SCHEDULER_TaskIdentifier receive_delay_task;
205
206   /**
207    * Address of the other peer (either based on our 'connect'
208    * call or on our 'accept' call).
209    */
210   void *connect_addr;
211
212   /**
213    * Last activity on this connection.  Used to select preferred
214    * connection.
215    */
216   struct GNUNET_TIME_Absolute last_activity;
217
218   /**
219    * Length of connect_addr.
220    */
221   size_t connect_alen;
222
223   /**
224    * Are we still expecting the welcome message? (GNUNET_YES/GNUNET_NO)
225    */
226   int expecting_welcome;
227
228   /**
229    * Was this a connection that was inbound (we accepted)? (GNUNET_YES/GNUNET_NO)
230    */
231   int inbound;
232
233 };
234
235
236 /**
237  * Encapsulation of all of the state of the plugin.
238  */
239 struct Plugin
240 {
241   /**
242    * Our environment.
243    */
244   struct GNUNET_TRANSPORT_PluginEnvironment *env;
245
246   /**
247    * The listen socket.
248    */
249   struct GNUNET_CONNECTION_Handle *lsock;
250
251   /**
252    * List of open TCP sessions.
253    */
254   struct Session *sessions;
255
256   /**
257    * Handle for the statistics service.
258    */
259   struct GNUNET_STATISTICS_Handle *statistics;
260
261   /**
262    * Handle to the network service.
263    */
264   struct GNUNET_SERVICE_Context *service;
265
266   /**
267    * Handle to the server for this service.
268    */
269   struct GNUNET_SERVER_Handle *server;
270
271   /**
272    * Copy of the handler array where the closures are
273    * set to this struct's instance.
274    */
275   struct GNUNET_SERVER_MessageHandler *handlers;
276
277   /**
278    * Handle for request of hostname resolution, non-NULL if pending.
279    */
280   struct GNUNET_RESOLVER_RequestHandle *hostname_dns;
281
282   /**
283    * ID of task used to update our addresses when one expires.
284    */
285   GNUNET_SCHEDULER_TaskIdentifier address_update_task;
286
287   /**
288    * Port that we are actually listening on.
289    */
290   uint16_t open_port;
291
292   /**
293    * Port that the user said we would have visible to the
294    * rest of the world.
295    */
296   uint16_t adv_port;
297
298 };
299
300
301
302
303 /**
304  * Function called for a quick conversion of the binary address to
305  * a numeric address.  Note that the caller must not free the 
306  * address and that the next call to this function is allowed
307  * to override the address again.
308  *
309  * @param cls closure ('struct Plugin*')
310  * @param addr binary address
311  * @param addrlen length of the address
312  * @return string representing the same address 
313  */
314 static const char* 
315 tcp_address_to_string (void *cls,
316                        const void *addr,
317                        size_t addrlen)
318 {
319   static char buf[INET6_ADDRSTRLEN];
320   const void *sb;
321   struct sockaddr_in a4;
322   struct sockaddr_in6 a6;
323   const struct IPv4TcpAddress *t4;
324   const struct IPv6TcpAddress *t6;
325   int af;
326
327   if (addrlen == sizeof (struct IPv6TcpAddress))
328     {
329       t6 = addr;
330       af = AF_INET6;
331       memset (&a6, 0, sizeof (a6));
332       a6.sin6_family = AF_INET6;
333       a6.sin6_port = t6->t6_port;
334       memcpy (a6.sin6_addr.s6_addr,
335               t6->ipv6_addr,
336               16);      
337       sb = &a6;
338     }
339   else if (addrlen == sizeof (struct IPv4TcpAddress))
340     {
341       t4 = addr;
342       af = AF_INET;
343       memset (&a4, 0, sizeof (a4));
344       a4.sin_family = AF_INET;
345       a4.sin_port = t4->t_port;
346       a4.sin_addr.s_addr = t4->ipv4_addr;
347       sb = &a4;
348     }
349   else
350     return NULL;
351   return inet_ntop (af, sb, buf, INET6_ADDRSTRLEN);
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           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           a4.sin_family = AF_INET;
866           a4.sin_port = t4->t_port;
867           a4.sin_addr.s_addr = t4->ipv4_addr;
868           sb = &a4;
869           sbs = sizeof (a4);
870         }
871       else
872         {
873           GNUNET_break_op (0);
874           return -1;
875         }
876       sa = GNUNET_CONNECTION_create_from_sockaddr (plugin->env->sched,
877                                                    af, sb, sbs,
878                                                    GNUNET_SERVER_MAX_MESSAGE_SIZE);
879       if (sa == NULL)
880         {
881 #if DEBUG_TCP
882           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
883                            "tcp",
884                            "Failed to create connection to `%4s' at `%s'\n",
885                            GNUNET_i2s (target),
886                            GNUNET_a2s (sb, sbs));
887 #endif
888           GNUNET_STATISTICS_update (plugin->env->stats,
889                                     gettext_noop ("# bytes discarded by TCP (failed to connect)"),
890                                     msgbuf_size,
891                                     GNUNET_NO);      
892           return -1;
893         }
894 #if DEBUG_TCP
895       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
896                        "tcp",
897                        "Asked to transmit to `%4s', creating fresh session using address `%s'.\n",
898                        GNUNET_i2s (target),
899                        GNUNET_a2s (sb, sbs));
900 #endif
901       session = create_session (plugin,
902                                 target,
903                                 GNUNET_SERVER_connect_socket (plugin->server,
904                                                               sa));
905       session->connect_addr = GNUNET_malloc (addrlen);
906       memcpy (session->connect_addr,
907               addr,
908               addrlen);
909       session->connect_alen = addrlen;
910     }
911   GNUNET_assert (session != NULL);
912   GNUNET_assert (session->client != NULL);
913   GNUNET_STATISTICS_update (plugin->env->stats,
914                             gettext_noop ("# bytes currently in TCP buffers"),
915                             msgbuf_size,
916                             GNUNET_NO);      
917   /* create new message entry */
918   pm = GNUNET_malloc (sizeof (struct PendingMessage) + msgbuf_size);
919   pm->msg = (const char*) &pm[1];
920   memcpy (&pm[1], msg, msgbuf_size);
921   pm->message_size = msgbuf_size;
922   pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
923   pm->transmit_cont = cont;
924   pm->transmit_cont_cls = cont_cls;
925
926   /* append pm to pending_messages list */
927   GNUNET_CONTAINER_DLL_insert_after (session->pending_messages_head,
928                                      session->pending_messages_tail,
929                                      session->pending_messages_tail,
930                                      pm);
931 #if DEBUG_TCP
932   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
933                    "tcp",
934                    "Asked to transmit %u bytes to `%s', added message to list.\n",
935                    msgbuf_size,
936                    GNUNET_i2s (target));
937 #endif
938   process_pending_messages (session);
939   return msgbuf_size;
940 }
941
942
943 /**
944  * Function that can be called to force a disconnect from the
945  * specified neighbour.  This should also cancel all previously
946  * scheduled transmissions.  Obviously the transmission may have been
947  * partially completed already, which is OK.  The plugin is supposed
948  * to close the connection (if applicable) and no longer call the
949  * transmit continuation(s).
950  *
951  * Finally, plugin MUST NOT call the services's receive function to
952  * notify the service that the connection to the specified target was
953  * closed after a getting this call.
954  *
955  * @param cls closure
956  * @param target peer for which the last transmission is
957  *        to be cancelled
958  */
959 static void
960 tcp_plugin_disconnect (void *cls,
961                        const struct GNUNET_PeerIdentity *target)
962 {
963   struct Plugin *plugin = cls;
964   struct Session *session;
965   struct Session *next;
966   struct PendingMessage *pm;
967
968 #if DEBUG_TCP
969   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
970                    "tcp",
971                    "Asked to cancel session with `%4s'\n",
972                    GNUNET_i2s (target));
973 #endif
974   next = plugin->sessions;
975   while (NULL != (session = next))
976     {
977       next = session->next;
978       if (0 != memcmp (target,
979                        &session->target,
980                        sizeof (struct GNUNET_PeerIdentity)))
981         continue;
982       pm = session->pending_messages_head;
983       while (pm != NULL)
984         {
985           pm->transmit_cont = NULL;
986           pm->transmit_cont_cls = NULL;
987           pm = pm->next;
988         }
989       disconnect_session (session);
990     }
991 }
992
993
994 /**
995  * Context for address to string conversion.
996  */
997 struct PrettyPrinterContext
998 {
999   /**
1000    * Function to call with the result.
1001    */
1002   GNUNET_TRANSPORT_AddressStringCallback asc;
1003
1004   /**
1005    * Clsoure for 'asc'.
1006    */
1007   void *asc_cls;
1008
1009   /**
1010    * Port to add after the IP address.
1011    */
1012   uint16_t port;
1013 };
1014
1015
1016 /**
1017  * Append our port and forward the result.
1018  *
1019  * @param cls the 'struct PrettyPrinterContext*'
1020  * @param hostname hostname part of the address
1021  */
1022 static void
1023 append_port (void *cls, const char *hostname)
1024 {
1025   struct PrettyPrinterContext *ppc = cls;
1026   char *ret;
1027
1028   if (hostname == NULL)
1029     {
1030       ppc->asc (ppc->asc_cls, NULL);
1031       GNUNET_free (ppc);
1032       return;
1033     }
1034   GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
1035   ppc->asc (ppc->asc_cls, ret);
1036   GNUNET_free (ret);
1037 }
1038
1039
1040 /**
1041  * Convert the transports address to a nice, human-readable
1042  * format.
1043  *
1044  * @param cls closure
1045  * @param type name of the transport that generated the address
1046  * @param addr one of the addresses of the host, NULL for the last address
1047  *        the specific address format depends on the transport
1048  * @param addrlen length of the address
1049  * @param numeric should (IP) addresses be displayed in numeric form?
1050  * @param timeout after how long should we give up?
1051  * @param asc function to call on each string
1052  * @param asc_cls closure for asc
1053  */
1054 static void
1055 tcp_plugin_address_pretty_printer (void *cls,
1056                                    const char *type,
1057                                    const void *addr,
1058                                    size_t addrlen,
1059                                    int numeric,
1060                                    struct GNUNET_TIME_Relative timeout,
1061                                    GNUNET_TRANSPORT_AddressStringCallback asc,
1062                                    void *asc_cls)
1063 {
1064   struct Plugin *plugin = cls;
1065   struct PrettyPrinterContext *ppc;
1066   const void *sb;
1067   size_t sbs;
1068   struct sockaddr_in a4;
1069   struct sockaddr_in6 a6;
1070   const struct IPv4TcpAddress *t4;
1071   const struct IPv6TcpAddress *t6;
1072   int af;
1073   uint16_t port;
1074
1075   if (addrlen == sizeof (struct IPv6TcpAddress))
1076     {
1077       t6 = addr;
1078       af = AF_INET6;
1079       memset (&a6, 0, sizeof (a6));
1080       a6.sin6_family = AF_INET6;
1081       a6.sin6_port = t6->t6_port;
1082       memcpy (a6.sin6_addr.s6_addr,
1083               t6->ipv6_addr,
1084               16);      
1085       port = ntohs (t6->t6_port);
1086       sb = &a6;
1087       sbs = sizeof (a6);
1088     }
1089   else if (addrlen == sizeof (struct IPv4TcpAddress))
1090     {
1091       t4 = addr;
1092       af = AF_INET;
1093       memset (&a4, 0, sizeof (a4));
1094       a4.sin_family = AF_INET;
1095       a4.sin_port = t4->t_port;
1096       a4.sin_addr.s_addr = t4->ipv4_addr;
1097       port = ntohs (t4->t_port);
1098       sb = &a4;
1099       sbs = sizeof (a4);
1100     }
1101   else
1102     {
1103       /* invalid address */
1104       GNUNET_break_op (0);
1105       asc (asc_cls, NULL);
1106       return;
1107     }
1108   ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
1109   ppc->asc = asc;
1110   ppc->asc_cls = asc_cls;
1111   ppc->port = port;
1112   GNUNET_RESOLVER_hostname_get (plugin->env->sched,
1113                                 plugin->env->cfg,
1114                                 sb,
1115                                 sbs,
1116                                 !numeric, timeout, &append_port, ppc);
1117 }
1118
1119
1120 /**
1121  * Check if the given port is plausible (must be either
1122  * our listen port or our advertised port).  If it is
1123  * neither, we return one of these two ports at random.
1124  *
1125  * @param plugin global variables
1126  * @param in_port port number to check
1127  * @return either in_port or a more plausible port
1128  */
1129 static uint16_t
1130 check_port (struct Plugin *plugin, uint16_t in_port)
1131 {
1132   if ((in_port == plugin->adv_port) || (in_port == plugin->open_port))
1133     return in_port;
1134   return (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1135                                     2) == 0)
1136     ? plugin->open_port : plugin->adv_port;
1137 }
1138
1139
1140 /**
1141  * Another peer has suggested an address for this peer and transport
1142  * plugin.  Check that this could be a valid address. This function
1143  * is not expected to 'validate' the address in the sense of trying to
1144  * connect to it but simply to see if the binary format is technically
1145  * legal for establishing a connection.
1146  *
1147  * @param cls closure, our 'struct Plugin*'
1148  * @param addr pointer to the address
1149  * @param addrlen length of addr
1150  * @return GNUNET_OK if this is a plausible address for this peer
1151  *         and transport, GNUNET_SYSERR if not
1152  */
1153 static int
1154 tcp_plugin_check_address (void *cls, void *addr, size_t addrlen)
1155 {
1156   struct Plugin *plugin = cls;
1157   struct IPv4TcpAddress *v4;
1158   struct IPv6TcpAddress *v6;
1159
1160   if ((addrlen != sizeof (struct IPv4TcpAddress)) &&
1161       (addrlen != sizeof (struct IPv6TcpAddress)))
1162     {
1163       GNUNET_break_op (0);
1164       return GNUNET_SYSERR;
1165     }
1166   if (addrlen == sizeof (struct IPv4TcpAddress))
1167     {
1168       v4 = (struct IPv4TcpAddress *) addr;
1169       v4->t_port = htons (check_port (plugin, ntohs (v4->t_port)));
1170     }
1171   else
1172     {
1173       v6 = (struct IPv6TcpAddress *) addr;
1174       v6->t6_port = htons (check_port (plugin, ntohs (v6->t6_port)));
1175     }
1176   return GNUNET_OK;
1177 }
1178
1179
1180 /**
1181  * We've received a welcome from this peer via TCP.  Possibly create a
1182  * fresh client record and send back our welcome.
1183  *
1184  * @param cls closure
1185  * @param client identification of the client
1186  * @param message the actual message
1187  */
1188 static void
1189 handle_tcp_welcome (void *cls,
1190                     struct GNUNET_SERVER_Client *client,
1191                     const struct GNUNET_MessageHeader *message)
1192 {
1193   struct Plugin *plugin = cls;
1194   const struct WelcomeMessage *wm = (const struct WelcomeMessage *) message;
1195   struct Session *session;
1196   size_t alen;
1197   void *vaddr;
1198   struct IPv4TcpAddress *t4;
1199   struct IPv6TcpAddress *t6;
1200   const struct sockaddr_in *s4;
1201   const struct sockaddr_in6 *s6;
1202
1203
1204 #if DEBUG_TCP
1205   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1206                    "tcp",
1207                    "Received %s message from a `%4s/%p'.\n", 
1208                    "WELCOME",
1209                    GNUNET_i2s (&wm->clientIdentity), client);
1210 #endif
1211   GNUNET_STATISTICS_update (plugin->env->stats,
1212                             gettext_noop ("# TCP WELCOME messages received"),
1213                             1,
1214                             GNUNET_NO);      
1215   session = find_session_by_client (plugin, client);
1216   if (session == NULL)
1217     {
1218       GNUNET_SERVER_client_keep (client);
1219       session = create_session (plugin,
1220                                 &wm->clientIdentity, client);
1221       session->inbound = GNUNET_YES;
1222       if (GNUNET_OK ==
1223           GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
1224         {
1225 #if DEBUG_TCP
1226           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1227                            "tcp",
1228                            "Found address `%s' for incoming connection %p\n",
1229                            GNUNET_a2s (vaddr, alen),
1230                            client);
1231 #endif
1232           if (alen == sizeof (struct sockaddr_in))
1233             {
1234               s4 = vaddr;
1235               t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress));
1236               t4->t_port = s4->sin_port;
1237               t4->ipv4_addr = s4->sin_addr.s_addr;
1238               session->connect_addr = t4;
1239               session->connect_alen = sizeof (struct IPv4TcpAddress);
1240             }
1241           else if (alen == sizeof (struct sockaddr_in6))
1242             {
1243               s6 = vaddr;
1244               t6 = GNUNET_malloc (sizeof (struct IPv6TcpAddress));
1245               t6->t6_port = s6->sin6_port;
1246               memcpy (t6->ipv6_addr,
1247                       s6->sin6_addr.s6_addr,
1248                       16);
1249               session->connect_addr = t6;
1250               session->connect_alen = sizeof (struct IPv6TcpAddress);
1251             }
1252           GNUNET_free (vaddr);
1253         }
1254       else
1255         {
1256 #if DEBUG_TCP
1257           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1258                            "tcp",
1259                            "Did not obtain TCP socket address for incoming connection\n");
1260 #endif
1261         }
1262 #if DEBUG_TCP
1263       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1264                        "tcp",
1265                        "Creating new session %p for connection %p\n",
1266                        session, client);
1267 #endif
1268       process_pending_messages (session);
1269     }
1270   if (session->expecting_welcome != GNUNET_YES)
1271     {
1272       GNUNET_break_op (0);
1273       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1274       return;
1275     }
1276   session->last_activity = GNUNET_TIME_absolute_get ();
1277   session->expecting_welcome = GNUNET_NO;
1278   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1279 }
1280
1281
1282 /**
1283  * Task to signal the server that we can continue
1284  * receiving from the TCP client now.
1285  *
1286  * @param cls the 'struct Session*'
1287  * @param tc task context (unused)
1288  */
1289 static void
1290 delayed_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1291 {
1292   struct Session *session = cls;
1293   struct GNUNET_TIME_Relative delay;
1294
1295   session->receive_delay_task = GNUNET_SCHEDULER_NO_TASK;
1296   delay = session->plugin->env->receive (session->plugin->env->cls,
1297                                          &session->target,
1298                                          NULL, 0, 
1299                                          session,
1300                                          NULL, 0);
1301   if (delay.value == 0)
1302     GNUNET_SERVER_receive_done (session->client, GNUNET_OK);
1303   else
1304     session->receive_delay_task = 
1305       GNUNET_SCHEDULER_add_delayed (session->plugin->env->sched,
1306                                     delay, &delayed_done, session);
1307 }
1308
1309
1310 /**
1311  * We've received data for this peer via TCP.  Unbox,
1312  * compute latency and forward.
1313  *
1314  * @param cls closure
1315  * @param client identification of the client
1316  * @param message the actual message
1317  */
1318 static void
1319 handle_tcp_data (void *cls,
1320                  struct GNUNET_SERVER_Client *client,
1321                  const struct GNUNET_MessageHeader *message)
1322 {
1323   struct Plugin *plugin = cls;
1324   struct Session *session;
1325   struct GNUNET_TIME_Relative delay;
1326
1327   if (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME == ntohs(message->type))
1328     {
1329       /* We don't want to propagate WELCOME messages up! */
1330       GNUNET_SERVER_receive_done (client, GNUNET_OK);
1331       return; 
1332     }    
1333   session = find_session_by_client (plugin, client);
1334   if ( (NULL == session) || (GNUNET_NO != session->expecting_welcome))
1335     {
1336       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1337       return;
1338     }
1339   session->last_activity = GNUNET_TIME_absolute_get ();
1340 #if DEBUG_TCP
1341   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1342                    "tcp", 
1343                    "Passing %u bytes of type %u from `%4s' to transport service.\n",
1344                    (unsigned int) ntohs (message->size), 
1345                    (unsigned int) ntohs (message->type),
1346                    GNUNET_i2s (&session->target));
1347 #endif
1348   GNUNET_STATISTICS_update (plugin->env->stats,
1349                             gettext_noop ("# bytes received via TCP"),
1350                             ntohs (message->size),
1351                             GNUNET_NO); 
1352   delay = plugin->env->receive (plugin->env->cls, &session->target, message, 1,
1353                                 session, 
1354                                 (GNUNET_YES == session->inbound) ? NULL : session->connect_addr,
1355                                 (GNUNET_YES == session->inbound) ? 0 : session->connect_alen);
1356   if (delay.value == 0)
1357     GNUNET_SERVER_receive_done (client, GNUNET_OK);
1358   else
1359     session->receive_delay_task = 
1360       GNUNET_SCHEDULER_add_delayed (session->plugin->env->sched,
1361                                     delay, &delayed_done, session);
1362 }
1363
1364
1365 /**
1366  * Handlers for the various TCP messages.
1367  */
1368 static struct GNUNET_SERVER_MessageHandler my_handlers[] = {
1369   {&handle_tcp_welcome, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME,
1370    sizeof (struct WelcomeMessage)},
1371   {&handle_tcp_data, NULL, GNUNET_MESSAGE_TYPE_ALL, 0},
1372   {NULL, NULL, 0, 0}
1373 };
1374
1375
1376 /**
1377  * Functions with this signature are called whenever a peer
1378  * is disconnected on the network level.
1379  *
1380  * @param cls closure
1381  * @param client identification of the client
1382  */
1383 static void
1384 disconnect_notify (void *cls, 
1385                    struct GNUNET_SERVER_Client *client)
1386 {
1387   struct Plugin *plugin = cls;
1388   struct Session *session;
1389
1390   if (client == NULL)
1391     return;
1392   session = find_session_by_client (plugin, client);
1393   if (session == NULL)
1394     return;                     /* unknown, nothing to do */
1395 #if DEBUG_TCP
1396   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1397                    "tcp",
1398                    "Destroying session of `%4s' with %s (%p) due to network-level disconnect.\n",
1399                    GNUNET_i2s (&session->target),
1400                    (session->connect_addr != NULL) ?
1401                    tcp_address_to_string (session->plugin,
1402                                           session->connect_addr,
1403                                           session->connect_alen) : "*",
1404                    client);
1405 #endif
1406   disconnect_session (session);
1407 }
1408
1409
1410 /**
1411  * Add the IP of our network interface to the list of
1412  * our external IP addresses.
1413  *
1414  * @param cls the 'struct Plugin*'
1415  * @param name name of the interface
1416  * @param isDefault do we think this may be our default interface
1417  * @param addr address of the interface
1418  * @param addrlen number of bytes in addr
1419  * @return GNUNET_OK to continue iterating
1420  */
1421 static int
1422 process_interfaces (void *cls,
1423                     const char *name,
1424                     int isDefault,
1425                     const struct sockaddr *addr, socklen_t addrlen)
1426 {
1427   struct Plugin *plugin = cls;
1428   int af;
1429   struct IPv4TcpAddress t4;
1430   struct IPv6TcpAddress t6;
1431   void *arg;
1432   uint16_t args;
1433
1434   af = addr->sa_family;
1435   if (af == AF_INET)
1436     {
1437       t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
1438       t4.t_port = htons (plugin->adv_port);
1439       arg = &t4;
1440       args = sizeof (t4);
1441     }
1442   else if (af == AF_INET6)
1443     {
1444       memcpy (t6.ipv6_addr,
1445               ((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
1446               16);
1447       t6.t6_port = htons (plugin->adv_port);
1448       arg = &t6;
1449       args = sizeof (t6);
1450     }
1451   else
1452     {
1453       GNUNET_break (0);
1454       return GNUNET_OK;
1455     }
1456   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO |
1457                    GNUNET_ERROR_TYPE_BULK,
1458                    "tcp", 
1459                    _("Found address `%s' (%s)\n"),
1460                    GNUNET_a2s (addr, addrlen), name);
1461   plugin->env->notify_address (plugin->env->cls,
1462                                "tcp",
1463                                arg, args, GNUNET_TIME_UNIT_FOREVER_REL);
1464   return GNUNET_OK;
1465 }
1466
1467
1468 /**
1469  * Function called by the resolver for each address obtained from DNS
1470  * for our own hostname.  Add the addresses to the list of our
1471  * external IP addresses.
1472  *
1473  * @param cls closure
1474  * @param addr one of the addresses of the host, NULL for the last address
1475  * @param addrlen length of the address
1476  */
1477 static void
1478 process_hostname_ips (void *cls,
1479                       const struct sockaddr *addr, socklen_t addrlen)
1480 {
1481   struct Plugin *plugin = cls;
1482
1483   if (addr == NULL)
1484     {
1485       plugin->hostname_dns = NULL;
1486       return;
1487     }
1488   process_interfaces (plugin, "<hostname>", GNUNET_YES, addr, addrlen);
1489 }
1490
1491
1492 /**
1493  * Entry point for the plugin.
1494  *
1495  * @param cls closure, the 'struct GNUNET_TRANSPORT_PluginEnvironment*'
1496  * @return the 'struct GNUNET_TRANSPORT_PluginFunctions*' or NULL on error
1497  */
1498 void *
1499 libgnunet_plugin_transport_tcp_init (void *cls)
1500 {
1501   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1502   struct GNUNET_TRANSPORT_PluginFunctions *api;
1503   struct Plugin *plugin;
1504   struct GNUNET_SERVICE_Context *service;
1505   unsigned long long aport;
1506   unsigned long long bport;
1507   unsigned int i;
1508
1509   service = GNUNET_SERVICE_start ("transport-tcp", env->sched, env->cfg);
1510   if (service == NULL)
1511     {
1512       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
1513                        "tcp",
1514                        _
1515                        ("Failed to start service for `%s' transport plugin.\n"),
1516                        "tcp");
1517       return NULL;
1518     }
1519   aport = 0;
1520   if ((GNUNET_OK !=
1521        GNUNET_CONFIGURATION_get_value_number (env->cfg,
1522                                               "transport-tcp",
1523                                               "PORT",
1524                                               &bport)) ||
1525       (bport > 65535) ||
1526       ((GNUNET_OK ==
1527         GNUNET_CONFIGURATION_get_value_number (env->cfg,
1528                                                "transport-tcp",
1529                                                "ADVERTISED-PORT",
1530                                                &aport)) && (aport > 65535)))
1531     {
1532       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1533                        "tcp",
1534                        _
1535                        ("Require valid port number for service `%s' in configuration!\n"),
1536                        "transport-tcp");
1537       GNUNET_SERVICE_stop (service);
1538       return NULL;
1539     }
1540   if (aport == 0)
1541     aport = bport;
1542   plugin = GNUNET_malloc (sizeof (struct Plugin));
1543   plugin->open_port = bport;
1544   plugin->adv_port = aport;
1545   plugin->env = env;
1546   plugin->lsock = NULL;
1547   plugin->statistics = NULL;
1548   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1549   api->cls = plugin;
1550   api->send = &tcp_plugin_send;
1551   api->disconnect = &tcp_plugin_disconnect;
1552   api->address_pretty_printer = &tcp_plugin_address_pretty_printer;
1553   api->check_address = &tcp_plugin_check_address;
1554   api->address_to_string = &tcp_address_to_string;
1555   plugin->service = service;
1556   plugin->server = GNUNET_SERVICE_get_server (service);
1557   plugin->handlers = GNUNET_malloc (sizeof (my_handlers));
1558   memcpy (plugin->handlers, my_handlers, sizeof (my_handlers));
1559   for (i = 0;
1560        i <
1561        sizeof (my_handlers) / sizeof (struct GNUNET_SERVER_MessageHandler);
1562        i++)
1563     plugin->handlers[i].callback_cls = plugin;
1564   GNUNET_SERVER_add_handlers (plugin->server, plugin->handlers);
1565
1566   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1567                    "tcp", _("TCP transport listening on port %llu\n"), bport);
1568   if (aport != bport)
1569     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1570                      "tcp",
1571                      _("TCP transport advertises itself as being on port %llu\n"),
1572                      aport);
1573   GNUNET_SERVER_disconnect_notify (plugin->server, 
1574                                    &disconnect_notify,
1575                                    plugin);
1576   /* FIXME: do the two calls below periodically again and
1577      not just once (since the info we get might change...) */
1578   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
1579   plugin->hostname_dns = GNUNET_RESOLVER_hostname_resolve (env->sched,
1580                                                            env->cfg,
1581                                                            AF_UNSPEC,
1582                                                            HOSTNAME_RESOLVE_TIMEOUT,
1583                                                            &process_hostname_ips,
1584                                                            plugin);
1585   return api;
1586 }
1587
1588
1589 /**
1590  * Exit point from the plugin.
1591  */
1592 void *
1593 libgnunet_plugin_transport_tcp_done (void *cls)
1594 {
1595   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1596   struct Plugin *plugin = api->cls;
1597   struct Session *session;
1598
1599   while (NULL != (session = plugin->sessions))
1600     disconnect_session (session);
1601   if (NULL != plugin->hostname_dns)
1602     {
1603       GNUNET_RESOLVER_request_cancel (plugin->hostname_dns);
1604       plugin->hostname_dns = NULL;
1605     }
1606   GNUNET_SERVICE_stop (plugin->service);
1607   GNUNET_free (plugin->handlers);
1608   GNUNET_free (plugin);
1609   GNUNET_free (api);
1610   return NULL;
1611 }
1612
1613 /* end of plugin_transport_tcp.c */