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 LOG(kind,...) GNUNET_log_from (kind, "transport-tcp",__VA_ARGS__)
45 * How long until we give up on establishing an NAT connection?
48 #define NAT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
51 GNUNET_NETWORK_STRUCT_BEGIN
54 * Initial handshake message for a session.
59 * Type is GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME.
61 struct GNUNET_MessageHeader header;
64 * Identity of the node connecting (TCP client)
66 struct GNUNET_PeerIdentity clientIdentity;
72 * Basically a WELCOME message, but with the purpose
73 * of giving the waiting peer a client handle to use
75 struct TCP_NAT_ProbeMessage
78 * Type is GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE.
80 struct GNUNET_MessageHeader header;
83 * Identity of the sender of the message.
85 struct GNUNET_PeerIdentity clientIdentity;
88 GNUNET_NETWORK_STRUCT_END
91 * Context for sending a NAT probe via TCP.
93 struct TCPProbeContext
97 * Active probes are kept in a DLL.
99 struct TCPProbeContext *next;
102 * Active probes are kept in a DLL.
104 struct TCPProbeContext *prev;
109 struct GNUNET_CONNECTION_Handle *sock;
112 * Message to be sent.
114 struct TCP_NAT_ProbeMessage message;
117 * Handle to the transmission.
119 struct GNUNET_CONNECTION_TransmitHandle *transmit_handle;
122 * Transport plugin handle.
124 struct Plugin *plugin;
128 GNUNET_NETWORK_STRUCT_BEGIN
131 * Network format for IPv4 addresses.
133 struct IPv4TcpAddress
136 * IPv4 address, in network byte order.
138 uint32_t ipv4_addr GNUNET_PACKED;
141 * Port number, in network byte order.
143 uint16_t t4_port GNUNET_PACKED;
149 * Network format for IPv6 addresses.
151 struct IPv6TcpAddress
156 struct in6_addr ipv6_addr GNUNET_PACKED;
159 * Port number, in network byte order.
161 uint16_t t6_port GNUNET_PACKED;
164 GNUNET_NETWORK_STRUCT_END
167 * Encapsulation of all of the state of the plugin.
173 * Information kept for each message that is yet to
176 struct PendingMessage
180 * This is a doubly-linked list.
182 struct PendingMessage *next;
185 * This is a doubly-linked list.
187 struct PendingMessage *prev;
190 * The pending message
195 * Continuation function to call once the message
196 * has been sent. Can be NULL if there is no
197 * continuation to call.
199 GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
202 * Closure for transmit_cont.
204 void *transmit_cont_cls;
207 * Timeout value for the pending message.
209 struct GNUNET_TIME_Absolute timeout;
212 * So that the gnunet-service-transport can group messages together,
213 * these pending messages need to accept a message buffer and size
214 * instead of just a GNUNET_MessageHeader.
222 * Session handle for TCP connections.
230 struct SessionHeader header;
233 * Stored in a linked list.
235 struct Session *next;
238 * Pointer to the global plugin struct.
240 struct Plugin *plugin;
243 * The client (used to identify this connection)
245 struct GNUNET_SERVER_Client *client;
248 * Task cleaning up a NAT client connection establishment attempt;
250 GNUNET_SCHEDULER_TaskIdentifier nat_connection_timeout;
253 * Messages currently pending for transmission
254 * to this peer, if any.
256 struct PendingMessage *pending_messages_head;
259 * Messages currently pending for transmission
260 * to this peer, if any.
262 struct PendingMessage *pending_messages_tail;
265 * Handle for pending transmission request.
267 struct GNUNET_SERVER_TransmitHandle *transmit_handle;
270 * To whom are we talking to (set to our identity
271 * if we are still waiting for the welcome message)
273 struct GNUNET_PeerIdentity target;
276 * ID of task used to delay receiving more to throttle sender.
278 GNUNET_SCHEDULER_TaskIdentifier receive_delay_task;
281 * Address of the other peer (either based on our 'connect'
282 * call or on our 'accept' call).
284 * struct IPv4TcpAddress or struct IPv6TcpAddress
290 * Length of connect_addr.
295 * Last activity on this connection. Used to select preferred
298 struct GNUNET_TIME_Absolute last_activity;
301 * Are we still expecting the welcome message? (GNUNET_YES/GNUNET_NO)
303 int expecting_welcome;
306 * Was this a connection that was inbound (we accepted)? (GNUNET_YES/GNUNET_NO)
311 * Was this session created using NAT traversal?
316 * ATS network type in NBO
318 uint32_t ats_address_network_type;
323 * Encapsulation of all of the state of the plugin.
330 struct GNUNET_TRANSPORT_PluginEnvironment *env;
335 struct GNUNET_CONNECTION_Handle *lsock;
338 * Our handle to the NAT module.
340 struct GNUNET_NAT_Handle *nat;
342 struct GNUNET_CONTAINER_MultiHashMap * sessionmap;
345 * Handle to the network service.
347 struct GNUNET_SERVICE_Context *service;
350 * Handle to the server for this service.
352 struct GNUNET_SERVER_Handle *server;
355 * Copy of the handler array where the closures are
356 * set to this struct's instance.
358 struct GNUNET_SERVER_MessageHandler *handlers;
361 * Map of peers we have tried to contact behind a NAT
363 struct GNUNET_CONTAINER_MultiHashMap *nat_wait_conns;
366 * List of active TCP probes.
368 struct TCPProbeContext *probe_head;
371 * List of active TCP probes.
373 struct TCPProbeContext *probe_tail;
376 * Handle for (DYN)DNS lookup of our external IP.
378 struct GNUNET_RESOLVER_RequestHandle *ext_dns;
381 * How many more TCP sessions are we allowed to open right now?
383 unsigned long long max_connections;
386 * ID of task used to update our addresses when one expires.
388 GNUNET_SCHEDULER_TaskIdentifier address_update_task;
391 * Port that we are actually listening on.
396 * Port that the user said we would have visible to the
405 tcp_address_to_string (void *cls, const void *addr, size_t addrlen);
407 static unsigned int sessions;
409 static void inc_sessions (struct Plugin *plugin, struct Session *session, int line)
412 unsigned int size = GNUNET_CONTAINER_multihashmap_size(plugin->sessionmap);
413 if (sessions != size)
414 LOG (GNUNET_ERROR_TYPE_DEBUG, "Inconsistent sessions %u <-> session map size: %u\n",
416 LOG (GNUNET_ERROR_TYPE_DEBUG, "%4i Session increased to %u (session map size: %u): `%s' `%s'\n",
420 GNUNET_i2s (&session->target),
421 tcp_address_to_string (NULL, session->addr, session->addrlen));
424 static void dec_sessions (struct Plugin *plugin, struct Session *session, int line)
426 GNUNET_assert (sessions > 0);
427 unsigned int size = GNUNET_CONTAINER_multihashmap_size(plugin->sessionmap);
429 if (sessions != size)
430 LOG (GNUNET_ERROR_TYPE_DEBUG, "Inconsistent sessions %u <-> session map size: %u\n",
432 LOG (GNUNET_ERROR_TYPE_DEBUG, "%4i Session decreased to %u (session map size: %u): `%s' `%s'\n",
436 GNUNET_i2s (&session->target),
437 tcp_address_to_string (NULL, session->addr, session->addrlen));
443 * Function to check if an inbound connection is acceptable.
444 * Mostly used to limit the total number of open connections
447 * @param cls the 'struct Plugin'
448 * @param ucred credentials, if available, otherwise NULL
449 * @param addr address
450 * @param addrlen length of address
451 * @return GNUNET_YES to allow, GNUNET_NO to deny, GNUNET_SYSERR
452 * for unknown address family (will be denied).
455 plugin_tcp_access_check (void *cls,
456 const struct GNUNET_CONNECTION_Credentials *ucred,
457 const struct sockaddr *addr, socklen_t addrlen)
459 struct Plugin *plugin = cls;
461 LOG (GNUNET_ERROR_TYPE_DEBUG,
462 "Accepting new incoming TCP connection\n");
463 if (0 == plugin->max_connections)
465 plugin->max_connections--;
471 * Our external IP address/port mapping has changed.
473 * @param cls closure, the 'struct LocalAddrList'
474 * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean
475 * the previous (now invalid) one
476 * @param addr either the previous or the new public IP address
477 * @param addrlen actual lenght of the address
480 tcp_nat_port_map_callback (void *cls, int add_remove,
481 const struct sockaddr *addr, socklen_t addrlen)
483 struct Plugin *plugin = cls;
484 struct IPv4TcpAddress t4;
485 struct IPv6TcpAddress t6;
489 LOG (GNUNET_ERROR_TYPE_DEBUG,
490 "NPMC called with %d for address `%s'\n", add_remove,
491 GNUNET_a2s (addr, addrlen));
492 /* convert 'addr' to our internal format */
493 switch (addr->sa_family)
496 GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
497 t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
498 t4.t4_port = ((struct sockaddr_in *) addr)->sin_port;
503 GNUNET_assert (addrlen == sizeof (struct sockaddr_in6));
504 memcpy (&t6.ipv6_addr, &((struct sockaddr_in6 *) addr)->sin6_addr,
505 sizeof (struct in6_addr));
506 t6.t6_port = ((struct sockaddr_in6 *) addr)->sin6_port;
514 /* modify our published address list */
515 plugin->env->notify_address (plugin->env->cls, add_remove, arg, args);
520 * Function called for a quick conversion of the binary address to
521 * a numeric address. Note that the caller must not free the
522 * address and that the next call to this function is allowed
523 * to override the address again.
525 * @param cls closure ('struct Plugin*')
526 * @param addr binary address
527 * @param addrlen length of the address
528 * @return string representing the same address
531 tcp_address_to_string (void *cls, const void *addr, size_t addrlen)
533 static char rbuf[INET6_ADDRSTRLEN + 12];
534 char buf[INET6_ADDRSTRLEN];
538 const struct IPv4TcpAddress *t4;
539 const struct IPv6TcpAddress *t6;
543 if (addrlen == sizeof (struct IPv6TcpAddress))
547 port = ntohs (t6->t6_port);
548 memcpy (&a6, &t6->ipv6_addr, sizeof (a6));
551 else if (addrlen == sizeof (struct IPv4TcpAddress))
555 port = ntohs (t4->t4_port);
556 memcpy (&a4, &t4->ipv4_addr, sizeof (a4));
561 LOG (GNUNET_ERROR_TYPE_ERROR,
562 _("Unexpected address length: %u bytes\n"),
563 (unsigned int) addrlen);
567 if (NULL == inet_ntop (af, sb, buf, INET6_ADDRSTRLEN))
569 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
572 GNUNET_snprintf (rbuf, sizeof (rbuf), (af == AF_INET6) ? "[%s]:%u" : "%s:%u",
579 * Function called to convert a string address to
582 * @param cls closure ('struct Plugin*')
583 * @param addr string address
584 * @param addrlen length of the address
585 * @param buf location to store the buffer
586 * @param added location to store the number of bytes in the buffer.
587 * If the function returns GNUNET_SYSERR, its contents are undefined.
588 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
591 tcp_string_to_address (void *cls, const char *addr, uint16_t addrlen,
592 void **buf, size_t *added)
594 struct sockaddr_storage socket_address;
596 if ((NULL == addr) || (addrlen == 0))
599 return GNUNET_SYSERR;
602 if ('\0' != addr[addrlen - 1])
605 return GNUNET_SYSERR;
608 if (strlen (addr) != addrlen - 1)
611 return GNUNET_SYSERR;
614 int ret = GNUNET_STRINGS_to_address_ip (addr, strlen (addr),
617 if (ret != GNUNET_OK)
620 return GNUNET_SYSERR;
623 if (socket_address.ss_family == AF_INET)
625 struct IPv4TcpAddress *t4;
626 struct sockaddr_in *in4 = (struct sockaddr_in *) &socket_address;
627 t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress));
628 t4->ipv4_addr = in4->sin_addr.s_addr;
629 t4->t4_port = in4->sin_port;
631 *added = sizeof (struct IPv4TcpAddress);
634 else if (socket_address.ss_family == AF_INET6)
636 struct IPv6TcpAddress *t6;
637 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *) &socket_address;
638 t6 = GNUNET_malloc (sizeof (struct IPv6TcpAddress));
639 t6->ipv6_addr = in6->sin6_addr;
640 t6->t6_port = in6->sin6_port;
642 *added = sizeof (struct IPv6TcpAddress);
645 return GNUNET_SYSERR;
649 struct SessionClientCtx
651 const struct GNUNET_SERVER_Client *client;
657 session_lookup_by_client_it (void *cls,
658 const GNUNET_HashCode * key,
661 struct SessionClientCtx *sc_ctx = cls;
662 struct Session *s = value;
664 if (s->client == sc_ctx->client)
674 * Find the session handle for the given client.
676 * @param plugin the plugin
677 * @param client which client to find the session handle for
678 * @return NULL if no matching session exists
680 static struct Session *
681 lookup_session_by_client (struct Plugin *plugin,
682 const struct GNUNET_SERVER_Client *client)
684 struct SessionClientCtx sc_ctx;
686 sc_ctx.client = client;
688 GNUNET_CONTAINER_multihashmap_iterate (plugin->sessionmap, &session_lookup_by_client_it, &sc_ctx);
694 * Create a new session. Also queues a welcome message.
696 * @param plugin the plugin
697 * @param target peer to connect to
698 * @param client client to use
699 * @param is_nat this a NAT session, we should wait for a client to
700 * connect to us from an address, then assign that to
702 * @return new session object
704 static struct Session *
705 create_session (struct Plugin *plugin, const struct GNUNET_PeerIdentity *target,
706 struct GNUNET_SERVER_Client *client, int is_nat)
709 struct PendingMessage *pm;
710 struct WelcomeMessage welcome;
712 if (is_nat != GNUNET_YES)
713 GNUNET_assert (client != NULL);
715 GNUNET_assert (client == NULL);
717 LOG (GNUNET_ERROR_TYPE_DEBUG,
718 "Creating new session for peer `%4s'\n",
719 GNUNET_i2s (target));
720 ret = GNUNET_malloc (sizeof (struct Session));
721 ret->last_activity = GNUNET_TIME_absolute_get ();
722 ret->plugin = plugin;
723 ret->is_nat = is_nat;
724 ret->client = client;
725 ret->target = *target;
726 ret->expecting_welcome = GNUNET_YES;
727 ret->ats_address_network_type = htonl (GNUNET_ATS_NET_UNSPECIFIED);
728 pm = GNUNET_malloc (sizeof (struct PendingMessage) +
729 sizeof (struct WelcomeMessage));
730 pm->msg = (const char *) &pm[1];
731 pm->message_size = sizeof (struct WelcomeMessage);
732 welcome.header.size = htons (sizeof (struct WelcomeMessage));
733 welcome.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME);
734 welcome.clientIdentity = *plugin->env->my_identity;
735 memcpy (&pm[1], &welcome, sizeof (welcome));
736 pm->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
737 GNUNET_STATISTICS_update (plugin->env->stats,
738 gettext_noop ("# bytes currently in TCP buffers"),
739 pm->message_size, GNUNET_NO);
740 GNUNET_CONTAINER_DLL_insert (ret->pending_messages_head,
741 ret->pending_messages_tail, pm);
742 if (is_nat != GNUNET_YES)
744 GNUNET_STATISTICS_update (plugin->env->stats,
745 gettext_noop ("# TCP sessions active"), 1,
753 * If we have pending messages, ask the server to
754 * transmit them (schedule the respective tasks, etc.)
756 * @param session for which session should we do this
759 process_pending_messages (struct Session *session);
763 * Function called to notify a client about the socket
764 * being ready to queue more data. "buf" will be
765 * NULL and "size" zero if the socket was closed for
766 * writing in the meantime.
769 * @param size number of bytes available in buf
770 * @param buf where the callee should write the message
771 * @return number of bytes written to buf
774 do_transmit (void *cls, size_t size, void *buf)
776 struct Session *session = cls;
777 struct GNUNET_PeerIdentity pid;
778 struct Plugin *plugin;
779 struct PendingMessage *pos;
780 struct PendingMessage *hd;
781 struct PendingMessage *tl;
782 struct GNUNET_TIME_Absolute now;
786 GNUNET_assert (session != NULL);
787 session->transmit_handle = NULL;
788 plugin = session->plugin;
791 LOG (GNUNET_ERROR_TYPE_DEBUG,
792 "Timeout trying to transmit to peer `%4s', discarding message queue.\n",
793 GNUNET_i2s (&session->target));
794 /* timeout; cancel all messages that have already expired */
798 now = GNUNET_TIME_absolute_get ();
799 while ((NULL != (pos = session->pending_messages_head)) &&
800 (pos->timeout.abs_value <= now.abs_value))
802 GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
803 session->pending_messages_tail, pos);
804 LOG (GNUNET_ERROR_TYPE_DEBUG,
805 "Failed to transmit %u byte message to `%4s'.\n",
806 pos->message_size, GNUNET_i2s (&session->target));
807 ret += pos->message_size;
808 GNUNET_CONTAINER_DLL_insert_after (hd, tl, tl, pos);
810 /* do this call before callbacks (so that if callbacks destroy
811 * session, they have a chance to cancel actions done by this
813 process_pending_messages (session);
814 pid = session->target;
815 /* no do callbacks and do not use session again since
816 * the callbacks may abort the session */
817 while (NULL != (pos = hd))
819 GNUNET_CONTAINER_DLL_remove (hd, tl, pos);
820 if (pos->transmit_cont != NULL)
821 pos->transmit_cont (pos->transmit_cont_cls, &pid, GNUNET_SYSERR);
824 GNUNET_STATISTICS_update (plugin->env->stats,
825 gettext_noop ("# bytes currently in TCP buffers"),
826 -(int64_t) ret, GNUNET_NO);
827 GNUNET_STATISTICS_update (plugin->env->stats,
829 ("# bytes discarded by TCP (timeout)"), ret,
833 /* copy all pending messages that would fit */
838 while (NULL != (pos = session->pending_messages_head))
840 if (ret + pos->message_size > size)
842 GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
843 session->pending_messages_tail, pos);
844 GNUNET_assert (size >= pos->message_size);
845 LOG (GNUNET_ERROR_TYPE_DEBUG,
846 "Transmitting message of type %u\n",
847 ntohs (((struct GNUNET_MessageHeader *) pos->msg)->type));
848 /* FIXME: this memcpy can be up to 7% of our total runtime */
849 memcpy (cbuf, pos->msg, pos->message_size);
850 cbuf += pos->message_size;
851 ret += pos->message_size;
852 size -= pos->message_size;
853 GNUNET_CONTAINER_DLL_insert_tail (hd, tl, pos);
855 /* schedule 'continuation' before callbacks so that callbacks that
856 * cancel everything don't cause us to use a session that no longer
858 process_pending_messages (session);
859 session->last_activity = GNUNET_TIME_absolute_get ();
860 pid = session->target;
861 /* we'll now call callbacks that may cancel the session; hence
862 * we should not use 'session' after this point */
863 while (NULL != (pos = hd))
865 GNUNET_CONTAINER_DLL_remove (hd, tl, pos);
866 if (pos->transmit_cont != NULL)
867 pos->transmit_cont (pos->transmit_cont_cls, &pid, GNUNET_OK);
870 GNUNET_assert (hd == NULL);
871 GNUNET_assert (tl == NULL);
872 LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmitting %u bytes\n",
874 GNUNET_STATISTICS_update (plugin->env->stats,
875 gettext_noop ("# bytes currently in TCP buffers"),
876 -(int64_t) ret, GNUNET_NO);
877 GNUNET_STATISTICS_update (plugin->env->stats,
878 gettext_noop ("# bytes transmitted via TCP"), ret,
885 * If we have pending messages, ask the server to
886 * transmit them (schedule the respective tasks, etc.)
888 * @param session for which session should we do this
891 process_pending_messages (struct Session *session)
893 struct PendingMessage *pm;
895 GNUNET_assert (session->client != NULL);
896 if (session->transmit_handle != NULL)
898 if (NULL == (pm = session->pending_messages_head))
901 session->transmit_handle =
902 GNUNET_SERVER_notify_transmit_ready (session->client, pm->message_size,
903 GNUNET_TIME_absolute_get_remaining
904 (pm->timeout), &do_transmit,
910 * Functions with this signature are called whenever we need
911 * to close a session due to a disconnect or failure to
912 * establish a connection.
914 * @param session session to close down
917 disconnect_session (struct Session *session)
919 struct PendingMessage *pm;
920 struct Plugin * plugin = session->plugin;
922 LOG (GNUNET_ERROR_TYPE_DEBUG,
923 "Disconnecting session of peer `%s' address `%s'\n",
924 GNUNET_i2s (&session->target),
925 tcp_address_to_string(NULL, session->addr, session->addrlen));
927 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(plugin->sessionmap, &session->target.hashPubKey, session))
929 GNUNET_STATISTICS_update (session->plugin->env->stats,
930 gettext_noop ("# TCP sessions active"), -1,
932 dec_sessions (plugin, session, __LINE__);
934 else GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(plugin->nat_wait_conns, &session->target.hashPubKey, session));
937 if (session->transmit_handle != NULL)
939 GNUNET_SERVER_notify_transmit_ready_cancel (session->transmit_handle);
940 session->transmit_handle = NULL;
942 session->plugin->env->session_end (session->plugin->env->cls,
943 &session->target, session);
945 if (session->nat_connection_timeout != GNUNET_SCHEDULER_NO_TASK)
947 GNUNET_SCHEDULER_cancel (session->nat_connection_timeout);
948 session->nat_connection_timeout = GNUNET_SCHEDULER_NO_TASK;
951 while (NULL != (pm = session->pending_messages_head))
953 LOG (GNUNET_ERROR_TYPE_DEBUG,
955 NULL ? "Could not deliver message to `%4s'.\n" :
956 "Could not deliver message to `%4s', notifying.\n",
957 GNUNET_i2s (&session->target));
958 GNUNET_STATISTICS_update (session->plugin->env->stats,
959 gettext_noop ("# bytes currently in TCP buffers"),
960 -(int64_t) pm->message_size, GNUNET_NO);
961 GNUNET_STATISTICS_update (session->plugin->env->stats,
963 ("# bytes discarded by TCP (disconnect)"),
964 pm->message_size, GNUNET_NO);
965 GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
966 session->pending_messages_tail, pm);
967 if (NULL != pm->transmit_cont)
968 pm->transmit_cont (pm->transmit_cont_cls, &session->target,
972 if (session->receive_delay_task != GNUNET_SCHEDULER_NO_TASK)
974 GNUNET_SCHEDULER_cancel (session->receive_delay_task);
975 if (session->client != NULL)
976 GNUNET_SERVER_receive_done (session->client, GNUNET_SYSERR);
978 if (session->client != NULL)
980 GNUNET_SERVER_client_drop (session->client);
981 session->client = NULL;
985 GNUNET_free_non_null (session->addr);
986 GNUNET_assert (NULL == session->transmit_handle);
987 GNUNET_free (session);
992 * Function that can be used by the transport service to transmit
993 * a message using the plugin. Note that in the case of a
994 * peer disconnecting, the continuation MUST be called
995 * prior to the disconnect notification itself. This function
996 * will be called with this peer's HELLO message to initiate
997 * a fresh connection to another peer.
1000 * @param session which session must be used
1001 * @param msgbuf the message to transmit
1002 * @param msgbuf_size number of bytes in 'msgbuf'
1003 * @param priority how important is the message (most plugins will
1004 * ignore message priority and just FIFO)
1005 * @param to how long to wait at most for the transmission (does not
1006 * require plugins to discard the message after the timeout,
1007 * just advisory for the desired delay; most plugins will ignore
1009 * @param cont continuation to call once the message has
1010 * been transmitted (or if the transport is ready
1011 * for the next transmission call; or if the
1012 * peer disconnected...); can be NULL
1013 * @param cont_cls closure for cont
1014 * @return number of bytes used (on the physical network, with overheads);
1015 * -1 on hard errors (i.e. address invalid); 0 is a legal value
1016 * and does NOT mean that the message was not transmitted (DV)
1019 tcp_plugin_send (void *cls,
1020 struct Session *session,
1021 const char *msgbuf, size_t msgbuf_size,
1022 unsigned int priority,
1023 struct GNUNET_TIME_Relative to,
1024 GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
1026 struct Plugin * plugin = cls;
1027 struct PendingMessage *pm;
1029 GNUNET_assert (plugin != NULL);
1030 GNUNET_assert (session != NULL);
1032 /* create new message entry */
1033 pm = GNUNET_malloc (sizeof (struct PendingMessage) + msgbuf_size);
1034 pm->msg = (const char *) &pm[1];
1035 memcpy (&pm[1], msgbuf, msgbuf_size);
1036 pm->message_size = msgbuf_size;
1037 pm->timeout = GNUNET_TIME_relative_to_absolute (to);
1038 pm->transmit_cont = cont;
1039 pm->transmit_cont_cls = cont_cls;
1041 LOG (GNUNET_ERROR_TYPE_DEBUG,
1042 "Asked to transmit %u bytes to `%s', added message to list.\n",
1043 msgbuf_size, GNUNET_i2s (&session->target));
1045 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains_value(plugin->sessionmap, &session->target.hashPubKey, session))
1047 GNUNET_assert (session->client != NULL);
1049 GNUNET_SERVER_client_set_timeout (session->client,
1050 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
1051 GNUNET_STATISTICS_update (plugin->env->stats,
1052 gettext_noop ("# bytes currently in TCP buffers"),
1053 msgbuf_size, GNUNET_NO);
1055 /* append pm to pending_messages list */
1056 GNUNET_CONTAINER_DLL_insert_tail (session->pending_messages_head,
1057 session->pending_messages_tail, pm);
1059 process_pending_messages (session);
1062 else if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains_value(plugin->nat_wait_conns, &session->target.hashPubKey, session))
1064 LOG (GNUNET_ERROR_TYPE_DEBUG,
1065 "This NAT WAIT session for peer `%s' is not yet ready!\n",
1066 GNUNET_i2s (&session->target));
1068 GNUNET_STATISTICS_update (plugin->env->stats,
1069 gettext_noop ("# bytes currently in TCP buffers"),
1070 msgbuf_size, GNUNET_NO);
1072 /* append pm to pending_messages list */
1073 GNUNET_CONTAINER_DLL_insert_tail (session->pending_messages_head,
1074 session->pending_messages_tail, pm);
1080 cont (cont_cls, &session->target, GNUNET_SYSERR);
1083 return GNUNET_SYSERR; /* session does not exist here */
1091 struct Session * result;
1095 session_lookup_it (void *cls,
1096 const GNUNET_HashCode * key,
1099 struct SessionItCtx * si_ctx = cls;
1100 struct Session * session = value;
1102 char * a1 = strdup (tcp_address_to_string(NULL, session->addr, session->addrlen));
1103 char * a2 = strdup (tcp_address_to_string(NULL, si_ctx->addr, si_ctx->addrlen));
1104 LOG (GNUNET_ERROR_TYPE_DEBUG,
1105 "Comparing: %s %u <-> %s %u\n",
1113 if (session->addrlen != si_ctx->addrlen)
1117 if (0 != memcmp (session->addr, si_ctx->addr, si_ctx->addrlen))
1122 a1 = strdup (tcp_address_to_string(NULL, session->addr, session->addrlen));
1123 a2 = strdup (tcp_address_to_string(NULL, si_ctx->addr, si_ctx->addrlen));
1124 LOG (GNUNET_ERROR_TYPE_DEBUG,
1125 "Comparing: %s %u <-> %s %u , OK!\n",
1133 /* Found existing session */
1134 si_ctx->result = session;
1140 * Task cleaning up a NAT connection attempt after timeout
1143 nat_connect_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1145 struct Session *session = cls;
1147 LOG (GNUNET_ERROR_TYPE_DEBUG,
1148 "NAT WAIT connection to `%4s' at `%s' could not be established, removing session\n",
1149 GNUNET_i2s (&session->target), tcp_address_to_string(NULL, session->addr, session->addrlen));
1150 disconnect_session (session);
1155 * Create a new session to transmit data to the target
1156 * This session will used to send data to this peer and the plugin will
1157 * notify us by calling the env->session_end function
1159 * @param cls closure
1160 * @param address pointer to the GNUNET_HELLO_Address
1161 * @return the session if the address is valid, NULL otherwise
1163 static struct Session *
1164 tcp_plugin_get_session (void *cls,
1165 const struct GNUNET_HELLO_Address *address)
1167 struct Plugin * plugin = cls;
1168 struct Session * session = NULL;
1172 struct GNUNET_CONNECTION_Handle *sa;
1173 struct sockaddr_in a4;
1174 struct sockaddr_in6 a6;
1175 const struct IPv4TcpAddress *t4;
1176 const struct IPv6TcpAddress *t6;
1177 struct GNUNET_ATS_Information ats;
1178 unsigned int is_natd = GNUNET_NO;
1181 GNUNET_assert (plugin != NULL);
1182 GNUNET_assert (address != NULL);
1183 addrlen = address->address_length;
1184 LOG (GNUNET_ERROR_TYPE_ERROR,
1185 "Trying to get session for `%s' address of peer `%s'\n",
1186 tcp_address_to_string(NULL, address->address, address->address_length),
1187 GNUNET_i2s (&address->peer));
1189 /* look for existing session */
1191 GNUNET_CONTAINER_multihashmap_contains(plugin->sessionmap, &address->peer.hashPubKey))
1193 struct SessionItCtx si_ctx;
1195 si_ctx.addr = (void *) address->address;
1196 si_ctx.addrlen = address->address_length;
1198 si_ctx.result = NULL;
1200 GNUNET_CONTAINER_multihashmap_get_multiple(plugin->sessionmap, &address->peer.hashPubKey, &session_lookup_it, &si_ctx);
1201 if (si_ctx.result != NULL)
1203 session = si_ctx.result;
1204 LOG (GNUNET_ERROR_TYPE_DEBUG,
1205 "Found exisiting session for `%s' address `%s' session %p\n",
1206 GNUNET_i2s (&address->peer),
1207 tcp_address_to_string(NULL, address->address, address->address_length),
1211 LOG (GNUNET_ERROR_TYPE_DEBUG,
1212 "Existing sessions did not match address `%s' or peer `%s'\n",
1213 tcp_address_to_string(NULL, address->address, address->address_length),
1214 GNUNET_i2s (&address->peer));
1217 if (addrlen == sizeof (struct IPv6TcpAddress))
1219 GNUNET_assert (NULL != address->address); /* make static analysis happy */
1220 t6 = address->address;
1222 memset (&a6, 0, sizeof (a6));
1223 #if HAVE_SOCKADDR_IN_SIN_LEN
1224 a6.sin6_len = sizeof (a6);
1226 a6.sin6_family = AF_INET6;
1227 a6.sin6_port = t6->t6_port;
1228 if (t6->t6_port == 0)
1229 is_natd = GNUNET_YES;
1230 memcpy (&a6.sin6_addr, &t6->ipv6_addr, sizeof (struct in6_addr));
1234 else if (addrlen == sizeof (struct IPv4TcpAddress))
1236 GNUNET_assert (NULL != address->address); /* make static analysis happy */
1237 t4 = address->address;
1239 memset (&a4, 0, sizeof (a4));
1240 #if HAVE_SOCKADDR_IN_SIN_LEN
1241 a4.sin_len = sizeof (a4);
1243 a4.sin_family = AF_INET;
1244 a4.sin_port = t4->t4_port;
1245 if (t4->t4_port == 0)
1246 is_natd = GNUNET_YES;
1247 a4.sin_addr.s_addr = t4->ipv4_addr;
1253 LOG (GNUNET_ERROR_TYPE_ERROR,
1254 _("Address of unexpected length: %u\n"), addrlen);
1259 ats = plugin->env->get_address_type (plugin->env->cls, sb ,sbs);
1261 if ((is_natd == GNUNET_YES) && (addrlen == sizeof (struct IPv6TcpAddress)))
1263 /* NAT client only works with IPv4 addresses */
1267 if (0 == plugin->max_connections)
1273 if ((is_natd == GNUNET_YES) &&
1275 GNUNET_CONTAINER_multihashmap_contains (plugin->nat_wait_conns,
1276 &address->peer.hashPubKey)))
1278 /* Only do one NAT punch attempt per peer identity */
1282 if ((is_natd == GNUNET_YES) && (NULL != plugin->nat) &&
1284 GNUNET_CONTAINER_multihashmap_contains (plugin->nat_wait_conns,
1285 &address->peer.hashPubKey)))
1287 LOG (GNUNET_ERROR_TYPE_DEBUG,
1288 "Found valid IPv4 NAT address (creating session)!\n") ;
1289 session = create_session (plugin, &address->peer, NULL, GNUNET_YES);
1290 session->addrlen = 0;
1291 session->addr = NULL;
1292 session->ats_address_network_type = ats.value;
1293 session->nat_connection_timeout = GNUNET_SCHEDULER_add_delayed(NAT_TIMEOUT,
1294 &nat_connect_timeout,
1296 GNUNET_assert (session != NULL);
1297 GNUNET_assert (GNUNET_CONTAINER_multihashmap_put
1298 (plugin->nat_wait_conns, &address->peer.hashPubKey, session,
1299 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) == GNUNET_OK);
1301 LOG (GNUNET_ERROR_TYPE_DEBUG,
1302 "Created NAT WAIT connection to `%4s' at `%s'\n",
1303 GNUNET_i2s (&session->target), GNUNET_a2s (sb, sbs));
1305 if (GNUNET_OK == GNUNET_NAT_run_client (plugin->nat, &a4))
1309 LOG (GNUNET_ERROR_TYPE_DEBUG,
1310 "Running NAT client for `%4s' at `%s' failed\n",
1311 GNUNET_i2s (&session->target), GNUNET_a2s (sb, sbs));
1312 disconnect_session (session);
1317 /* create new outbound session */
1318 GNUNET_assert (0 != plugin->max_connections);
1319 sa = GNUNET_CONNECTION_create_from_sockaddr (af, sb, sbs);
1322 LOG (GNUNET_ERROR_TYPE_DEBUG,
1323 "Failed to create connection to `%4s' at `%s'\n",
1324 GNUNET_i2s (&session->target), GNUNET_a2s (sb, sbs));
1327 plugin->max_connections--;
1329 LOG (GNUNET_ERROR_TYPE_DEBUG,
1330 "Asked to transmit to `%4s', creating fresh session using address `%s'.\n",
1331 GNUNET_i2s (&address->peer), GNUNET_a2s (sb, sbs));
1333 session = create_session (plugin,
1335 GNUNET_SERVER_connect_socket (plugin->server, sa),
1337 session->addr = GNUNET_malloc (addrlen);
1338 memcpy (session->addr, address->address, addrlen);
1339 session->addrlen = addrlen;
1340 session->ats_address_network_type = ats.value;
1342 GNUNET_CONTAINER_multihashmap_put(plugin->sessionmap, &address->peer.hashPubKey, session, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1343 inc_sessions (plugin, session, __LINE__);
1344 LOG (GNUNET_ERROR_TYPE_DEBUG,
1345 "Creating new session for `%s' address `%s' session %p\n",
1346 GNUNET_i2s (&address->peer),
1347 tcp_address_to_string(NULL, address->address, address->address_length),
1349 /* Send TCP Welcome */
1350 process_pending_messages (session);
1357 session_disconnect_it (void *cls,
1358 const GNUNET_HashCode * key,
1361 struct Session *session = value;
1363 GNUNET_STATISTICS_update (session->plugin->env->stats,
1365 ("# transport-service disconnect requests for TCP"),
1367 disconnect_session (session);
1372 * Function that can be called to force a disconnect from the
1373 * specified neighbour. This should also cancel all previously
1374 * scheduled transmissions. Obviously the transmission may have been
1375 * partially completed already, which is OK. The plugin is supposed
1376 * to close the connection (if applicable) and no longer call the
1377 * transmit continuation(s).
1379 * Finally, plugin MUST NOT call the services's receive function to
1380 * notify the service that the connection to the specified target was
1381 * closed after a getting this call.
1383 * @param cls closure
1384 * @param target peer for which the last transmission is
1388 tcp_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
1390 struct Plugin *plugin = cls;
1392 LOG (GNUNET_ERROR_TYPE_DEBUG,
1393 "Disconnecting peer `%4s'\n", GNUNET_i2s (target));
1394 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->sessionmap, &target->hashPubKey, &session_disconnect_it, plugin);
1395 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->nat_wait_conns, &target->hashPubKey, &session_disconnect_it, plugin);
1400 * Context for address to string conversion.
1402 struct PrettyPrinterContext
1405 * Function to call with the result.
1407 GNUNET_TRANSPORT_AddressStringCallback asc;
1410 * Clsoure for 'asc'.
1415 * Port to add after the IP address.
1424 * Append our port and forward the result.
1426 * @param cls the 'struct PrettyPrinterContext*'
1427 * @param hostname hostname part of the address
1430 append_port (void *cls, const char *hostname)
1432 struct PrettyPrinterContext *ppc = cls;
1435 if (hostname == NULL)
1437 ppc->asc (ppc->asc_cls, NULL);
1441 if (GNUNET_YES == ppc->ipv6)
1442 GNUNET_asprintf (&ret, "[%s]:%d", hostname, ppc->port);
1444 GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
1445 ppc->asc (ppc->asc_cls, ret);
1451 * Convert the transports address to a nice, human-readable
1454 * @param cls closure
1455 * @param type name of the transport that generated the address
1456 * @param addr one of the addresses of the host, NULL for the last address
1457 * the specific address format depends on the transport
1458 * @param addrlen length of the address
1459 * @param numeric should (IP) addresses be displayed in numeric form?
1460 * @param timeout after how long should we give up?
1461 * @param asc function to call on each string
1462 * @param asc_cls closure for asc
1465 tcp_plugin_address_pretty_printer (void *cls, const char *type,
1466 const void *addr, size_t addrlen,
1468 struct GNUNET_TIME_Relative timeout,
1469 GNUNET_TRANSPORT_AddressStringCallback asc,
1472 struct PrettyPrinterContext *ppc;
1475 struct sockaddr_in a4;
1476 struct sockaddr_in6 a6;
1477 const struct IPv4TcpAddress *t4;
1478 const struct IPv6TcpAddress *t6;
1481 if (addrlen == sizeof (struct IPv6TcpAddress))
1484 memset (&a6, 0, sizeof (a6));
1485 a6.sin6_family = AF_INET6;
1486 a6.sin6_port = t6->t6_port;
1487 memcpy (&a6.sin6_addr, &t6->ipv6_addr, sizeof (struct in6_addr));
1488 port = ntohs (t6->t6_port);
1492 else if (addrlen == sizeof (struct IPv4TcpAddress))
1495 memset (&a4, 0, sizeof (a4));
1496 a4.sin_family = AF_INET;
1497 a4.sin_port = t4->t4_port;
1498 a4.sin_addr.s_addr = t4->ipv4_addr;
1499 port = ntohs (t4->t4_port);
1503 else if (0 == addrlen)
1505 asc (asc_cls, "<inbound connection>");
1506 asc (asc_cls, NULL);
1511 /* invalid address */
1512 GNUNET_break_op (0);
1513 asc (asc_cls, NULL);
1516 ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
1517 if (addrlen == sizeof (struct IPv6TcpAddress))
1518 ppc->ipv6 = GNUNET_YES;
1520 ppc->ipv6 = GNUNET_NO;
1522 ppc->asc_cls = asc_cls;
1524 GNUNET_RESOLVER_hostname_get (sb, sbs, !numeric, timeout, &append_port, ppc);
1529 * Check if the given port is plausible (must be either our listen
1530 * port or our advertised port), or any port if we are behind NAT
1531 * and do not have a port open. If it is neither, we return
1534 * @param plugin global variables
1535 * @param in_port port number to check
1536 * @return GNUNET_OK if port is either open_port or adv_port
1539 check_port (struct Plugin *plugin, uint16_t in_port)
1541 if ((in_port == plugin->adv_port) || (in_port == plugin->open_port))
1543 return GNUNET_SYSERR;
1548 * Function that will be called to check if a binary address for this
1549 * plugin is well-formed and corresponds to an address for THIS peer
1550 * (as per our configuration). Naturally, if absolutely necessary,
1551 * plugins can be a bit conservative in their answer, but in general
1552 * plugins should make sure that the address does not redirect
1553 * traffic to a 3rd party that might try to man-in-the-middle our
1556 * @param cls closure, our 'struct Plugin*'
1557 * @param addr pointer to the address
1558 * @param addrlen length of addr
1559 * @return GNUNET_OK if this is a plausible address for this peer
1560 * and transport, GNUNET_SYSERR if not
1563 tcp_plugin_check_address (void *cls, const void *addr, size_t addrlen)
1565 struct Plugin *plugin = cls;
1566 struct IPv4TcpAddress *v4;
1567 struct IPv6TcpAddress *v6;
1569 if ((addrlen != sizeof (struct IPv4TcpAddress)) &&
1570 (addrlen != sizeof (struct IPv6TcpAddress)))
1572 GNUNET_break_op (0);
1573 return GNUNET_SYSERR;
1575 if (addrlen == sizeof (struct IPv4TcpAddress))
1577 v4 = (struct IPv4TcpAddress *) addr;
1578 if (GNUNET_OK != check_port (plugin, ntohs (v4->t4_port)))
1579 return GNUNET_SYSERR;
1581 GNUNET_NAT_test_address (plugin->nat, &v4->ipv4_addr,
1582 sizeof (struct in_addr)))
1583 return GNUNET_SYSERR;
1587 v6 = (struct IPv6TcpAddress *) addr;
1588 if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
1590 GNUNET_break_op (0);
1591 return GNUNET_SYSERR;
1593 if (GNUNET_OK != check_port (plugin, ntohs (v6->t6_port)))
1594 return GNUNET_SYSERR;
1596 GNUNET_NAT_test_address (plugin->nat, &v6->ipv6_addr,
1597 sizeof (struct in6_addr)))
1598 return GNUNET_SYSERR;
1605 * We've received a nat probe from this peer via TCP. Finish
1606 * creating the client session and resume sending of queued
1609 * @param cls closure
1610 * @param client identification of the client
1611 * @param message the actual message
1614 handle_tcp_nat_probe (void *cls, struct GNUNET_SERVER_Client *client,
1615 const struct GNUNET_MessageHeader *message)
1617 struct Plugin *plugin = cls;
1618 struct Session *session;
1619 const struct TCP_NAT_ProbeMessage *tcp_nat_probe;
1622 struct IPv4TcpAddress *t4;
1623 struct IPv6TcpAddress *t6;
1624 const struct sockaddr_in *s4;
1625 const struct sockaddr_in6 *s6;
1627 LOG (GNUNET_ERROR_TYPE_DEBUG, "received NAT probe\n");
1629 /* We have received a TCP NAT probe, meaning we (hopefully) initiated
1630 * a connection to this peer by running gnunet-nat-client. This peer
1631 * received the punch message and now wants us to use the new connection
1632 * as the default for that peer. Do so and then send a WELCOME message
1633 * so we can really be connected!
1635 if (ntohs (message->size) != sizeof (struct TCP_NAT_ProbeMessage))
1637 GNUNET_break_op (0);
1638 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1642 tcp_nat_probe = (const struct TCP_NAT_ProbeMessage *) message;
1644 memcmp (&tcp_nat_probe->clientIdentity, plugin->env->my_identity,
1645 sizeof (struct GNUNET_PeerIdentity)))
1647 /* refuse connections from ourselves */
1648 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1653 GNUNET_CONTAINER_multihashmap_get (plugin->nat_wait_conns,
1655 clientIdentity.hashPubKey);
1656 if (session == NULL)
1658 LOG (GNUNET_ERROR_TYPE_DEBUG,
1659 "Did NOT find session for NAT probe!\n");
1660 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1663 LOG (GNUNET_ERROR_TYPE_DEBUG,
1664 "Found session for NAT probe!\n");
1666 if (session->nat_connection_timeout != GNUNET_SCHEDULER_NO_TASK)
1668 GNUNET_SCHEDULER_cancel (session->nat_connection_timeout);
1669 session->nat_connection_timeout = GNUNET_SCHEDULER_NO_TASK;
1672 GNUNET_assert (GNUNET_CONTAINER_multihashmap_remove
1673 (plugin->nat_wait_conns,
1674 &tcp_nat_probe->clientIdentity.hashPubKey,
1675 session) == GNUNET_YES);
1676 if (GNUNET_OK != GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
1679 GNUNET_free (session);
1680 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1684 GNUNET_SERVER_client_keep (client);
1685 session->client = client;
1686 session->last_activity = GNUNET_TIME_absolute_get ();
1687 session->inbound = GNUNET_NO;
1688 LOG (GNUNET_ERROR_TYPE_DEBUG,
1689 "Found address `%s' for incoming connection\n",
1690 GNUNET_a2s (vaddr, alen));
1691 switch (((const struct sockaddr *) vaddr)->sa_family)
1695 t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress));
1696 t4->t4_port = s4->sin_port;
1697 t4->ipv4_addr = s4->sin_addr.s_addr;
1699 session->addrlen = sizeof (struct IPv4TcpAddress);
1703 t6 = GNUNET_malloc (sizeof (struct IPv6TcpAddress));
1704 t6->t6_port = s6->sin6_port;
1705 memcpy (&t6->ipv6_addr, &s6->sin6_addr, sizeof (struct in6_addr));
1707 session->addrlen = sizeof (struct IPv6TcpAddress);
1710 GNUNET_break_op (0);
1711 LOG (GNUNET_ERROR_TYPE_DEBUG,
1712 "Bad address for incoming connection!\n");
1713 GNUNET_free (vaddr);
1715 GNUNET_SERVER_client_drop (client);
1716 GNUNET_free (session);
1717 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1720 GNUNET_free (vaddr);
1722 GNUNET_CONTAINER_multihashmap_put(plugin->sessionmap, &session->target.hashPubKey, session, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1723 inc_sessions (plugin, session, __LINE__);
1724 GNUNET_STATISTICS_update (plugin->env->stats,
1725 gettext_noop ("# TCP sessions active"), 1,
1727 process_pending_messages (session);
1728 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1733 * We've received a welcome from this peer via TCP. Possibly create a
1734 * fresh client record and send back our welcome.
1736 * @param cls closure
1737 * @param client identification of the client
1738 * @param message the actual message
1741 handle_tcp_welcome (void *cls, struct GNUNET_SERVER_Client *client,
1742 const struct GNUNET_MessageHeader *message)
1744 struct Plugin *plugin = cls;
1745 const struct WelcomeMessage *wm = (const struct WelcomeMessage *) message;
1746 struct Session *session;
1749 struct IPv4TcpAddress *t4;
1750 struct IPv6TcpAddress *t6;
1751 const struct sockaddr_in *s4;
1752 const struct sockaddr_in6 *s6;
1755 memcmp (&wm->clientIdentity, plugin->env->my_identity,
1756 sizeof (struct GNUNET_PeerIdentity)))
1758 /* refuse connections from ourselves */
1759 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1762 LOG (GNUNET_ERROR_TYPE_ERROR,
1763 "Received %s message from `%4s'\n", "WELCOME",
1764 GNUNET_i2s (&wm->clientIdentity));
1765 GNUNET_STATISTICS_update (plugin->env->stats,
1766 gettext_noop ("# TCP WELCOME messages received"), 1,
1768 session = lookup_session_by_client (plugin, client);
1769 if (session != NULL)
1771 if (GNUNET_OK == GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
1773 LOG (GNUNET_ERROR_TYPE_DEBUG,
1774 "Found existing session %p for peer `%s'\n",
1776 GNUNET_a2s (vaddr, alen));
1777 GNUNET_free (vaddr);
1782 GNUNET_SERVER_client_keep (client);
1783 session = create_session (plugin, &wm->clientIdentity, client, GNUNET_NO);
1784 session->inbound = GNUNET_YES;
1785 if (GNUNET_OK == GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
1787 if (alen == sizeof (struct sockaddr_in))
1790 t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress));
1791 t4->t4_port = s4->sin_port;
1792 t4->ipv4_addr = s4->sin_addr.s_addr;
1794 session->addrlen = sizeof (struct IPv4TcpAddress);
1796 else if (alen == sizeof (struct sockaddr_in6))
1799 t6 = GNUNET_malloc (sizeof (struct IPv6TcpAddress));
1800 t6->t6_port = s6->sin6_port;
1801 memcpy (&t6->ipv6_addr, &s6->sin6_addr, sizeof (struct in6_addr));
1803 session->addrlen = sizeof (struct IPv6TcpAddress);
1806 struct GNUNET_ATS_Information ats;
1807 ats = plugin->env->get_address_type (plugin->env->cls, vaddr ,alen);
1808 session->ats_address_network_type = ats.value;
1810 GNUNET_free (vaddr);
1814 LOG (GNUNET_ERROR_TYPE_DEBUG,
1815 "Did not obtain TCP socket address for incoming connection\n");
1817 GNUNET_CONTAINER_multihashmap_put(plugin->sessionmap, &wm->clientIdentity.hashPubKey, session, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1818 inc_sessions (plugin, session, __LINE__);
1821 if (session->expecting_welcome != GNUNET_YES)
1823 GNUNET_break_op (0);
1824 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1827 session->last_activity = GNUNET_TIME_absolute_get ();
1828 session->expecting_welcome = GNUNET_NO;
1831 process_pending_messages (session);
1833 GNUNET_SERVER_client_set_timeout (client,
1834 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
1835 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1840 * Task to signal the server that we can continue
1841 * receiving from the TCP client now.
1843 * @param cls the 'struct Session*'
1844 * @param tc task context (unused)
1847 delayed_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1849 struct Session *session = cls;
1850 struct GNUNET_TIME_Relative delay;
1851 struct GNUNET_ATS_Information ats;
1853 session->receive_delay_task = GNUNET_SCHEDULER_NO_TASK;
1855 session->plugin->env->receive (session->plugin->env->cls,
1856 &session->target, NULL, &ats, 0, session,
1858 if (delay.rel_value == 0)
1859 GNUNET_SERVER_receive_done (session->client, GNUNET_OK);
1861 session->receive_delay_task =
1862 GNUNET_SCHEDULER_add_delayed (delay, &delayed_done, session);
1867 * We've received data for this peer via TCP. Unbox,
1868 * compute latency and forward.
1870 * @param cls closure
1871 * @param client identification of the client
1872 * @param message the actual message
1875 handle_tcp_data (void *cls, struct GNUNET_SERVER_Client *client,
1876 const struct GNUNET_MessageHeader *message)
1878 struct Plugin *plugin = cls;
1879 struct Session *session;
1880 struct GNUNET_TIME_Relative delay;
1883 type = ntohs (message->type);
1884 if ((GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME == type) ||
1885 (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE == type))
1887 /* We don't want to propagate WELCOME and NAT Probe messages up! */
1888 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1891 session = lookup_session_by_client (plugin, client);
1892 if (NULL == session)
1894 /* No inbound session found */
1898 GNUNET_SERVER_client_get_address (client, &vaddr, &alen);
1899 LOG (GNUNET_ERROR_TYPE_ERROR,
1900 "Received unexpected %u bytes of type %u from `%s'\n",
1901 (unsigned int) ntohs (message->size),
1902 (unsigned int) ntohs (message->type),
1903 GNUNET_a2s(vaddr, alen));
1904 GNUNET_break_op (0);
1905 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1906 GNUNET_free_non_null(vaddr);
1909 else if (GNUNET_YES == session->expecting_welcome)
1911 /* Session is expecting WELCOME message */
1915 GNUNET_SERVER_client_get_address (client, &vaddr, &alen);
1916 LOG (GNUNET_ERROR_TYPE_ERROR,
1917 "Received unexpected %u bytes of type %u from `%s'\n",
1918 (unsigned int) ntohs (message->size),
1919 (unsigned int) ntohs (message->type),
1920 GNUNET_a2s(vaddr, alen));
1921 GNUNET_break_op (0);
1922 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1923 GNUNET_free_non_null(vaddr);
1927 session->last_activity = GNUNET_TIME_absolute_get ();
1928 LOG (GNUNET_ERROR_TYPE_DEBUG,
1929 "Passing %u bytes of type %u from `%4s' to transport service.\n",
1930 (unsigned int) ntohs (message->size),
1931 (unsigned int) ntohs (message->type),
1932 GNUNET_i2s (&session->target));
1934 GNUNET_STATISTICS_update (plugin->env->stats,
1935 gettext_noop ("# bytes received via TCP"),
1936 ntohs (message->size), GNUNET_NO);
1937 struct GNUNET_ATS_Information distance[2];
1939 distance[0].type = htonl (GNUNET_ATS_QUALITY_NET_DISTANCE);
1940 distance[0].value = htonl (1);
1941 distance[1].type = htonl (GNUNET_ATS_NETWORK_TYPE);
1942 distance[1].value = session->ats_address_network_type;
1943 GNUNET_break (ntohl(session->ats_address_network_type) != GNUNET_ATS_NET_UNSPECIFIED);
1945 delay = plugin->env->receive (plugin->env->cls,
1948 (const struct GNUNET_ATS_Information *) &distance,
1950 (GNUNET_YES == session->inbound) ? NULL : session->addr,
1951 (GNUNET_YES == session->inbound) ? 0 : session->addrlen);
1952 if (delay.rel_value == 0)
1954 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1958 LOG (GNUNET_ERROR_TYPE_DEBUG,
1959 "Throttling receiving from `%s' for %llu ms\n",
1960 GNUNET_i2s (&session->target),
1961 (unsigned long long) delay.rel_value);
1962 GNUNET_SERVER_disable_receive_done_warning (client);
1963 session->receive_delay_task =
1964 GNUNET_SCHEDULER_add_delayed (delay, &delayed_done, session);
1970 * Functions with this signature are called whenever a peer
1971 * is disconnected on the network level.
1973 * @param cls closure
1974 * @param client identification of the client
1977 disconnect_notify (void *cls, struct GNUNET_SERVER_Client *client)
1979 struct Plugin *plugin = cls;
1980 struct Session *session;
1984 plugin->max_connections++;
1985 session = lookup_session_by_client (plugin, client);
1986 if (session == NULL)
1987 return; /* unknown, nothing to do */
1988 LOG (GNUNET_ERROR_TYPE_DEBUG,
1989 "Destroying session of `%4s' with %s due to network-level disconnect.\n",
1990 GNUNET_i2s (&session->target),
1992 NULL) ? tcp_address_to_string (session->plugin,
1996 GNUNET_STATISTICS_update (session->plugin->env->stats,
1998 ("# network-level TCP disconnect events"), 1,
2000 disconnect_session (session);
2005 * We can now send a probe message, copy into buffer to really send.
2007 * @param cls closure, a struct TCPProbeContext
2008 * @param size max size to copy
2009 * @param buf buffer to copy message to
2010 * @return number of bytes copied into buf
2013 notify_send_probe (void *cls, size_t size, void *buf)
2015 struct TCPProbeContext *tcp_probe_ctx = cls;
2016 struct Plugin *plugin = tcp_probe_ctx->plugin;
2019 tcp_probe_ctx->transmit_handle = NULL;
2020 GNUNET_CONTAINER_DLL_remove (plugin->probe_head, plugin->probe_tail,
2024 GNUNET_CONNECTION_destroy (tcp_probe_ctx->sock);
2025 GNUNET_free (tcp_probe_ctx);
2028 GNUNET_assert (size >= sizeof (tcp_probe_ctx->message));
2029 memcpy (buf, &tcp_probe_ctx->message, sizeof (tcp_probe_ctx->message));
2030 GNUNET_SERVER_connect_socket (tcp_probe_ctx->plugin->server,
2031 tcp_probe_ctx->sock);
2032 ret = sizeof (tcp_probe_ctx->message);
2033 GNUNET_free (tcp_probe_ctx);
2039 * Function called by the NAT subsystem suggesting another peer wants
2040 * to connect to us via connection reversal. Try to connect back to the
2043 * @param cls closure
2044 * @param addr address to try
2045 * @param addrlen number of bytes in addr
2048 try_connection_reversal (void *cls, const struct sockaddr *addr,
2051 struct Plugin *plugin = cls;
2052 struct GNUNET_CONNECTION_Handle *sock;
2053 struct TCPProbeContext *tcp_probe_ctx;
2056 * We have received an ICMP response, ostensibly from a peer
2057 * that wants to connect to us! Send a message to establish a connection.
2059 sock = GNUNET_CONNECTION_create_from_sockaddr (AF_INET, addr, addrlen);
2062 /* failed for some odd reason (out of sockets?); ignore attempt */
2066 /* FIXME: do we need to track these probe context objects so that
2067 * we can clean them up on plugin unload? */
2068 tcp_probe_ctx = GNUNET_malloc (sizeof (struct TCPProbeContext));
2069 tcp_probe_ctx->message.header.size =
2070 htons (sizeof (struct TCP_NAT_ProbeMessage));
2071 tcp_probe_ctx->message.header.type =
2072 htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE);
2073 memcpy (&tcp_probe_ctx->message.clientIdentity, plugin->env->my_identity,
2074 sizeof (struct GNUNET_PeerIdentity));
2075 tcp_probe_ctx->plugin = plugin;
2076 tcp_probe_ctx->sock = sock;
2077 GNUNET_CONTAINER_DLL_insert (plugin->probe_head, plugin->probe_tail,
2079 tcp_probe_ctx->transmit_handle =
2080 GNUNET_CONNECTION_notify_transmit_ready (sock,
2081 ntohs (tcp_probe_ctx->
2082 message.header.size),
2083 GNUNET_TIME_UNIT_FOREVER_REL,
2091 * Entry point for the plugin.
2093 * @param cls closure, the 'struct GNUNET_TRANSPORT_PluginEnvironment*'
2094 * @return the 'struct GNUNET_TRANSPORT_PluginFunctions*' or NULL on error
2097 libgnunet_plugin_transport_tcp_init (void *cls)
2099 static const struct GNUNET_SERVER_MessageHandler my_handlers[] = {
2100 {&handle_tcp_welcome, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME,
2101 sizeof (struct WelcomeMessage)},
2102 {&handle_tcp_nat_probe, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE,
2103 sizeof (struct TCP_NAT_ProbeMessage)},
2104 {&handle_tcp_data, NULL, GNUNET_MESSAGE_TYPE_ALL, 0},
2107 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
2108 struct GNUNET_TRANSPORT_PluginFunctions *api;
2109 struct Plugin *plugin;
2110 struct GNUNET_SERVICE_Context *service;
2111 unsigned long long aport;
2112 unsigned long long bport;
2113 unsigned long long max_connections;
2115 struct GNUNET_TIME_Relative idle_timeout;
2117 struct sockaddr **addrs;
2118 socklen_t *addrlens;
2120 if (NULL == env->receive)
2122 /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
2123 initialze the plugin or the API */
2124 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
2126 api->address_pretty_printer = &tcp_plugin_address_pretty_printer;
2127 api->address_to_string = &tcp_address_to_string;
2128 api->string_to_address = &tcp_string_to_address;
2133 GNUNET_CONFIGURATION_get_value_number (env->cfg, "transport-tcp",
2136 max_connections = 128;
2140 GNUNET_CONFIGURATION_get_value_number (env->cfg, "transport-tcp", "PORT",
2141 &bport)) || (bport > 65535) ||
2143 GNUNET_CONFIGURATION_get_value_number (env->cfg, "transport-tcp",
2144 "ADVERTISED-PORT", &aport)) &&
2147 LOG (GNUNET_ERROR_TYPE_ERROR,
2149 ("Require valid port number for service `%s' in configuration!\n"),
2159 service = GNUNET_SERVICE_start ("transport-tcp", env->cfg, GNUNET_SERVICE_OPTION_NONE);
2160 if (service == NULL)
2162 LOG (GNUNET_ERROR_TYPE_WARNING,
2163 _("Failed to start service.\n"));
2170 plugin = GNUNET_malloc (sizeof (struct Plugin));
2171 plugin->sessionmap = GNUNET_CONTAINER_multihashmap_create(max_connections);
2172 plugin->max_connections = max_connections;
2173 plugin->open_port = bport;
2174 plugin->adv_port = aport;
2176 plugin->lsock = NULL;
2177 if ((service != NULL) &&
2180 GNUNET_SERVICE_get_server_addresses ("transport-tcp", env->cfg, &addrs,
2184 GNUNET_NAT_register (env->cfg, GNUNET_YES, aport, (unsigned int) ret,
2185 (const struct sockaddr **) addrs, addrlens,
2186 &tcp_nat_port_map_callback,
2187 &try_connection_reversal, plugin);
2191 GNUNET_assert (addrs[ret] != NULL);
2192 GNUNET_free (addrs[ret]);
2194 GNUNET_free_non_null (addrs);
2195 GNUNET_free_non_null (addrlens);
2200 GNUNET_NAT_register (env->cfg, GNUNET_YES, 0, 0, NULL, NULL, NULL,
2201 &try_connection_reversal, plugin);
2203 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
2205 api->send = &tcp_plugin_send;
2206 api->get_session = &tcp_plugin_get_session;
2208 api->disconnect = &tcp_plugin_disconnect;
2209 api->address_pretty_printer = &tcp_plugin_address_pretty_printer;
2210 api->check_address = &tcp_plugin_check_address;
2211 api->address_to_string = &tcp_address_to_string;
2212 api->string_to_address = &tcp_string_to_address;
2213 plugin->service = service;
2214 if (service != NULL)
2216 plugin->server = GNUNET_SERVICE_get_server (service);
2221 GNUNET_CONFIGURATION_get_value_time (env->cfg, "transport-tcp",
2222 "TIMEOUT", &idle_timeout))
2224 LOG (GNUNET_ERROR_TYPE_ERROR,
2225 _("Failed to find option %s in section %s!\n"),
2226 "TIMEOUT", "transport-tcp");
2227 if (plugin->nat != NULL)
2228 GNUNET_NAT_unregister (plugin->nat);
2229 GNUNET_free (plugin);
2234 GNUNET_SERVER_create_with_sockets (&plugin_tcp_access_check, plugin,
2235 NULL, idle_timeout, GNUNET_YES);
2237 plugin->handlers = GNUNET_malloc (sizeof (my_handlers));
2238 memcpy (plugin->handlers, my_handlers, sizeof (my_handlers));
2240 i < sizeof (my_handlers) / sizeof (struct GNUNET_SERVER_MessageHandler);
2242 plugin->handlers[i].callback_cls = plugin;
2243 GNUNET_SERVER_add_handlers (plugin->server, plugin->handlers);
2244 GNUNET_SERVER_disconnect_notify (plugin->server, &disconnect_notify, plugin);
2245 plugin->nat_wait_conns = GNUNET_CONTAINER_multihashmap_create (16);
2247 LOG (GNUNET_ERROR_TYPE_INFO,
2248 _("TCP transport listening on port %llu\n"), bport);
2250 LOG (GNUNET_ERROR_TYPE_INFO,
2252 ("TCP transport not listening on any port (client only)\n"));
2254 LOG (GNUNET_ERROR_TYPE_INFO,
2256 ("TCP transport advertises itself as being on port %llu\n"),
2258 /* Initially set connections to 0 */
2259 GNUNET_STATISTICS_set(plugin->env->stats,
2260 gettext_noop ("# TCP sessions active"), 0,
2267 * Exit point from the plugin.
2270 libgnunet_plugin_transport_tcp_done (void *cls)
2272 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
2273 struct Plugin *plugin = api->cls;
2274 struct TCPProbeContext *tcp_probe;
2281 LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down TCP plugin\n");
2283 /* Removing leftover sessions */
2284 GNUNET_CONTAINER_multihashmap_iterate(plugin->sessionmap, &session_disconnect_it, NULL);
2285 /* Removing leftover NAT sessions */
2286 GNUNET_CONTAINER_multihashmap_iterate(plugin->nat_wait_conns, &session_disconnect_it, NULL);
2288 if (plugin->service != NULL)
2289 GNUNET_SERVICE_stop (plugin->service);
2291 GNUNET_SERVER_destroy (plugin->server);
2292 GNUNET_free (plugin->handlers);
2293 if (plugin->nat != NULL)
2294 GNUNET_NAT_unregister (plugin->nat);
2295 while (NULL != (tcp_probe = plugin->probe_head))
2297 GNUNET_CONTAINER_DLL_remove (plugin->probe_head, plugin->probe_tail,
2299 GNUNET_CONNECTION_destroy (tcp_probe->sock);
2300 GNUNET_free (tcp_probe);
2302 GNUNET_CONTAINER_multihashmap_destroy (plugin->nat_wait_conns);
2303 GNUNET_CONTAINER_multihashmap_destroy (plugin->sessionmap);
2304 GNUNET_free (plugin);
2309 /* end of plugin_transport_tcp.c */