2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Christian Grothoff (and other contributing authors)
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 3, or (at your
8 option) any later version.
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.
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.
21 * @file transport/plugin_transport_tcp.c
22 * @brief Implementation of the TCP transport service
23 * @author Christian Grothoff
26 #include "gnunet_hello_lib.h"
27 #include "gnunet_constants.h"
28 #include "gnunet_connection_lib.h"
29 #include "gnunet_container_lib.h"
30 #include "gnunet_nat_lib.h"
31 #include "gnunet_os_lib.h"
32 #include "gnunet_protocols.h"
33 #include "gnunet_resolver_service.h"
34 #include "gnunet_server_lib.h"
35 #include "gnunet_service_lib.h"
36 #include "gnunet_signatures.h"
37 #include "gnunet_statistics_service.h"
38 #include "gnunet_transport_service.h"
39 #include "gnunet_transport_plugin.h"
40 #include "transport.h"
42 #define DEBUG_TCP GNUNET_NO
44 #define DEBUG_TCP_NAT GNUNET_NO
47 * Initial handshake message for a session.
52 * Type is GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME.
54 struct GNUNET_MessageHeader header;
57 * Identity of the node connecting (TCP client)
59 struct GNUNET_PeerIdentity clientIdentity;
65 * Basically a WELCOME message, but with the purpose
66 * of giving the waiting peer a client handle to use
68 struct TCP_NAT_ProbeMessage
71 * Type is GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE.
73 struct GNUNET_MessageHeader header;
76 * Identity of the sender of the message.
78 struct GNUNET_PeerIdentity clientIdentity;
84 * Context for sending a NAT probe via TCP.
86 struct TCPProbeContext
90 * Active probes are kept in a DLL.
92 struct TCPProbeContext *next;
95 * Active probes are kept in a DLL.
97 struct TCPProbeContext *prev;
102 struct GNUNET_CONNECTION_Handle *sock;
105 * Message to be sent.
107 struct TCP_NAT_ProbeMessage message;
110 * Handle to the transmission.
112 struct GNUNET_CONNECTION_TransmitHandle *transmit_handle;
115 * Transport plugin handle.
117 struct Plugin *plugin;
122 * Network format for IPv4 addresses.
124 struct IPv4TcpAddress
127 * IPv4 address, in network byte order.
129 uint32_t ipv4_addr GNUNET_PACKED;
132 * Port number, in network byte order.
134 uint16_t t4_port GNUNET_PACKED;
140 * Network format for IPv6 addresses.
142 struct IPv6TcpAddress
147 struct in6_addr ipv6_addr GNUNET_PACKED;
150 * Port number, in network byte order.
152 uint16_t t6_port GNUNET_PACKED;
158 * Encapsulation of all of the state of the plugin.
164 * Information kept for each message that is yet to
167 struct PendingMessage
171 * This is a doubly-linked list.
173 struct PendingMessage *next;
176 * This is a doubly-linked list.
178 struct PendingMessage *prev;
181 * The pending message
186 * Continuation function to call once the message
187 * has been sent. Can be NULL if there is no
188 * continuation to call.
190 GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
193 * Closure for transmit_cont.
195 void *transmit_cont_cls;
198 * Timeout value for the pending message.
200 struct GNUNET_TIME_Absolute timeout;
203 * So that the gnunet-service-transport can group messages together,
204 * these pending messages need to accept a message buffer and size
205 * instead of just a GNUNET_MessageHeader.
213 * Session handle for TCP connections.
221 struct SessionHeader header;
224 * Stored in a linked list.
226 struct Session *next;
229 * Pointer to the global plugin struct.
231 struct Plugin *plugin;
234 * The client (used to identify this connection)
236 struct GNUNET_SERVER_Client *client;
239 * Messages currently pending for transmission
240 * to this peer, if any.
242 struct PendingMessage *pending_messages_head;
245 * Messages currently pending for transmission
246 * to this peer, if any.
248 struct PendingMessage *pending_messages_tail;
251 * Handle for pending transmission request.
253 struct GNUNET_CONNECTION_TransmitHandle *transmit_handle;
256 * To whom are we talking to (set to our identity
257 * if we are still waiting for the welcome message)
259 struct GNUNET_PeerIdentity target;
262 * ID of task used to delay receiving more to throttle sender.
264 GNUNET_SCHEDULER_TaskIdentifier receive_delay_task;
267 * Address of the other peer (either based on our 'connect'
268 * call or on our 'accept' call).
273 * Last activity on this connection. Used to select preferred
276 struct GNUNET_TIME_Absolute last_activity;
279 * Length of connect_addr.
284 * Are we still expecting the welcome message? (GNUNET_YES/GNUNET_NO)
286 int expecting_welcome;
289 * Was this a connection that was inbound (we accepted)? (GNUNET_YES/GNUNET_NO)
294 * Was this session created using NAT traversal?
302 * Encapsulation of all of the state of the plugin.
309 struct GNUNET_TRANSPORT_PluginEnvironment *env;
314 struct GNUNET_CONNECTION_Handle *lsock;
317 * Our handle to the NAT module.
319 struct GNUNET_NAT_Handle *nat;
322 * List of open TCP sessions.
324 struct Session *sessions;
327 * Handle to the network service.
329 struct GNUNET_SERVICE_Context *service;
332 * Handle to the server for this service.
334 struct GNUNET_SERVER_Handle *server;
337 * Copy of the handler array where the closures are
338 * set to this struct's instance.
340 struct GNUNET_SERVER_MessageHandler *handlers;
343 * Map of peers we have tried to contact behind a NAT
345 struct GNUNET_CONTAINER_MultiHashMap *nat_wait_conns;
348 * List of active TCP probes.
350 struct TCPProbeContext *probe_head;
353 * List of active TCP probes.
355 struct TCPProbeContext *probe_tail;
358 * Handle for (DYN)DNS lookup of our external IP.
360 struct GNUNET_RESOLVER_RequestHandle *ext_dns;
363 * How many more TCP sessions are we allowed to open right now?
365 unsigned long long max_connections;
368 * ID of task used to update our addresses when one expires.
370 GNUNET_SCHEDULER_TaskIdentifier address_update_task;
373 * Port that we are actually listening on.
378 * Port that the user said we would have visible to the
387 * Function to check if an inbound connection is acceptable.
388 * Mostly used to limit the total number of open connections
391 * @param cls the 'struct Plugin'
392 * @param ucred credentials, if available, otherwise NULL
393 * @param addr address
394 * @param addrlen length of address
395 * @return GNUNET_YES to allow, GNUNET_NO to deny, GNUNET_SYSERR
396 * for unknown address family (will be denied).
399 plugin_tcp_access_check (void *cls,
400 const struct GNUNET_CONNECTION_Credentials *ucred,
401 const struct sockaddr *addr, socklen_t addrlen)
403 struct Plugin *plugin = cls;
405 if (0 == plugin->max_connections)
407 plugin->max_connections--;
413 * Our external IP address/port mapping has changed.
415 * @param cls closure, the 'struct LocalAddrList'
416 * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean
417 * the previous (now invalid) one
418 * @param addr either the previous or the new public IP address
419 * @param addrlen actual lenght of the address
422 tcp_nat_port_map_callback (void *cls,
424 const struct sockaddr *addr, socklen_t addrlen)
426 struct Plugin *plugin = cls;
427 struct IPv4TcpAddress t4;
428 struct IPv6TcpAddress t6;
432 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
434 "NPMC called with %d for address `%s'\n",
435 add_remove, GNUNET_a2s (addr, addrlen));
436 /* convert 'addr' to our internal format */
437 switch (addr->sa_family)
440 GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
441 t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
442 t4.t4_port = ((struct sockaddr_in *) addr)->sin_port;
447 GNUNET_assert (addrlen == sizeof (struct sockaddr_in6));
448 memcpy (&t6.ipv6_addr,
449 &((struct sockaddr_in6 *) addr)->sin6_addr,
450 sizeof (struct in6_addr));
451 t6.t6_port = ((struct sockaddr_in6 *) addr)->sin6_port;
459 /* modify our published address list */
460 plugin->env->notify_address (plugin->env->cls, add_remove, arg, args);
465 * Function called for a quick conversion of the binary address to
466 * a numeric address. Note that the caller must not free the
467 * address and that the next call to this function is allowed
468 * to override the address again.
470 * @param cls closure ('struct Plugin*')
471 * @param addr binary address
472 * @param addrlen length of the address
473 * @return string representing the same address
476 tcp_address_to_string (void *cls, const void *addr, size_t addrlen)
478 static char rbuf[INET6_ADDRSTRLEN + 12];
479 char buf[INET6_ADDRSTRLEN];
483 const struct IPv4TcpAddress *t4;
484 const struct IPv6TcpAddress *t6;
488 if (addrlen == sizeof (struct IPv6TcpAddress))
492 port = ntohs (t6->t6_port);
493 memcpy (&a6, &t6->ipv6_addr, sizeof (a6));
496 else if (addrlen == sizeof (struct IPv4TcpAddress))
500 port = ntohs (t4->t4_port);
501 memcpy (&a4, &t4->ipv4_addr, sizeof (a4));
506 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
508 _("Unexpected address length: %u bytes\n"),
509 (unsigned int) addrlen);
513 if (NULL == inet_ntop (af, sb, buf, INET6_ADDRSTRLEN))
515 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
518 GNUNET_snprintf (rbuf,
520 (af == AF_INET6) ? "[%s]:%u" : "%s:%u", buf, port);
526 * Find the session handle for the given client.
528 * @param plugin the plugin
529 * @param client which client to find the session handle for
530 * @return NULL if no matching session exists
532 static struct Session *
533 find_session_by_client (struct Plugin *plugin,
534 const struct GNUNET_SERVER_Client *client)
538 ret = plugin->sessions;
539 while ((ret != NULL) && (client != ret->client))
546 * Create a new session. Also queues a welcome message.
548 * @param plugin the plugin
549 * @param target peer to connect to
550 * @param client client to use
551 * @param is_nat this a NAT session, we should wait for a client to
552 * connect to us from an address, then assign that to
554 * @return new session object
556 static struct Session *
557 create_session (struct Plugin *plugin,
558 const struct GNUNET_PeerIdentity *target,
559 struct GNUNET_SERVER_Client *client, int is_nat)
562 struct PendingMessage *pm;
563 struct WelcomeMessage welcome;
565 if (is_nat != GNUNET_YES)
566 GNUNET_assert (client != NULL);
568 GNUNET_assert (client == NULL);
570 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
572 "Creating new session for peer `%4s'\n",
573 GNUNET_i2s (target));
575 ret = GNUNET_malloc (sizeof (struct Session));
576 ret->last_activity = GNUNET_TIME_absolute_get ();
577 ret->plugin = plugin;
578 ret->is_nat = is_nat;
579 if (is_nat != GNUNET_YES) /* If not a NAT WAIT conn, add it to global list */
581 ret->next = plugin->sessions;
582 plugin->sessions = ret;
584 ret->client = client;
585 ret->target = *target;
586 ret->expecting_welcome = GNUNET_YES;
587 pm = GNUNET_malloc (sizeof (struct PendingMessage) +
588 sizeof (struct WelcomeMessage));
589 pm->msg = (const char *) &pm[1];
590 pm->message_size = sizeof (struct WelcomeMessage);
591 welcome.header.size = htons (sizeof (struct WelcomeMessage));
592 welcome.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME);
593 welcome.clientIdentity = *plugin->env->my_identity;
594 memcpy (&pm[1], &welcome, sizeof (welcome));
595 pm->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
596 GNUNET_STATISTICS_update (plugin->env->stats,
597 gettext_noop ("# bytes currently in TCP buffers"),
598 pm->message_size, GNUNET_NO);
599 GNUNET_CONTAINER_DLL_insert (ret->pending_messages_head,
600 ret->pending_messages_tail, pm);
601 if (is_nat != GNUNET_YES)
602 GNUNET_STATISTICS_update (plugin->env->stats,
603 gettext_noop ("# TCP sessions active"),
610 * If we have pending messages, ask the server to
611 * transmit them (schedule the respective tasks, etc.)
613 * @param session for which session should we do this
615 static void process_pending_messages (struct Session *session);
619 * Function called to notify a client about the socket
620 * being ready to queue more data. "buf" will be
621 * NULL and "size" zero if the socket was closed for
622 * writing in the meantime.
625 * @param size number of bytes available in buf
626 * @param buf where the callee should write the message
627 * @return number of bytes written to buf
630 do_transmit (void *cls, size_t size, void *buf)
632 struct Session *session = cls;
633 struct GNUNET_PeerIdentity pid;
634 struct Plugin *plugin;
635 struct PendingMessage *pos;
636 struct PendingMessage *hd;
637 struct PendingMessage *tl;
638 struct GNUNET_TIME_Absolute now;
642 GNUNET_assert (session != NULL);
643 session->transmit_handle = NULL;
644 plugin = session->plugin;
648 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
650 "Timeout trying to transmit to peer `%4s', discarding message queue.\n",
651 GNUNET_i2s (&session->target));
653 /* timeout; cancel all messages that have already expired */
657 now = GNUNET_TIME_absolute_get ();
658 while ((NULL != (pos = session->pending_messages_head)) &&
659 (pos->timeout.abs_value <= now.abs_value))
661 GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
662 session->pending_messages_tail, pos);
664 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
666 "Failed to transmit %u byte message to `%4s'.\n",
667 pos->message_size, GNUNET_i2s (&session->target));
669 ret += pos->message_size;
670 GNUNET_CONTAINER_DLL_insert_after (hd, tl, tl, pos);
672 /* do this call before callbacks (so that if callbacks destroy
673 * session, they have a chance to cancel actions done by this
675 process_pending_messages (session);
676 pid = session->target;
677 /* no do callbacks and do not use session again since
678 * the callbacks may abort the session */
679 while (NULL != (pos = hd))
681 GNUNET_CONTAINER_DLL_remove (hd, tl, pos);
682 if (pos->transmit_cont != NULL)
683 pos->transmit_cont (pos->transmit_cont_cls, &pid, GNUNET_SYSERR);
686 GNUNET_STATISTICS_update (plugin->env->stats,
687 gettext_noop ("# bytes currently in TCP buffers"),
688 -(int64_t) ret, GNUNET_NO);
689 GNUNET_STATISTICS_update (plugin->env->stats,
691 ("# bytes discarded by TCP (timeout)"), ret,
695 /* copy all pending messages that would fit */
700 while (NULL != (pos = session->pending_messages_head))
702 if (ret + pos->message_size > size)
704 GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
705 session->pending_messages_tail, pos);
706 GNUNET_assert (size >= pos->message_size);
707 /* FIXME: this memcpy can be up to 7% of our total runtime */
708 memcpy (cbuf, pos->msg, pos->message_size);
709 cbuf += pos->message_size;
710 ret += pos->message_size;
711 size -= pos->message_size;
712 GNUNET_CONTAINER_DLL_insert_after (hd, tl, tl, pos);
714 /* schedule 'continuation' before callbacks so that callbacks that
715 * cancel everything don't cause us to use a session that no longer
717 process_pending_messages (session);
718 session->last_activity = GNUNET_TIME_absolute_get ();
719 pid = session->target;
720 /* we'll now call callbacks that may cancel the session; hence
721 * we should not use 'session' after this point */
722 while (NULL != (pos = hd))
724 GNUNET_CONTAINER_DLL_remove (hd, tl, pos);
725 if (pos->transmit_cont != NULL)
726 pos->transmit_cont (pos->transmit_cont_cls, &pid, GNUNET_OK);
729 GNUNET_assert (hd == NULL);
730 GNUNET_assert (tl == NULL);
732 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
733 "tcp", "Transmitting %u bytes\n", ret);
735 GNUNET_STATISTICS_update (plugin->env->stats,
736 gettext_noop ("# bytes currently in TCP buffers"),
737 -(int64_t) ret, GNUNET_NO);
738 GNUNET_STATISTICS_update (plugin->env->stats,
739 gettext_noop ("# bytes transmitted via TCP"),
746 * If we have pending messages, ask the server to
747 * transmit them (schedule the respective tasks, etc.)
749 * @param session for which session should we do this
752 process_pending_messages (struct Session *session)
754 struct PendingMessage *pm;
756 GNUNET_assert (session->client != NULL);
757 if (session->transmit_handle != NULL)
759 if (NULL == (pm = session->pending_messages_head))
762 session->transmit_handle
763 = GNUNET_SERVER_notify_transmit_ready (session->client,
765 GNUNET_TIME_absolute_get_remaining
767 &do_transmit, session);
772 * Functions with this signature are called whenever we need
773 * to close a session due to a disconnect or failure to
774 * establish a connection.
776 * @param session session to close down
779 disconnect_session (struct Session *session)
781 struct Session *prev;
783 struct PendingMessage *pm;
786 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
788 "Disconnecting from `%4s' at %s.\n",
789 GNUNET_i2s (&session->target),
790 (session->connect_addr != NULL) ?
791 tcp_address_to_string (session->plugin,
792 session->connect_addr,
793 session->connect_alen) : "*");
795 /* remove from session list */
797 pos = session->plugin->sessions;
798 while (pos != session)
804 session->plugin->sessions = session->next;
806 prev->next = session->next;
809 if (session->transmit_handle != NULL)
811 GNUNET_CONNECTION_notify_transmit_ready_cancel (session->transmit_handle);
812 session->transmit_handle = NULL;
814 session->plugin->env->session_end (session->plugin->env->cls,
815 &session->target, session);
816 while (NULL != (pm = session->pending_messages_head))
819 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
821 pm->transmit_cont != NULL
822 ? "Could not deliver message to `%4s'.\n"
823 : "Could not deliver message to `%4s', notifying.\n",
824 GNUNET_i2s (&session->target));
826 GNUNET_STATISTICS_update (session->plugin->env->stats,
827 gettext_noop ("# bytes currently in TCP buffers"),
828 -(int64_t) pm->message_size, GNUNET_NO);
829 GNUNET_STATISTICS_update (session->plugin->env->stats,
831 ("# bytes discarded by TCP (disconnect)"),
832 pm->message_size, GNUNET_NO);
833 GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
834 session->pending_messages_tail, pm);
835 if (NULL != pm->transmit_cont)
836 pm->transmit_cont (pm->transmit_cont_cls,
837 &session->target, GNUNET_SYSERR);
840 GNUNET_break (session->client != NULL);
841 if (session->receive_delay_task != GNUNET_SCHEDULER_NO_TASK)
843 GNUNET_SCHEDULER_cancel (session->receive_delay_task);
844 if (session->client != NULL)
845 GNUNET_SERVER_receive_done (session->client, GNUNET_SYSERR);
847 if (session->client != NULL)
849 GNUNET_SERVER_client_drop (session->client);
850 session->client = NULL;
852 GNUNET_STATISTICS_update (session->plugin->env->stats,
853 gettext_noop ("# TCP sessions active"),
855 GNUNET_free_non_null (session->connect_addr);
856 GNUNET_assert (NULL == session->transmit_handle);
857 GNUNET_free (session);
862 * Given two otherwise equivalent sessions, pick the better one.
864 * @param s1 one session (also default)
865 * @param s2 other session
866 * @return "better" session (more active)
868 static struct Session *
869 select_better_session (struct Session *s1, struct Session *s2)
875 if ((s1->expecting_welcome == GNUNET_NO) &&
876 (s2->expecting_welcome == GNUNET_YES))
878 if ((s1->expecting_welcome == GNUNET_YES) &&
879 (s2->expecting_welcome == GNUNET_NO))
881 if (s1->last_activity.abs_value < s2->last_activity.abs_value)
883 if (s1->last_activity.abs_value > s2->last_activity.abs_value)
885 if ((GNUNET_YES == s1->inbound) && (GNUNET_NO == s2->inbound))
887 if ((GNUNET_NO == s1->inbound) && (GNUNET_YES == s2->inbound))
895 * Function that can be used by the transport service to transmit
896 * a message using the plugin. Note that in the case of a
897 * peer disconnecting, the continuation MUST be called
898 * prior to the disconnect notification itself. This function
899 * will be called with this peer's HELLO message to initiate
900 * a fresh connection to another peer.
903 * @param target who should receive this message
904 * @param msg the message to transmit
905 * @param msgbuf_size number of bytes in 'msg'
906 * @param priority how important is the message (most plugins will
907 * ignore message priority and just FIFO)
908 * @param timeout how long to wait at most for the transmission (does not
909 * require plugins to discard the message after the timeout,
910 * just advisory for the desired delay; most plugins will ignore
912 * @param session which session must be used (or NULL for "any")
913 * @param addr the address to use (can be NULL if the plugin
914 * is "on its own" (i.e. re-use existing TCP connection))
915 * @param addrlen length of the address in bytes
916 * @param force_address GNUNET_YES if the plugin MUST use the given address,
917 * GNUNET_NO means the plugin may use any other address and
918 * GNUNET_SYSERR means that only reliable existing
919 * bi-directional connections should be used (regardless
921 * @param cont continuation to call once the message has
922 * been transmitted (or if the transport is ready
923 * for the next transmission call; or if the
924 * peer disconnected...); can be NULL
925 * @param cont_cls closure for cont
926 * @return number of bytes used (on the physical network, with overheads);
927 * -1 on hard errors (i.e. address invalid); 0 is a legal value
928 * and does NOT mean that the message was not transmitted (DV and NAT)
931 tcp_plugin_send (void *cls,
932 const struct GNUNET_PeerIdentity *target,
936 struct GNUNET_TIME_Relative timeout,
937 struct Session *session,
941 GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
943 struct Plugin *plugin = cls;
944 struct Session *cand_session;
945 struct Session *next;
946 struct PendingMessage *pm;
947 struct GNUNET_CONNECTION_Handle *sa;
951 struct sockaddr_in a4;
952 struct sockaddr_in6 a6;
953 const struct IPv4TcpAddress *t4;
954 const struct IPv6TcpAddress *t6;
955 unsigned int is_natd;
957 GNUNET_STATISTICS_update (plugin->env->stats,
958 gettext_noop ("# bytes TCP was asked to transmit"),
959 msgbuf_size, GNUNET_NO);
960 /* FIXME: we could do this cheaper with a hash table
961 * where we could restrict the iteration to entries that match
962 * the target peer... */
967 next = plugin->sessions;
968 while (NULL != (session = next))
970 next = session->next;
971 GNUNET_assert (session->client != NULL);
972 if (0 != memcmp (target,
973 &session->target, sizeof (struct GNUNET_PeerIdentity)))
975 if (((GNUNET_SYSERR == force_address) &&
976 (session->expecting_welcome == GNUNET_NO)) ||
977 (GNUNET_NO == force_address))
979 cand_session = select_better_session (cand_session, session);
982 if (GNUNET_SYSERR == force_address)
984 GNUNET_break (GNUNET_YES == force_address);
990 if ((addrlen != session->connect_alen) && (session->is_nat == GNUNET_NO))
992 if ((0 != memcmp (session->connect_addr,
993 addr, addrlen)) && (session->is_nat == GNUNET_NO))
995 cand_session = select_better_session (cand_session, session);
997 session = cand_session;
999 if ((session == NULL) && (addr == NULL))
1002 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1004 "Asked to transmit to `%4s' without address and I have no existing connection (failing).\n",
1005 GNUNET_i2s (target));
1007 GNUNET_STATISTICS_update (plugin->env->stats,
1009 ("# bytes discarded by TCP (no address and no connection)"),
1010 msgbuf_size, GNUNET_NO);
1013 if (session == NULL)
1015 if (addrlen == sizeof (struct IPv6TcpAddress))
1019 memset (&a6, 0, sizeof (a6));
1020 #if HAVE_SOCKADDR_IN_SIN_LEN
1021 a6.sin6_len = sizeof (a6);
1023 a6.sin6_family = AF_INET6;
1024 a6.sin6_port = t6->t6_port;
1025 if (t6->t6_port == 0)
1026 is_natd = GNUNET_YES;
1027 memcpy (&a6.sin6_addr, &t6->ipv6_addr, sizeof (struct in6_addr));
1031 else if (addrlen == sizeof (struct IPv4TcpAddress))
1035 memset (&a4, 0, sizeof (a4));
1036 #if HAVE_SOCKADDR_IN_SIN_LEN
1037 a4.sin_len = sizeof (a4);
1039 a4.sin_family = AF_INET;
1040 a4.sin_port = t4->t4_port;
1041 if (t4->t4_port == 0)
1042 is_natd = GNUNET_YES;
1043 a4.sin_addr.s_addr = t4->ipv4_addr;
1049 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1050 "tcp", _("Address of unexpected length: %u\n"), addrlen);
1055 if ((is_natd == GNUNET_YES) && (addrlen == sizeof (struct IPv6TcpAddress)))
1056 return -1; /* NAT client only works with IPv4 addresses */
1057 if (0 == plugin->max_connections)
1058 return -1; /* saturated */
1060 if ((is_natd == GNUNET_YES) &&
1061 (NULL != plugin->nat) &&
1063 GNUNET_CONTAINER_multihashmap_contains (plugin->nat_wait_conns,
1064 &target->hashPubKey)))
1067 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1069 _("Found valid IPv4 NAT address (creating session)!\n"));
1071 session = create_session (plugin, target, NULL, GNUNET_YES);
1072 GNUNET_assert (session != NULL);
1074 /* create new message entry */
1075 pm = GNUNET_malloc (sizeof (struct PendingMessage) + msgbuf_size);
1076 /* FIXME: the memset of this malloc can be up to 2% of our total runtime */
1077 pm->msg = (const char *) &pm[1];
1078 memcpy (&pm[1], msg, msgbuf_size);
1079 /* FIXME: this memcpy can be up to 7% of our total run-time
1080 * (for transport service) */
1081 pm->message_size = msgbuf_size;
1082 pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1083 pm->transmit_cont = cont;
1084 pm->transmit_cont_cls = cont_cls;
1086 /* append pm to pending_messages list */
1087 GNUNET_CONTAINER_DLL_insert_after (session->pending_messages_head,
1088 session->pending_messages_tail,
1089 session->pending_messages_tail, pm);
1091 GNUNET_assert (GNUNET_CONTAINER_multihashmap_put (plugin->nat_wait_conns,
1092 &target->hashPubKey,
1094 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
1097 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1099 "Created NAT WAIT connection to `%4s' at `%s'\n",
1100 GNUNET_i2s (target), GNUNET_a2s (sb, sbs));
1102 GNUNET_NAT_run_client (plugin->nat, &a4);
1105 if ((is_natd == GNUNET_YES) &&
1107 GNUNET_CONTAINER_multihashmap_contains (plugin->nat_wait_conns,
1108 &target->hashPubKey)))
1110 /* Only do one NAT punch attempt per peer identity */
1113 sa = GNUNET_CONNECTION_create_from_sockaddr (af, sb, sbs);
1117 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1119 "Failed to create connection to `%4s' at `%s'\n",
1120 GNUNET_i2s (target), GNUNET_a2s (sb, sbs));
1122 GNUNET_STATISTICS_update (plugin->env->stats,
1124 ("# bytes discarded by TCP (failed to connect)"),
1125 msgbuf_size, GNUNET_NO);
1128 GNUNET_assert (0 != plugin->max_connections);
1129 plugin->max_connections--;
1131 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1133 "Asked to transmit to `%4s', creating fresh session using address `%s'.\n",
1134 GNUNET_i2s (target), GNUNET_a2s (sb, sbs));
1136 session = create_session (plugin,
1138 GNUNET_SERVER_connect_socket (plugin->server,
1140 session->connect_addr = GNUNET_malloc (addrlen);
1141 memcpy (session->connect_addr, addr, addrlen);
1142 session->connect_alen = addrlen;
1144 else /* session != NULL */
1146 /* check if session is valid */
1147 struct Session *ses = plugin->sessions;
1149 while ((ses != NULL) && (ses != session))
1157 GNUNET_assert (session != NULL);
1158 GNUNET_assert (session->client != NULL);
1161 GNUNET_SERVER_client_set_timeout (session->client,
1162 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
1163 GNUNET_STATISTICS_update (plugin->env->stats,
1164 gettext_noop ("# bytes currently in TCP buffers"),
1165 msgbuf_size, GNUNET_NO);
1166 /* create new message entry */
1167 pm = GNUNET_malloc (sizeof (struct PendingMessage) + msgbuf_size);
1168 pm->msg = (const char *) &pm[1];
1169 memcpy (&pm[1], msg, msgbuf_size);
1170 pm->message_size = msgbuf_size;
1171 pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1172 pm->transmit_cont = cont;
1173 pm->transmit_cont_cls = cont_cls;
1175 /* append pm to pending_messages list */
1176 GNUNET_CONTAINER_DLL_insert_after (session->pending_messages_head,
1177 session->pending_messages_tail,
1178 session->pending_messages_tail, pm);
1180 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1182 "Asked to transmit %u bytes to `%s', added message to list.\n",
1183 msgbuf_size, GNUNET_i2s (target));
1185 process_pending_messages (session);
1191 * Function that can be called to force a disconnect from the
1192 * specified neighbour. This should also cancel all previously
1193 * scheduled transmissions. Obviously the transmission may have been
1194 * partially completed already, which is OK. The plugin is supposed
1195 * to close the connection (if applicable) and no longer call the
1196 * transmit continuation(s).
1198 * Finally, plugin MUST NOT call the services's receive function to
1199 * notify the service that the connection to the specified target was
1200 * closed after a getting this call.
1202 * @param cls closure
1203 * @param target peer for which the last transmission is
1207 tcp_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
1209 struct Plugin *plugin = cls;
1210 struct Session *session;
1211 struct Session *next;
1212 struct PendingMessage *pm;
1215 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1217 "Asked to cancel session with `%4s'\n", GNUNET_i2s (target));
1219 next = plugin->sessions;
1220 while (NULL != (session = next))
1222 next = session->next;
1223 if (0 != memcmp (target,
1224 &session->target, sizeof (struct GNUNET_PeerIdentity)))
1226 pm = session->pending_messages_head;
1229 pm->transmit_cont = NULL;
1230 pm->transmit_cont_cls = NULL;
1233 GNUNET_STATISTICS_update (session->plugin->env->stats,
1235 ("# transport-service disconnect requests for TCP"),
1237 disconnect_session (session);
1243 * Context for address to string conversion.
1245 struct PrettyPrinterContext
1248 * Function to call with the result.
1250 GNUNET_TRANSPORT_AddressStringCallback asc;
1253 * Clsoure for 'asc'.
1258 * Port to add after the IP address.
1265 * Append our port and forward the result.
1267 * @param cls the 'struct PrettyPrinterContext*'
1268 * @param hostname hostname part of the address
1271 append_port (void *cls, const char *hostname)
1273 struct PrettyPrinterContext *ppc = cls;
1276 if (hostname == NULL)
1278 ppc->asc (ppc->asc_cls, NULL);
1282 GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
1283 ppc->asc (ppc->asc_cls, ret);
1289 * Convert the transports address to a nice, human-readable
1292 * @param cls closure
1293 * @param type name of the transport that generated the address
1294 * @param addr one of the addresses of the host, NULL for the last address
1295 * the specific address format depends on the transport
1296 * @param addrlen length of the address
1297 * @param numeric should (IP) addresses be displayed in numeric form?
1298 * @param timeout after how long should we give up?
1299 * @param asc function to call on each string
1300 * @param asc_cls closure for asc
1303 tcp_plugin_address_pretty_printer (void *cls,
1308 struct GNUNET_TIME_Relative timeout,
1309 GNUNET_TRANSPORT_AddressStringCallback asc,
1312 struct PrettyPrinterContext *ppc;
1315 struct sockaddr_in a4;
1316 struct sockaddr_in6 a6;
1317 const struct IPv4TcpAddress *t4;
1318 const struct IPv6TcpAddress *t6;
1321 if (addrlen == sizeof (struct IPv6TcpAddress))
1324 memset (&a6, 0, sizeof (a6));
1325 a6.sin6_family = AF_INET6;
1326 a6.sin6_port = t6->t6_port;
1327 memcpy (&a6.sin6_addr, &t6->ipv6_addr, sizeof (struct in6_addr));
1328 port = ntohs (t6->t6_port);
1332 else if (addrlen == sizeof (struct IPv4TcpAddress))
1335 memset (&a4, 0, sizeof (a4));
1336 a4.sin_family = AF_INET;
1337 a4.sin_port = t4->t4_port;
1338 a4.sin_addr.s_addr = t4->ipv4_addr;
1339 port = ntohs (t4->t4_port);
1345 /* invalid address */
1346 GNUNET_break_op (0);
1347 asc (asc_cls, NULL);
1350 ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
1352 ppc->asc_cls = asc_cls;
1354 GNUNET_RESOLVER_hostname_get (sb, sbs, !numeric, timeout, &append_port, ppc);
1359 * Check if the given port is plausible (must be either our listen
1360 * port or our advertised port), or any port if we are behind NAT
1361 * and do not have a port open. If it is neither, we return
1364 * @param plugin global variables
1365 * @param in_port port number to check
1366 * @return GNUNET_OK if port is either open_port or adv_port
1369 check_port (struct Plugin *plugin, uint16_t in_port)
1371 if ((in_port == plugin->adv_port) || (in_port == plugin->open_port))
1373 return GNUNET_SYSERR;
1378 * Function that will be called to check if a binary address for this
1379 * plugin is well-formed and corresponds to an address for THIS peer
1380 * (as per our configuration). Naturally, if absolutely necessary,
1381 * plugins can be a bit conservative in their answer, but in general
1382 * plugins should make sure that the address does not redirect
1383 * traffic to a 3rd party that might try to man-in-the-middle our
1386 * @param cls closure, our 'struct Plugin*'
1387 * @param addr pointer to the address
1388 * @param addrlen length of addr
1389 * @return GNUNET_OK if this is a plausible address for this peer
1390 * and transport, GNUNET_SYSERR if not
1393 tcp_plugin_check_address (void *cls, const void *addr, size_t addrlen)
1395 struct Plugin *plugin = cls;
1396 struct IPv4TcpAddress *v4;
1397 struct IPv6TcpAddress *v6;
1399 if ((addrlen != sizeof (struct IPv4TcpAddress)) &&
1400 (addrlen != sizeof (struct IPv6TcpAddress)))
1402 GNUNET_break_op (0);
1403 return GNUNET_SYSERR;
1405 if (addrlen == sizeof (struct IPv4TcpAddress))
1407 v4 = (struct IPv4TcpAddress *) addr;
1408 if (GNUNET_OK != check_port (plugin, ntohs (v4->t4_port)))
1409 return GNUNET_SYSERR;
1411 GNUNET_NAT_test_address (plugin->nat,
1412 &v4->ipv4_addr, sizeof (struct in_addr)))
1413 return GNUNET_SYSERR;
1417 v6 = (struct IPv6TcpAddress *) addr;
1418 if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
1420 GNUNET_break_op (0);
1421 return GNUNET_SYSERR;
1423 if (GNUNET_OK != check_port (plugin, ntohs (v6->t6_port)))
1424 return GNUNET_SYSERR;
1426 GNUNET_NAT_test_address (plugin->nat,
1427 &v6->ipv6_addr, sizeof (struct in6_addr)))
1428 return GNUNET_SYSERR;
1435 * We've received a nat probe from this peer via TCP. Finish
1436 * creating the client session and resume sending of queued
1439 * @param cls closure
1440 * @param client identification of the client
1441 * @param message the actual message
1444 handle_tcp_nat_probe (void *cls,
1445 struct GNUNET_SERVER_Client *client,
1446 const struct GNUNET_MessageHeader *message)
1448 struct Plugin *plugin = cls;
1449 struct Session *session;
1450 const struct TCP_NAT_ProbeMessage *tcp_nat_probe;
1453 struct IPv4TcpAddress *t4;
1454 struct IPv6TcpAddress *t6;
1455 const struct sockaddr_in *s4;
1456 const struct sockaddr_in6 *s6;
1459 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp", "received NAT probe\n");
1461 /* We have received a TCP NAT probe, meaning we (hopefully) initiated
1462 * a connection to this peer by running gnunet-nat-client. This peer
1463 * received the punch message and now wants us to use the new connection
1464 * as the default for that peer. Do so and then send a WELCOME message
1465 * so we can really be connected!
1467 if (ntohs (message->size) != sizeof (struct TCP_NAT_ProbeMessage))
1469 GNUNET_break_op (0);
1470 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1474 tcp_nat_probe = (const struct TCP_NAT_ProbeMessage *) message;
1475 if (0 == memcmp (&tcp_nat_probe->clientIdentity,
1476 plugin->env->my_identity,
1477 sizeof (struct GNUNET_PeerIdentity)))
1479 /* refuse connections from ourselves */
1480 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1484 session = GNUNET_CONTAINER_multihashmap_get (plugin->nat_wait_conns,
1486 clientIdentity.hashPubKey);
1487 if (session == NULL)
1490 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1491 "tcp", "Did NOT find session for NAT probe!\n");
1493 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1497 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1498 "tcp", "Found session for NAT probe!\n");
1500 GNUNET_assert (GNUNET_CONTAINER_multihashmap_remove (plugin->nat_wait_conns,
1501 &tcp_nat_probe->clientIdentity.hashPubKey,
1502 session) == GNUNET_YES);
1503 if (GNUNET_OK != GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
1506 GNUNET_free (session);
1507 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1511 GNUNET_SERVER_client_keep (client);
1512 session->client = client;
1513 session->last_activity = GNUNET_TIME_absolute_get ();
1514 session->inbound = GNUNET_NO;
1517 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1519 "Found address `%s' for incoming connection\n",
1520 GNUNET_a2s (vaddr, alen));
1522 switch (((const struct sockaddr *) vaddr)->sa_family)
1526 t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress));
1527 t4->t4_port = s4->sin_port;
1528 t4->ipv4_addr = s4->sin_addr.s_addr;
1529 session->connect_addr = t4;
1530 session->connect_alen = sizeof (struct IPv4TcpAddress);
1534 t6 = GNUNET_malloc (sizeof (struct IPv6TcpAddress));
1535 t6->t6_port = s6->sin6_port;
1536 memcpy (&t6->ipv6_addr, &s6->sin6_addr, sizeof (struct in6_addr));
1537 session->connect_addr = t6;
1538 session->connect_alen = sizeof (struct IPv6TcpAddress);
1541 GNUNET_break_op (0);
1543 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1544 "tcp", "Bad address for incoming connection!\n");
1546 GNUNET_free (vaddr);
1547 GNUNET_SERVER_client_drop (client);
1548 GNUNET_free (session);
1549 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1552 GNUNET_free (vaddr);
1554 session->next = plugin->sessions;
1555 plugin->sessions = session;
1556 GNUNET_STATISTICS_update (plugin->env->stats,
1557 gettext_noop ("# TCP sessions active"),
1559 process_pending_messages (session);
1560 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1565 * We've received a welcome from this peer via TCP. Possibly create a
1566 * fresh client record and send back our welcome.
1568 * @param cls closure
1569 * @param client identification of the client
1570 * @param message the actual message
1573 handle_tcp_welcome (void *cls,
1574 struct GNUNET_SERVER_Client *client,
1575 const struct GNUNET_MessageHeader *message)
1577 struct Plugin *plugin = cls;
1578 const struct WelcomeMessage *wm = (const struct WelcomeMessage *) message;
1579 struct Session *session;
1582 struct IPv4TcpAddress *t4;
1583 struct IPv6TcpAddress *t6;
1584 const struct sockaddr_in *s4;
1585 const struct sockaddr_in6 *s6;
1587 if (0 == memcmp (&wm->clientIdentity,
1588 plugin->env->my_identity,
1589 sizeof (struct GNUNET_PeerIdentity)))
1591 /* refuse connections from ourselves */
1592 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1596 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1598 "Received %s message from `%4s'.\n",
1599 "WELCOME", GNUNET_i2s (&wm->clientIdentity));
1601 GNUNET_STATISTICS_update (plugin->env->stats,
1602 gettext_noop ("# TCP WELCOME messages received"),
1604 session = find_session_by_client (plugin, client);
1606 if (session == NULL)
1609 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1611 "Received %s message from a `%4s', creating new session\n",
1612 "WELCOME", GNUNET_i2s (&wm->clientIdentity));
1614 GNUNET_SERVER_client_keep (client);
1615 session = create_session (plugin, &wm->clientIdentity, client, GNUNET_NO);
1616 session->inbound = GNUNET_YES;
1617 if (GNUNET_OK == GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
1620 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1622 "Found address `%s' for incoming connection\n",
1623 GNUNET_a2s (vaddr, alen));
1625 if (alen == sizeof (struct sockaddr_in))
1628 t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress));
1629 t4->t4_port = s4->sin_port;
1630 t4->ipv4_addr = s4->sin_addr.s_addr;
1631 session->connect_addr = t4;
1632 session->connect_alen = sizeof (struct IPv4TcpAddress);
1634 else if (alen == sizeof (struct sockaddr_in6))
1637 t6 = GNUNET_malloc (sizeof (struct IPv6TcpAddress));
1638 t6->t6_port = s6->sin6_port;
1639 memcpy (&t6->ipv6_addr, &s6->sin6_addr, sizeof (struct in6_addr));
1640 session->connect_addr = t6;
1641 session->connect_alen = sizeof (struct IPv6TcpAddress);
1644 GNUNET_free (vaddr);
1649 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1651 "Did not obtain TCP socket address for incoming connection\n");
1654 process_pending_messages (session);
1659 if (GNUNET_OK == GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
1661 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1663 "Found address `%s' (already have session)\n",
1664 GNUNET_a2s (vaddr, alen));
1665 GNUNET_free (vaddr);
1670 if (session->expecting_welcome != GNUNET_YES)
1672 GNUNET_break_op (0);
1673 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1676 session->last_activity = GNUNET_TIME_absolute_get ();
1677 session->expecting_welcome = GNUNET_NO;
1678 GNUNET_SERVER_client_set_timeout (client,
1679 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
1680 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1685 * Task to signal the server that we can continue
1686 * receiving from the TCP client now.
1688 * @param cls the 'struct Session*'
1689 * @param tc task context (unused)
1692 delayed_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1694 struct Session *session = cls;
1695 struct GNUNET_TIME_Relative delay;
1697 session->receive_delay_task = GNUNET_SCHEDULER_NO_TASK;
1698 delay = session->plugin->env->receive (session->plugin->env->cls,
1700 NULL, NULL, 0, session, NULL, 0);
1701 if (delay.rel_value == 0)
1702 GNUNET_SERVER_receive_done (session->client, GNUNET_OK);
1704 session->receive_delay_task =
1705 GNUNET_SCHEDULER_add_delayed (delay, &delayed_done, session);
1710 * We've received data for this peer via TCP. Unbox,
1711 * compute latency and forward.
1713 * @param cls closure
1714 * @param client identification of the client
1715 * @param message the actual message
1718 handle_tcp_data (void *cls,
1719 struct GNUNET_SERVER_Client *client,
1720 const struct GNUNET_MessageHeader *message)
1722 struct Plugin *plugin = cls;
1723 struct Session *session;
1724 struct GNUNET_TIME_Relative delay;
1727 type = ntohs (message->type);
1728 if ((GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME == type) ||
1729 (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE == type))
1731 /* We don't want to propagate WELCOME and NAT Probe messages up! */
1732 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1735 session = find_session_by_client (plugin, client);
1736 if ((NULL == session) || (GNUNET_YES == session->expecting_welcome))
1738 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1741 session->last_activity = GNUNET_TIME_absolute_get ();
1743 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1745 "Passing %u bytes of type %u from `%4s' to transport service.\n",
1746 (unsigned int) ntohs (message->size),
1747 (unsigned int) ntohs (message->type),
1748 GNUNET_i2s (&session->target));
1750 GNUNET_STATISTICS_update (plugin->env->stats,
1751 gettext_noop ("# bytes received via TCP"),
1752 ntohs (message->size), GNUNET_NO);
1753 struct GNUNET_TRANSPORT_ATS_Information distance[2];
1755 distance[0].type = htonl (GNUNET_TRANSPORT_ATS_QUALITY_NET_DISTANCE);
1756 distance[0].value = htonl (1);
1757 distance[1].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
1758 distance[1].value = htonl (0);
1759 delay = plugin->env->receive (plugin->env->cls, &session->target, message,
1760 (const struct GNUNET_TRANSPORT_ATS_Information
1761 *) &distance, 2, session,
1764 inbound) ? NULL : session->connect_addr,
1766 session->inbound) ? 0 : session->connect_alen);
1767 if (delay.rel_value == 0)
1769 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1774 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1776 "Throttling receiving from `%s' for %llu ms\n",
1777 GNUNET_i2s (&session->target),
1778 (unsigned long long) delay.rel_value);
1780 GNUNET_SERVER_disable_receive_done_warning (client);
1781 session->receive_delay_task =
1782 GNUNET_SCHEDULER_add_delayed (delay, &delayed_done, session);
1788 * Functions with this signature are called whenever a peer
1789 * is disconnected on the network level.
1791 * @param cls closure
1792 * @param client identification of the client
1795 disconnect_notify (void *cls, struct GNUNET_SERVER_Client *client)
1797 struct Plugin *plugin = cls;
1798 struct Session *session;
1802 plugin->max_connections++;
1803 session = find_session_by_client (plugin, client);
1804 if (session == NULL)
1805 return; /* unknown, nothing to do */
1807 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1809 "Destroying session of `%4s' with %s due to network-level disconnect.\n",
1810 GNUNET_i2s (&session->target),
1811 (session->connect_addr != NULL) ?
1812 tcp_address_to_string (session->plugin,
1813 session->connect_addr,
1814 session->connect_alen) : "*");
1816 GNUNET_STATISTICS_update (session->plugin->env->stats,
1818 ("# network-level TCP disconnect events"), 1,
1820 disconnect_session (session);
1825 * We can now send a probe message, copy into buffer to really send.
1827 * @param cls closure, a struct TCPProbeContext
1828 * @param size max size to copy
1829 * @param buf buffer to copy message to
1830 * @return number of bytes copied into buf
1833 notify_send_probe (void *cls, size_t size, void *buf)
1835 struct TCPProbeContext *tcp_probe_ctx = cls;
1836 struct Plugin *plugin = tcp_probe_ctx->plugin;
1839 tcp_probe_ctx->transmit_handle = NULL;
1840 GNUNET_CONTAINER_DLL_remove (plugin->probe_head,
1841 plugin->probe_tail, tcp_probe_ctx);
1844 GNUNET_CONNECTION_destroy (tcp_probe_ctx->sock, GNUNET_NO);
1845 GNUNET_free (tcp_probe_ctx);
1848 GNUNET_assert (size >= sizeof (tcp_probe_ctx->message));
1849 memcpy (buf, &tcp_probe_ctx->message, sizeof (tcp_probe_ctx->message));
1850 GNUNET_SERVER_connect_socket (tcp_probe_ctx->plugin->server,
1851 tcp_probe_ctx->sock);
1852 ret = sizeof (tcp_probe_ctx->message);
1853 GNUNET_free (tcp_probe_ctx);
1859 * Function called by the NAT subsystem suggesting another peer wants
1860 * to connect to us via connection reversal. Try to connect back to the
1863 * @param cls closure
1864 * @param addr address to try
1865 * @param addrlen number of bytes in addr
1868 try_connection_reversal (void *cls,
1869 const struct sockaddr *addr, socklen_t addrlen)
1871 struct Plugin *plugin = cls;
1872 struct GNUNET_CONNECTION_Handle *sock;
1873 struct TCPProbeContext *tcp_probe_ctx;
1876 * We have received an ICMP response, ostensibly from a peer
1877 * that wants to connect to us! Send a message to establish a connection.
1879 sock = GNUNET_CONNECTION_create_from_sockaddr (AF_INET, addr, addrlen);
1882 /* failed for some odd reason (out of sockets?); ignore attempt */
1886 /* FIXME: do we need to track these probe context objects so that
1887 * we can clean them up on plugin unload? */
1888 tcp_probe_ctx = GNUNET_malloc (sizeof (struct TCPProbeContext));
1889 tcp_probe_ctx->message.header.size
1890 = htons (sizeof (struct TCP_NAT_ProbeMessage));
1891 tcp_probe_ctx->message.header.type
1892 = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE);
1893 memcpy (&tcp_probe_ctx->message.clientIdentity,
1894 plugin->env->my_identity, sizeof (struct GNUNET_PeerIdentity));
1895 tcp_probe_ctx->plugin = plugin;
1896 tcp_probe_ctx->sock = sock;
1897 GNUNET_CONTAINER_DLL_insert (plugin->probe_head,
1898 plugin->probe_tail, tcp_probe_ctx);
1899 tcp_probe_ctx->transmit_handle
1900 = GNUNET_CONNECTION_notify_transmit_ready (sock,
1901 ntohs (tcp_probe_ctx->
1902 message.header.size),
1903 GNUNET_TIME_UNIT_FOREVER_REL,
1911 * Entry point for the plugin.
1913 * @param cls closure, the 'struct GNUNET_TRANSPORT_PluginEnvironment*'
1914 * @return the 'struct GNUNET_TRANSPORT_PluginFunctions*' or NULL on error
1917 libgnunet_plugin_transport_tcp_init (void *cls)
1919 static const struct GNUNET_SERVER_MessageHandler my_handlers[] = {
1920 {&handle_tcp_welcome, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME,
1921 sizeof (struct WelcomeMessage)},
1922 {&handle_tcp_nat_probe, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE,
1923 sizeof (struct TCP_NAT_ProbeMessage)},
1924 {&handle_tcp_data, NULL, GNUNET_MESSAGE_TYPE_ALL, 0},
1927 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1928 struct GNUNET_TRANSPORT_PluginFunctions *api;
1929 struct Plugin *plugin;
1930 struct GNUNET_SERVICE_Context *service;
1931 unsigned long long aport;
1932 unsigned long long bport;
1933 unsigned long long max_connections;
1935 struct GNUNET_TIME_Relative idle_timeout;
1937 struct sockaddr **addrs;
1938 socklen_t *addrlens;
1941 GNUNET_CONFIGURATION_get_value_number (env->cfg,
1945 max_connections = 128;
1949 GNUNET_CONFIGURATION_get_value_number (env->cfg,
1955 GNUNET_CONFIGURATION_get_value_number (env->cfg,
1958 &aport)) && (aport > 65535)))
1960 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1963 ("Require valid port number for service `%s' in configuration!\n"),
1973 service = GNUNET_SERVICE_start ("transport-tcp", env->cfg);
1974 if (service == NULL)
1976 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
1977 "tcp", _("Failed to start service.\n"));
1986 plugin = GNUNET_malloc (sizeof (struct Plugin));
1987 plugin->max_connections = max_connections;
1988 plugin->open_port = bport;
1989 plugin->adv_port = aport;
1991 plugin->lsock = NULL;
1992 if ((service != NULL) &&
1994 (ret = GNUNET_SERVICE_get_server_addresses ("transport-tcp",
1996 &addrs, &addrlens))))
1998 plugin->nat = GNUNET_NAT_register (env->cfg,
2002 (const struct sockaddr **) addrs,
2004 &tcp_nat_port_map_callback,
2005 &try_connection_reversal, plugin);
2009 GNUNET_assert (addrs[ret] != NULL);
2010 GNUNET_free (addrs[ret]);
2012 GNUNET_free_non_null (addrs);
2013 GNUNET_free_non_null (addrlens);
2017 plugin->nat = GNUNET_NAT_register (env->cfg,
2021 NULL, &try_connection_reversal, plugin);
2023 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
2025 api->send = &tcp_plugin_send;
2026 api->disconnect = &tcp_plugin_disconnect;
2027 api->address_pretty_printer = &tcp_plugin_address_pretty_printer;
2028 api->check_address = &tcp_plugin_check_address;
2029 api->address_to_string = &tcp_address_to_string;
2030 plugin->service = service;
2031 if (service != NULL)
2033 plugin->server = GNUNET_SERVICE_get_server (service);
2038 GNUNET_CONFIGURATION_get_value_time (env->cfg,
2040 "TIMEOUT", &idle_timeout))
2042 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2044 _("Failed to find option %s in section %s!\n"),
2045 "TIMEOUT", "transport-tcp");
2046 if (plugin->nat != NULL)
2047 GNUNET_NAT_unregister (plugin->nat);
2048 GNUNET_free (plugin);
2053 GNUNET_SERVER_create_with_sockets (&plugin_tcp_access_check, plugin,
2054 NULL, idle_timeout, GNUNET_YES);
2056 plugin->handlers = GNUNET_malloc (sizeof (my_handlers));
2057 memcpy (plugin->handlers, my_handlers, sizeof (my_handlers));
2059 i < sizeof (my_handlers) / sizeof (struct GNUNET_SERVER_MessageHandler);
2061 plugin->handlers[i].callback_cls = plugin;
2062 GNUNET_SERVER_add_handlers (plugin->server, plugin->handlers);
2063 GNUNET_SERVER_disconnect_notify (plugin->server, &disconnect_notify, plugin);
2064 plugin->nat_wait_conns = GNUNET_CONTAINER_multihashmap_create (16);
2066 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
2067 "tcp", _("TCP transport listening on port %llu\n"), bport);
2069 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
2072 ("TCP transport not listening on any port (client only)\n"));
2074 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
2077 ("TCP transport advertises itself as being on port %llu\n"),
2084 * Exit point from the plugin.
2087 libgnunet_plugin_transport_tcp_done (void *cls)
2089 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
2090 struct Plugin *plugin = api->cls;
2091 struct Session *session;
2092 struct TCPProbeContext *tcp_probe;
2094 while (NULL != (session = plugin->sessions))
2095 disconnect_session (session);
2096 if (plugin->service != NULL)
2097 GNUNET_SERVICE_stop (plugin->service);
2099 GNUNET_SERVER_destroy (plugin->server);
2100 GNUNET_free (plugin->handlers);
2101 if (plugin->nat != NULL)
2102 GNUNET_NAT_unregister (plugin->nat);
2103 while (NULL != (tcp_probe = plugin->probe_head))
2105 GNUNET_CONTAINER_DLL_remove (plugin->probe_head,
2106 plugin->probe_tail, tcp_probe);
2107 GNUNET_CONNECTION_destroy (tcp_probe->sock, GNUNET_NO);
2108 GNUNET_free (tcp_probe);
2110 GNUNET_CONTAINER_multihashmap_destroy (plugin->nat_wait_conns);
2111 GNUNET_free (plugin);
2116 /* end of plugin_transport_tcp.c */