fixing disconnect handling, making TCP plugin ready for bi-di use
[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_NO
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  * Encapsulation of all of the state of the plugin.
68  */
69 struct Plugin;
70
71
72 /**
73  * Information kept for each message that is yet to
74  * be transmitted.
75  */
76 struct PendingMessage
77 {
78
79   /**
80    * This is a doubly-linked list.
81    */
82   struct PendingMessage *next;
83
84   /**
85    * This is a doubly-linked list.
86    */
87   struct PendingMessage *prev;
88
89   /**
90    * The pending message
91    */
92   const char *msg;
93
94   /**
95    * Continuation function to call once the message
96    * has been sent.  Can be NULL if there is no
97    * continuation to call.
98    */
99   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
100
101   /**
102    * Closure for transmit_cont.
103    */
104   void *transmit_cont_cls;
105
106   /**
107    * Timeout value for the pending message.
108    */
109   struct GNUNET_TIME_Absolute timeout;
110
111   /**
112    * So that the gnunet-service-transport can group messages together,
113    * these pending messages need to accept a message buffer and size
114    * instead of just a GNUNET_MessageHeader.
115    */
116   size_t message_size;
117
118 };
119
120
121 /**
122  * Session handle for TCP connections.
123  */
124 struct Session
125 {
126
127   /**
128    * Stored in a linked list.
129    */
130   struct Session *next;
131
132   /**
133    * Pointer to the global plugin struct.
134    */
135   struct Plugin *plugin;
136
137   /**
138    * The client (used to identify this connection)
139    */
140   struct GNUNET_SERVER_Client *client;
141
142   /**
143    * Messages currently pending for transmission
144    * to this peer, if any.
145    */
146   struct PendingMessage *pending_messages_head;
147
148   /**
149    * Messages currently pending for transmission
150    * to this peer, if any.
151    */
152   struct PendingMessage *pending_messages_tail;
153
154   /**
155    * Handle for pending transmission request.
156    */
157   struct GNUNET_CONNECTION_TransmitHandle *transmit_handle;
158
159   /**
160    * To whom are we talking to (set to our identity
161    * if we are still waiting for the welcome message)
162    */
163   struct GNUNET_PeerIdentity target;
164
165   /**
166    * ID of task used to delay receiving more to throttle sender.
167    */
168   GNUNET_SCHEDULER_TaskIdentifier receive_delay_task;
169
170   /**
171    * Address of the other peer (either based on our 'connect'
172    * call or on our 'accept' call).
173    */
174   void *connect_addr;
175
176   /**
177    * Length of connect_addr.
178    */
179   size_t connect_alen;
180
181   /**
182    * Are we still expecting the welcome message? (GNUNET_YES/GNUNET_NO)
183    */
184   int expecting_welcome;
185
186   /**
187    * Was this a connection that was inbound (we accepted)? (GNUNET_YES/GNUNET_NO)
188    */
189   int inbound;
190
191 };
192
193
194 /**
195  * Encapsulation of all of the state of the plugin.
196  */
197 struct Plugin
198 {
199   /**
200    * Our environment.
201    */
202   struct GNUNET_TRANSPORT_PluginEnvironment *env;
203
204   /**
205    * The listen socket.
206    */
207   struct GNUNET_CONNECTION_Handle *lsock;
208
209   /**
210    * List of open TCP sessions.
211    */
212   struct Session *sessions;
213
214   /**
215    * Handle for the statistics service.
216    */
217   struct GNUNET_STATISTICS_Handle *statistics;
218
219   /**
220    * Handle to the network service.
221    */
222   struct GNUNET_SERVICE_Context *service;
223
224   /**
225    * Handle to the server for this service.
226    */
227   struct GNUNET_SERVER_Handle *server;
228
229   /**
230    * Copy of the handler array where the closures are
231    * set to this struct's instance.
232    */
233   struct GNUNET_SERVER_MessageHandler *handlers;
234
235   /**
236    * Handle for request of hostname resolution, non-NULL if pending.
237    */
238   struct GNUNET_RESOLVER_RequestHandle *hostname_dns;
239
240   /**
241    * ID of task used to update our addresses when one expires.
242    */
243   GNUNET_SCHEDULER_TaskIdentifier address_update_task;
244
245   /**
246    * Port that we are actually listening on.
247    */
248   uint16_t open_port;
249
250   /**
251    * Port that the user said we would have visible to the
252    * rest of the world.
253    */
254   uint16_t adv_port;
255
256 };
257
258
259 /**
260  * Find the session handle for the given client.
261  *
262  * @return NULL if no matching session exists
263  */
264 static struct Session *
265 find_session_by_client (struct Plugin *plugin,
266                         const struct GNUNET_SERVER_Client *client)
267 {
268   struct Session *ret;
269
270   ret = plugin->sessions;
271   while ((ret != NULL) && (client != ret->client))
272     ret = ret->next;
273   return ret;
274 }
275
276
277 /**
278  * Create a new session.  Also queues a welcome message.
279  *
280  * @param plugin us
281  * @param target peer to connect to
282  * @param client client to use
283  * @return new session object
284  */
285 static struct Session *
286 create_session (struct Plugin *plugin,
287                 const struct GNUNET_PeerIdentity *target,
288                 struct GNUNET_SERVER_Client *client)
289 {
290   struct Session *ret;
291   struct PendingMessage *pm;
292   struct WelcomeMessage welcome;
293
294   GNUNET_assert (client != NULL);
295   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
296                    "tcp",
297                    "Creating new session for peer `%4s'\n",
298                    GNUNET_i2s (target));
299   ret = GNUNET_malloc (sizeof (struct Session));
300   ret->plugin = plugin;
301   ret->next = plugin->sessions;
302   plugin->sessions = ret;
303   ret->client = client;
304   ret->target = *target;
305   ret->expecting_welcome = GNUNET_YES;
306   pm = GNUNET_malloc (sizeof (struct PendingMessage) + sizeof (struct WelcomeMessage));
307   pm->msg = (const char*) &pm[1];
308   pm->message_size = sizeof (struct WelcomeMessage);
309   welcome.header.size = htons (sizeof (struct WelcomeMessage));
310   welcome.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME);
311   welcome.clientIdentity = *plugin->env->my_identity;
312   memcpy (&pm[1], &welcome, sizeof (welcome));
313   pm->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
314   GNUNET_STATISTICS_update (plugin->env->stats,
315                             gettext_noop ("# bytes currently in TCP buffers"),
316                             pm->message_size,
317                             GNUNET_NO);      
318   GNUNET_CONTAINER_DLL_insert (ret->pending_messages_head,
319                                ret->pending_messages_tail,
320                                pm);
321   GNUNET_STATISTICS_update (plugin->env->stats,
322                             gettext_noop ("# TCP sessions active"),
323                             1,
324                             GNUNET_NO);      
325   return ret;
326 }
327
328
329 /**
330  * If we have pending messages, ask the server to
331  * transmit them (schedule the respective tasks, etc.)
332  *
333  * @param session for which session should we do this
334  */
335 static void process_pending_messages (struct Session *session);
336
337
338 /**
339  * Function called to notify a client about the socket
340  * being ready to queue more data.  "buf" will be
341  * NULL and "size" zero if the socket was closed for
342  * writing in the meantime.
343  *
344  * @param cls closure
345  * @param size number of bytes available in buf
346  * @param buf where the callee should write the message
347  * @return number of bytes written to buf
348  */
349 static size_t
350 do_transmit (void *cls, size_t size, void *buf)
351 {
352   struct Session *session = cls;
353   struct GNUNET_PeerIdentity pid;
354   struct Plugin *plugin;
355   struct PendingMessage *pos;
356   struct PendingMessage *hd;
357   struct PendingMessage *tl;
358   struct GNUNET_TIME_Absolute now;
359   char *cbuf;
360   size_t ret;
361
362   session->transmit_handle = NULL;
363   plugin = session->plugin;
364   if (buf == NULL)
365     {
366 #if DEBUG_TCP
367       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
368                        "tcp",
369                        "Timeout trying to transmit to peer `%4s', discarding message queue.\n",
370                        GNUNET_i2s (&session->target));
371 #endif
372       /* timeout; cancel all messages that have already expired */
373       hd = NULL;
374       tl = NULL;
375       ret = 0;
376       now = GNUNET_TIME_absolute_get ();
377       while ( (NULL != (pos = session->pending_messages_head)) &&
378               (pos->timeout.value <= now.value) )
379         {
380           GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
381                                        session->pending_messages_tail,
382                                        pos);
383 #if DEBUG_TCP
384           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
385                            "tcp",
386                            "Failed to transmit %u byte message to `%4s'.\n",
387                            pos->message_size,
388                            GNUNET_i2s (&session->target));
389 #endif
390           ret += pos->message_size;
391           GNUNET_CONTAINER_DLL_insert_after (hd, tl, tl, pos);
392         }
393       /* do this call before callbacks (so that if callbacks destroy
394          session, they have a chance to cancel actions done by this
395          call) */
396       process_pending_messages (session);  
397       pid = session->target;
398       /* no do callbacks and do not use session again since
399          the callbacks may abort the session */
400       while (NULL != (pos = hd))
401         {
402           GNUNET_CONTAINER_DLL_remove (hd, tl, pos);
403           if (pos->transmit_cont != NULL)
404             pos->transmit_cont (pos->transmit_cont_cls,
405                                 &pid, GNUNET_SYSERR);
406           GNUNET_free (pos);
407         }
408       GNUNET_STATISTICS_update (plugin->env->stats,
409                                 gettext_noop ("# bytes currently in TCP buffers"),
410                                 - (int64_t) ret,
411                                 GNUNET_NO); 
412       GNUNET_STATISTICS_update (plugin->env->stats,
413                                 gettext_noop ("# bytes discarded by TCP (timeout)"),
414                                 ret,
415                                 GNUNET_NO);      
416       return 0;
417     }
418   /* copy all pending messages that would fit */
419   ret = 0;
420   cbuf = buf;
421   hd = NULL;
422   tl = NULL;
423   while (NULL != (pos = session->pending_messages_head)) 
424     {
425       if (ret + pos->message_size > size) 
426         break;
427       GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
428                                    session->pending_messages_tail,
429                                    pos);
430       GNUNET_assert (size >= pos->message_size);
431       memcpy (cbuf, pos->msg, pos->message_size);
432       cbuf += pos->message_size;
433       ret += pos->message_size;
434       size -= pos->message_size;
435       GNUNET_CONTAINER_DLL_insert_after (hd, tl, tl, pos);
436     }
437   /* schedule 'continuation' before callbacks so that callbacks that
438      cancel everything don't cause us to use a session that no longer
439      exists... */
440   process_pending_messages (session);  
441   pid = session->target;
442   /* we'll now call callbacks that may cancel the session; hence
443      we should not use 'session' after this point */
444   while (NULL != (pos = hd))
445     {
446       GNUNET_CONTAINER_DLL_remove (hd, tl, pos);
447       if (pos->transmit_cont != NULL)
448         pos->transmit_cont (pos->transmit_cont_cls,
449                             &pid, GNUNET_OK);
450       GNUNET_free (pos);
451     }
452   GNUNET_assert (hd == NULL);
453   GNUNET_assert (tl == NULL);
454 #if DEBUG_TCP > 1
455   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
456                    "tcp", "Transmitting %u bytes\n", ret);
457 #endif
458   GNUNET_STATISTICS_update (plugin->env->stats,
459                             gettext_noop ("# bytes currently in TCP buffers"),
460                             - (int64_t) ret,
461                             GNUNET_NO);       
462   GNUNET_STATISTICS_update (plugin->env->stats,
463                             gettext_noop ("# bytes transmitted via TCP"),
464                             ret,
465                             GNUNET_NO);      
466   return ret;
467 }
468
469
470 /**
471  * If we have pending messages, ask the server to
472  * transmit them (schedule the respective tasks, etc.)
473  *
474  * @param session for which session should we do this
475  */
476 static void
477 process_pending_messages (struct Session *session)
478 {
479   struct PendingMessage *pm;
480
481   GNUNET_assert (session->client != NULL);
482   if (session->transmit_handle != NULL)
483     return;
484   if (NULL == (pm = session->pending_messages_head))
485     return;
486   session->transmit_handle
487     = GNUNET_SERVER_notify_transmit_ready (session->client,
488                                            pm->message_size,
489                                            GNUNET_TIME_absolute_get_remaining
490                                            (pm->timeout),
491                                            &do_transmit, session);
492 }
493
494
495 /**
496  * Functions with this signature are called whenever we need
497  * to close a session due to a disconnect or failure to
498  * establish a connection.
499  *
500  * @param session session to close down
501  */
502 static void
503 disconnect_session (struct Session *session)
504 {
505   struct Session *prev;
506   struct Session *pos;
507   struct PendingMessage *pm;
508
509 #if DEBUG_TCP
510   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
511                    "tcp",
512                    "Disconnecting from `%4s' at %s (session %p).\n",
513                    GNUNET_i2s (&session->target),
514                    (session->connect_addr != NULL) ?
515                    GNUNET_a2s (session->connect_addr,
516                                session->connect_alen) : "*", session);
517 #endif
518   /* remove from session list */
519   prev = NULL;
520   pos = session->plugin->sessions;
521   while (pos != session)
522     {
523       prev = pos;
524       pos = pos->next;
525     }
526   if (prev == NULL)
527     session->plugin->sessions = session->next;
528   else
529     prev->next = session->next;
530   /* clean up state */
531   if (session->transmit_handle != NULL)
532     {
533       GNUNET_CONNECTION_notify_transmit_ready_cancel
534         (session->transmit_handle);
535       session->transmit_handle = NULL;
536     }
537   while (NULL != (pm = session->pending_messages_head))
538     {
539 #if DEBUG_TCP
540       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
541                        "tcp",
542                        pm->transmit_cont != NULL
543                        ? "Could not deliver message to `%4s'.\n"
544                        :
545                        "Could not deliver message to `%4s', notifying.\n",
546                        GNUNET_i2s (&session->target));
547 #endif
548       GNUNET_STATISTICS_update (session->plugin->env->stats,
549                                 gettext_noop ("# bytes currently in TCP buffers"),
550                                 - (int64_t) pm->message_size,
551                                 GNUNET_NO);      
552       GNUNET_STATISTICS_update (session->plugin->env->stats,
553                                 gettext_noop ("# bytes discarded by TCP (disconnect)"),
554                                 pm->message_size,
555                                 GNUNET_NO);      
556       GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
557                                    session->pending_messages_tail,
558                                    pm);
559       if (NULL != pm->transmit_cont)
560         pm->transmit_cont (pm->transmit_cont_cls,
561                            &session->target, GNUNET_SYSERR);
562       GNUNET_free (pm);
563     }
564   GNUNET_break (session->client != NULL);
565   if (session->receive_delay_task != GNUNET_SCHEDULER_NO_TASK)
566     {
567       GNUNET_SCHEDULER_cancel (session->plugin->env->sched,
568                                session->receive_delay_task);
569       GNUNET_SERVER_receive_done (session->client, 
570                                   GNUNET_SYSERR);       
571     }
572   GNUNET_SERVER_client_drop (session->client);
573   GNUNET_STATISTICS_update (session->plugin->env->stats,
574                             gettext_noop ("# TCP sessions active"),
575                             -1,
576                             GNUNET_NO);      
577   GNUNET_free_non_null (session->connect_addr);
578   GNUNET_free (session);
579 }
580
581
582 /**
583  * Function that can be used by the transport service to transmit
584  * a message using the plugin.   Note that in the case of a
585  * peer disconnecting, the continuation MUST be called
586  * prior to the disconnect notification itself.  This function
587  * will be called with this peer's HELLO message to initiate
588  * a fresh connection to another peer.
589  *
590  * @param cls closure
591  * @param target who should receive this message
592  * @param msg the message to transmit
593  * @param msgbuf_size number of bytes in 'msg'
594  * @param priority how important is the message (most plugins will
595  *                 ignore message priority and just FIFO)
596  * @param timeout how long to wait at most for the transmission (does not
597  *                require plugins to discard the message after the timeout,
598  *                just advisory for the desired delay; most plugins will ignore
599  *                this as well)
600  * @param addr the address to use (can be NULL if the plugin
601  *                is "on its own" (i.e. re-use existing TCP connection))
602  * @param addrlen length of the address in bytes
603  * @param force_address GNUNET_YES if the plugin MUST use the given address,
604  *                GNUNET_NO means the plugin may use any other address and
605  *                GNUNET_SYSERR means that only reliable existing
606  *                bi-directional connections should be used (regardless
607  *                of address)
608  * @param cont continuation to call once the message has
609  *        been transmitted (or if the transport is ready
610  *        for the next transmission call; or if the
611  *        peer disconnected...); can be NULL
612  * @param cont_cls closure for cont
613  * @return number of bytes used (on the physical network, with overheads);
614  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
615  *         and does NOT mean that the message was not transmitted (DV)
616  */
617 static ssize_t
618 tcp_plugin_send (void *cls,
619                  const struct GNUNET_PeerIdentity *target,
620                  const char *msg,
621                  size_t msgbuf_size,
622                  uint32_t priority,
623                  struct GNUNET_TIME_Relative timeout,
624                  const void *addr,
625                  size_t addrlen,
626                  int force_address,
627                  GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
628 {
629   struct Plugin *plugin = cls;
630   struct Session *session;
631   struct Session *next;
632   struct PendingMessage *pm;
633   struct GNUNET_CONNECTION_Handle *sa;
634   int af;
635
636   GNUNET_STATISTICS_update (plugin->env->stats,
637                             gettext_noop ("# bytes TCP was asked to transmit"),
638                             msgbuf_size,
639                             GNUNET_NO);      
640   /* FIXME: we could do this a cheaper with a hash table
641      where we could restrict the iteration to entries that match
642      the target peer... */
643   next = plugin->sessions;
644   while (NULL != (session = next)) 
645     {
646       next = session->next;
647       if (session->client == NULL) 
648         continue;
649       if (0 != memcmp (target,
650                        &session->target, 
651                        sizeof (struct GNUNET_PeerIdentity)))
652         continue;
653       if (GNUNET_SYSERR == force_address) 
654         {
655           if (session->expecting_welcome == GNUNET_NO)
656             break; /* established and reliable (TCP!) */
657           else
658             continue; /* not established */
659         }
660       if (GNUNET_NO == force_address)
661         break;
662       GNUNET_break (GNUNET_YES == force_address);
663       if (addr == NULL)
664         {
665           GNUNET_break (0);
666           break;
667         }
668       if (session->inbound == GNUNET_YES) 
669         continue;
670       if (addrlen != session->connect_alen)
671         continue;
672       if (0 == memcmp (session->connect_addr,
673                        addr,
674                        addrlen))
675         break;
676     }
677   if ( (session == NULL) &&
678        (addr == NULL) )
679     {
680 #if DEBUG_TCP
681       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
682                        "tcp",
683                        "Asked to transmit to `%4s' without address and I have no existing connection (failing).\n",
684                        GNUNET_i2s (target));
685 #endif
686       GNUNET_STATISTICS_update (plugin->env->stats,
687                                 gettext_noop ("# bytes discarded by TCP (no address and no connection)"),
688                                 msgbuf_size,
689                                 GNUNET_NO);      
690       return -1;
691     }
692   if (session == NULL)
693     {
694       if (sizeof (struct sockaddr_in) == addrlen)
695         {
696           af = AF_INET;
697         }
698       else if (sizeof (struct sockaddr_in6) == addrlen)
699         {
700           af = AF_INET6;
701         }
702       else
703         {
704           GNUNET_break_op (0);
705           return -1;
706         }
707       sa = GNUNET_CONNECTION_create_from_sockaddr (plugin->env->sched,
708                                                    af, addr, addrlen,
709                                                    GNUNET_SERVER_MAX_MESSAGE_SIZE);
710       if (sa == NULL)
711         {
712 #if DEBUG_TCP
713           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
714                            "tcp",
715                            "Failed to create connection to `%4s' at `%s'\n",
716                            GNUNET_i2s (target),
717                            GNUNET_a2s (addr, addrlen));
718 #endif
719           GNUNET_STATISTICS_update (plugin->env->stats,
720                                     gettext_noop ("# bytes discarded by TCP (failed to connect)"),
721                                     msgbuf_size,
722                                     GNUNET_NO);      
723           return -1;
724         }
725 #if DEBUG_TCP
726       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
727                        "tcp",
728                        "Asked to transmit to `%4s', creating fresh session using address `%s'.\n",
729                        GNUNET_i2s (target),
730                        GNUNET_a2s (addr, addrlen));
731 #endif
732       session = create_session (plugin,
733                                 target,
734                                 GNUNET_SERVER_connect_socket (plugin->server,
735                                                               sa));
736       session->connect_addr = GNUNET_malloc (addrlen);
737       memcpy (session->connect_addr,
738               addr,
739               addrlen);
740       session->connect_alen = addrlen;
741     }
742   GNUNET_assert (session != NULL);
743   GNUNET_assert (session->client != NULL);
744   GNUNET_STATISTICS_update (plugin->env->stats,
745                             gettext_noop ("# bytes currently in TCP buffers"),
746                             msgbuf_size,
747                             GNUNET_NO);      
748   /* create new message entry */
749   pm = GNUNET_malloc (sizeof (struct PendingMessage) + msgbuf_size);
750   pm->msg = (const char*) &pm[1];
751   memcpy (&pm[1], msg, msgbuf_size);
752   pm->message_size = msgbuf_size;
753   pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
754   pm->transmit_cont = cont;
755   pm->transmit_cont_cls = cont_cls;
756
757   /* append pm to pending_messages list */
758   GNUNET_CONTAINER_DLL_insert_after (session->pending_messages_head,
759                                      session->pending_messages_tail,
760                                      session->pending_messages_tail,
761                                      pm);
762 #if DEBUG_TCP
763   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
764                    "tcp",
765                    "Asked to transmit %u bytes to `%s', added message to list.\n",
766                    msgbuf_size,
767                    GNUNET_i2s (target));
768 #endif
769   process_pending_messages (session);
770   return msgbuf_size;
771 }
772
773
774 /**
775  * Function that can be called to force a disconnect from the
776  * specified neighbour.  This should also cancel all previously
777  * scheduled transmissions.  Obviously the transmission may have been
778  * partially completed already, which is OK.  The plugin is supposed
779  * to close the connection (if applicable) and no longer call the
780  * transmit continuation(s).
781  *
782  * Finally, plugin MUST NOT call the services's receive function to
783  * notify the service that the connection to the specified target was
784  * closed after a getting this call.
785  *
786  * @param cls closure
787  * @param target peer for which the last transmission is
788  *        to be cancelled
789  */
790 static void
791 tcp_plugin_disconnect (void *cls,
792                        const struct GNUNET_PeerIdentity *target)
793 {
794   struct Plugin *plugin = cls;
795   struct Session *session;
796   struct Session *next;
797   struct PendingMessage *pm;
798
799 #if DEBUG_TCP
800   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
801                    "tcp",
802                    "Asked to cancel session with `%4s'\n",
803                    GNUNET_i2s (target));
804 #endif
805   next = plugin->sessions;
806   while (NULL != (session = next))
807     {
808       next = session->next;
809       if (0 != memcmp (target,
810                        &session->target,
811                        sizeof (struct GNUNET_PeerIdentity)))
812         continue;
813       pm = session->pending_messages_head;
814       while (pm != NULL)
815         {
816           pm->transmit_cont = NULL;
817           pm->transmit_cont_cls = NULL;
818           pm = pm->next;
819         }
820       disconnect_session (session);
821     }
822 }
823
824
825 /**
826  * Context for address to string conversion.
827  */
828 struct PrettyPrinterContext
829 {
830   /**
831    * Function to call with the result.
832    */
833   GNUNET_TRANSPORT_AddressStringCallback asc;
834
835   /**
836    * Clsoure for 'asc'.
837    */
838   void *asc_cls;
839
840   /**
841    * Port to add after the IP address.
842    */
843   uint16_t port;
844 };
845
846
847 /**
848  * Append our port and forward the result.
849  *
850  * @param cls the 'struct PrettyPrinterContext*'
851  * @param hostname hostname part of the address
852  */
853 static void
854 append_port (void *cls, const char *hostname)
855 {
856   struct PrettyPrinterContext *ppc = cls;
857   char *ret;
858
859   if (hostname == NULL)
860     {
861       ppc->asc (ppc->asc_cls, NULL);
862       GNUNET_free (ppc);
863       return;
864     }
865   GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
866   ppc->asc (ppc->asc_cls, ret);
867   GNUNET_free (ret);
868 }
869
870
871 /**
872  * Convert the transports address to a nice, human-readable
873  * format.
874  *
875  * @param cls closure
876  * @param type name of the transport that generated the address
877  * @param addr one of the addresses of the host, NULL for the last address
878  *        the specific address format depends on the transport
879  * @param addrlen length of the address
880  * @param numeric should (IP) addresses be displayed in numeric form?
881  * @param timeout after how long should we give up?
882  * @param asc function to call on each string
883  * @param asc_cls closure for asc
884  */
885 static void
886 tcp_plugin_address_pretty_printer (void *cls,
887                                    const char *type,
888                                    const void *addr,
889                                    size_t addrlen,
890                                    int numeric,
891                                    struct GNUNET_TIME_Relative timeout,
892                                    GNUNET_TRANSPORT_AddressStringCallback asc,
893                                    void *asc_cls)
894 {
895   struct Plugin *plugin = cls;
896   const struct sockaddr_in *v4;
897   const struct sockaddr_in6 *v6;
898   struct PrettyPrinterContext *ppc;
899
900   if ((addrlen != sizeof (struct sockaddr_in)) &&
901       (addrlen != sizeof (struct sockaddr_in6)))
902     {
903       /* invalid address */
904       GNUNET_break_op (0);
905       asc (asc_cls, NULL);
906       return;
907     }
908   ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
909   ppc->asc = asc;
910   ppc->asc_cls = asc_cls;
911   if (addrlen == sizeof (struct sockaddr_in))
912     {
913       v4 = (const struct sockaddr_in *) addr;
914       ppc->port = ntohs (v4->sin_port);
915     }
916   else
917     {
918       v6 = (const struct sockaddr_in6 *) addr;
919       ppc->port = ntohs (v6->sin6_port);
920
921     }
922   GNUNET_RESOLVER_hostname_get (plugin->env->sched,
923                                 plugin->env->cfg,
924                                 addr,
925                                 addrlen,
926                                 !numeric, timeout, &append_port, ppc);
927 }
928
929
930 /**
931  * Check if the given port is plausible (must be either
932  * our listen port or our advertised port).  If it is
933  * neither, we return one of these two ports at random.
934  *
935  * @param plugin global variables
936  * @param in_port port number to check
937  * @return either in_port or a more plausible port
938  */
939 static uint16_t
940 check_port (struct Plugin *plugin, uint16_t in_port)
941 {
942   if ((in_port == plugin->adv_port) || (in_port == plugin->open_port))
943     return in_port;
944   return (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
945                                     2) == 0)
946     ? plugin->open_port : plugin->adv_port;
947 }
948
949
950 /**
951  * Another peer has suggested an address for this peer and transport
952  * plugin.  Check that this could be a valid address.
953  *
954  * @param cls closure, our 'struct Plugin*'
955  * @param addr pointer to the address
956  * @param addrlen length of addr
957  * @return GNUNET_OK if this is a plausible address for this peer
958  *         and transport
959  */
960 static int
961 tcp_plugin_check_address (void *cls, void *addr, size_t addrlen)
962 {
963   struct Plugin *plugin = cls;
964   char buf[sizeof (struct sockaddr_in6)];
965   struct sockaddr_in *v4;
966   struct sockaddr_in6 *v6;
967
968   if ((addrlen != sizeof (struct sockaddr_in)) &&
969       (addrlen != sizeof (struct sockaddr_in6)))
970     {
971       GNUNET_break_op (0);
972       return GNUNET_SYSERR;
973     }
974   memcpy (buf, addr, sizeof (struct sockaddr_in6));
975   if (addrlen == sizeof (struct sockaddr_in))
976     {
977       v4 = (struct sockaddr_in *) buf;
978       v4->sin_port = htons (check_port (plugin, ntohs (v4->sin_port)));
979     }
980   else
981     {
982       v6 = (struct sockaddr_in6 *) buf;
983       v6->sin6_port = htons (check_port (plugin, ntohs (v6->sin6_port)));
984     }
985 #if DEBUG_TCP
986   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
987                    "tcp",
988                    "Informing transport service about my address `%s'.\n",
989                    GNUNET_a2s (addr, addrlen));
990 #endif
991   return GNUNET_OK;
992 }
993
994
995 /**
996  * We've received a welcome from this peer via TCP.  Possibly create a
997  * fresh client record and send back our welcome.
998  *
999  * @param cls closure
1000  * @param client identification of the client
1001  * @param message the actual message
1002  */
1003 static void
1004 handle_tcp_welcome (void *cls,
1005                     struct GNUNET_SERVER_Client *client,
1006                     const struct GNUNET_MessageHeader *message)
1007 {
1008   struct Plugin *plugin = cls;
1009   const struct WelcomeMessage *wm = (const struct WelcomeMessage *) message;
1010   struct Session *session;
1011   size_t alen;
1012   void *vaddr;
1013
1014 #if DEBUG_TCP
1015   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1016                    "tcp",
1017                    "Received %s message from a `%4s/%p'.\n", 
1018                    "WELCOME",
1019                    GNUNET_i2s (&wm->clientIdentity), client);
1020 #endif
1021   GNUNET_STATISTICS_update (plugin->env->stats,
1022                             gettext_noop ("# TCP WELCOME messages received"),
1023                             1,
1024                             GNUNET_NO);      
1025   session = find_session_by_client (plugin, client);
1026   if (session == NULL)
1027     {
1028       GNUNET_SERVER_client_keep (client);
1029       session = create_session (plugin,
1030                                 &wm->clientIdentity, client);
1031       session->inbound = GNUNET_YES;
1032       if (GNUNET_OK ==
1033           GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
1034         {
1035 #if DEBUG_TCP
1036           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1037                            "tcp",
1038                            "Found address `%s' for incoming connection %p\n",
1039                            GNUNET_a2s (vaddr, alen),
1040                            client);
1041 #endif
1042           session->connect_addr = vaddr;
1043           session->connect_alen = alen;
1044         }
1045       else
1046         {
1047 #if DEBUG_TCP
1048           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1049                            "tcp",
1050                            "Did not obtain TCP socket address for incoming connection\n");
1051 #endif
1052         }
1053 #if DEBUG_TCP
1054       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1055                        "tcp",
1056                        "Creating new session %p for connection %p\n",
1057                        session, client);
1058 #endif
1059       process_pending_messages (session);
1060     }
1061   if (session->expecting_welcome != GNUNET_YES)
1062     {
1063       GNUNET_break_op (0);
1064       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1065       return;
1066     }
1067   session->expecting_welcome = GNUNET_NO;
1068   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1069 }
1070
1071
1072 /**
1073  * Task to signal the server that we can continue
1074  * receiving from the TCP client now.
1075  *
1076  * @param cls the 'struct Session*'
1077  * @param tc task context (unused)
1078  */
1079 static void
1080 delayed_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1081 {
1082   struct Session *session = cls;
1083   struct GNUNET_TIME_Relative delay;
1084
1085   session->receive_delay_task = GNUNET_SCHEDULER_NO_TASK;
1086   delay = session->plugin->env->receive (session->plugin->env->cls,
1087                                          &session->target,
1088                                          NULL, 0, NULL, 0);
1089   if (delay.value == 0)
1090     GNUNET_SERVER_receive_done (session->client, GNUNET_OK);
1091   else
1092     session->receive_delay_task = 
1093       GNUNET_SCHEDULER_add_delayed (session->plugin->env->sched,
1094                                     delay, &delayed_done, session);
1095 }
1096
1097
1098 /**
1099  * We've received data for this peer via TCP.  Unbox,
1100  * compute latency and forward.
1101  *
1102  * @param cls closure
1103  * @param client identification of the client
1104  * @param message the actual message
1105  */
1106 static void
1107 handle_tcp_data (void *cls,
1108                  struct GNUNET_SERVER_Client *client,
1109                  const struct GNUNET_MessageHeader *message)
1110 {
1111   struct Plugin *plugin = cls;
1112   struct Session *session;
1113   struct GNUNET_TIME_Relative delay;
1114
1115   session = find_session_by_client (plugin, client);
1116   if (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME == ntohs(message->type))
1117     {
1118       /* We don't want to propagate WELCOME messages up! */
1119       GNUNET_SERVER_receive_done (client, GNUNET_OK);
1120       return; 
1121     }    
1122   if ( (NULL == session) || (GNUNET_NO != session->expecting_welcome))
1123     {
1124       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1125       return;
1126     }
1127 #if DEBUG_TCP
1128   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1129                    "tcp", 
1130                    "Passing %u bytes of type %u from `%4s' to transport service.\n",
1131                    (unsigned int) ntohs (message->size), 
1132                    (unsigned int) ntohs (message->type),
1133                    GNUNET_i2s (&session->target));
1134 #endif
1135   GNUNET_STATISTICS_update (plugin->env->stats,
1136                             gettext_noop ("# bytes received via TCP"),
1137                             ntohs (message->size),
1138                             GNUNET_NO); 
1139   delay = plugin->env->receive (plugin->env->cls, &session->target, message, 1,
1140                                 session->connect_addr,
1141                                 session->connect_alen);
1142
1143   if (delay.value == 0)
1144     GNUNET_SERVER_receive_done (client, GNUNET_OK);
1145   else
1146     session->receive_delay_task = 
1147       GNUNET_SCHEDULER_add_delayed (session->plugin->env->sched,
1148                                     delay, &delayed_done, session);
1149 }
1150
1151
1152 /**
1153  * Handlers for the various TCP messages.
1154  */
1155 static struct GNUNET_SERVER_MessageHandler my_handlers[] = {
1156   {&handle_tcp_welcome, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME,
1157    sizeof (struct WelcomeMessage)},
1158   {&handle_tcp_data, NULL, GNUNET_MESSAGE_TYPE_ALL, 0},
1159   {NULL, NULL, 0, 0}
1160 };
1161
1162
1163 /**
1164  * Functions with this signature are called whenever a peer
1165  * is disconnected on the network level.
1166  *
1167  * @param cls closure
1168  * @param client identification of the client
1169  */
1170 static void
1171 disconnect_notify (void *cls, struct GNUNET_SERVER_Client *client)
1172 {
1173   struct Plugin *plugin = cls;
1174   struct Session *session;
1175
1176   if (client == NULL)
1177     return;
1178   session = find_session_by_client (plugin, client);
1179   if (session == NULL)
1180     return;                     /* unknown, nothing to do */
1181 #if DEBUG_TCP
1182   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1183                    "tcp",
1184                    "Destroying session of `%4s' with %s (%p) due to network-level disconnect.\n",
1185                    GNUNET_i2s (&session->target),
1186                    (session->connect_addr != NULL) ?
1187                    GNUNET_a2s (session->connect_addr,
1188                                session->connect_alen) : "*", client);
1189 #endif
1190   disconnect_session (session);
1191 }
1192
1193
1194 /**
1195  * Add the IP of our network interface to the list of
1196  * our external IP addresses.
1197  */
1198 static int
1199 process_interfaces (void *cls,
1200                     const char *name,
1201                     int isDefault,
1202                     const struct sockaddr *addr, socklen_t addrlen)
1203 {
1204   struct Plugin *plugin = cls;
1205   int af;
1206   struct sockaddr_in *v4;
1207   struct sockaddr_in6 *v6;
1208
1209   af = addr->sa_family;
1210   if (af == AF_INET)
1211     {
1212       v4 = (struct sockaddr_in *) addr;
1213       v4->sin_port = htons (plugin->adv_port);
1214     }
1215   else
1216     {
1217       GNUNET_assert (af == AF_INET6);
1218       v6 = (struct sockaddr_in6 *) addr;
1219       v6->sin6_port = htons (plugin->adv_port);
1220     }
1221   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO |
1222                    GNUNET_ERROR_TYPE_BULK,
1223                    "tcp", _("Found address `%s' (%s)\n"),
1224                    GNUNET_a2s (addr, addrlen), name);
1225   plugin->env->notify_address (plugin->env->cls,
1226                                "tcp",
1227                                addr, addrlen, GNUNET_TIME_UNIT_FOREVER_REL);
1228   return GNUNET_OK;
1229 }
1230
1231
1232 /**
1233  * Function called by the resolver for each address obtained from DNS
1234  * for our own hostname.  Add the addresses to the list of our
1235  * external IP addresses.
1236  *
1237  * @param cls closure
1238  * @param addr one of the addresses of the host, NULL for the last address
1239  * @param addrlen length of the address
1240  */
1241 static void
1242 process_hostname_ips (void *cls,
1243                       const struct sockaddr *addr, socklen_t addrlen)
1244 {
1245   struct Plugin *plugin = cls;
1246
1247   if (addr == NULL)
1248     {
1249       plugin->hostname_dns = NULL;
1250       return;
1251     }
1252   process_interfaces (plugin, "<hostname>", GNUNET_YES, addr, addrlen);
1253 }
1254
1255
1256 /**
1257  * Entry point for the plugin.
1258  */
1259 void *
1260 libgnunet_plugin_transport_tcp_init (void *cls)
1261 {
1262   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1263   struct GNUNET_TRANSPORT_PluginFunctions *api;
1264   struct Plugin *plugin;
1265   struct GNUNET_SERVICE_Context *service;
1266   unsigned long long aport;
1267   unsigned long long bport;
1268   unsigned int i;
1269
1270   service = GNUNET_SERVICE_start ("transport-tcp", env->sched, env->cfg);
1271   if (service == NULL)
1272     {
1273       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
1274                        "tcp",
1275                        _
1276                        ("Failed to start service for `%s' transport plugin.\n"),
1277                        "tcp");
1278       return NULL;
1279     }
1280   aport = 0;
1281   if ((GNUNET_OK !=
1282        GNUNET_CONFIGURATION_get_value_number (env->cfg,
1283                                               "transport-tcp",
1284                                               "PORT",
1285                                               &bport)) ||
1286       (bport > 65535) ||
1287       ((GNUNET_OK ==
1288         GNUNET_CONFIGURATION_get_value_number (env->cfg,
1289                                                "transport-tcp",
1290                                                "ADVERTISED-PORT",
1291                                                &aport)) && (aport > 65535)))
1292     {
1293       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1294                        "tcp",
1295                        _
1296                        ("Require valid port number for service `%s' in configuration!\n"),
1297                        "transport-tcp");
1298       GNUNET_SERVICE_stop (service);
1299       return NULL;
1300     }
1301   if (aport == 0)
1302     aport = bport;
1303   plugin = GNUNET_malloc (sizeof (struct Plugin));
1304   plugin->open_port = bport;
1305   plugin->adv_port = aport;
1306   plugin->env = env;
1307   plugin->lsock = NULL;
1308   plugin->statistics = NULL;
1309   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1310   api->cls = plugin;
1311   api->send = &tcp_plugin_send;
1312   api->disconnect = &tcp_plugin_disconnect;
1313   api->address_pretty_printer = &tcp_plugin_address_pretty_printer;
1314   api->check_address = &tcp_plugin_check_address;
1315   plugin->service = service;
1316   plugin->server = GNUNET_SERVICE_get_server (service);
1317   plugin->handlers = GNUNET_malloc (sizeof (my_handlers));
1318   memcpy (plugin->handlers, my_handlers, sizeof (my_handlers));
1319   for (i = 0;
1320        i <
1321        sizeof (my_handlers) / sizeof (struct GNUNET_SERVER_MessageHandler);
1322        i++)
1323     plugin->handlers[i].callback_cls = plugin;
1324   GNUNET_SERVER_add_handlers (plugin->server, plugin->handlers);
1325
1326   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1327                    "tcp", _("TCP transport listening on port %llu\n"), bport);
1328   if (aport != bport)
1329     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1330                      "tcp",
1331                      _("TCP transport advertises itself as being on port %llu\n"),
1332                      aport);
1333   GNUNET_SERVER_disconnect_notify (plugin->server, 
1334                                    &disconnect_notify,
1335                                    plugin);
1336   /* FIXME: do the two calls below periodically again and
1337      not just once (since the info we get might change...) */
1338   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
1339   plugin->hostname_dns = GNUNET_RESOLVER_hostname_resolve (env->sched,
1340                                                            env->cfg,
1341                                                            AF_UNSPEC,
1342                                                            HOSTNAME_RESOLVE_TIMEOUT,
1343                                                            &process_hostname_ips,
1344                                                            plugin);
1345   return api;
1346 }
1347
1348
1349 /**
1350  * Exit point from the plugin.
1351  */
1352 void *
1353 libgnunet_plugin_transport_tcp_done (void *cls)
1354 {
1355   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1356   struct Plugin *plugin = api->cls;
1357   struct Session *session;
1358
1359   while (NULL != (session = plugin->sessions))
1360     disconnect_session (session);
1361   if (NULL != plugin->hostname_dns)
1362     {
1363       GNUNET_RESOLVER_request_cancel (plugin->hostname_dns);
1364       plugin->hostname_dns = NULL;
1365     }
1366   GNUNET_SERVICE_stop (plugin->service);
1367   GNUNET_free (plugin->handlers);
1368   GNUNET_free (plugin);
1369   GNUNET_free (api);
1370   return NULL;
1371 }
1372
1373 /* end of plugin_transport_tcp.c */