aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/transport/plugin_transport_tcp.c695
-rw-r--r--src/transport/plugin_transport_udp.c39
-rw-r--r--src/transport/test_transport_api_tcp_peer1.conf1
-rw-r--r--src/transport/test_transport_api_tcp_peer2.conf5
4 files changed, 688 insertions, 52 deletions
diff --git a/src/transport/plugin_transport_tcp.c b/src/transport/plugin_transport_tcp.c
index f6ba584eb..7fa586588 100644
--- a/src/transport/plugin_transport_tcp.c
+++ b/src/transport/plugin_transport_tcp.c
@@ -62,6 +62,50 @@ struct WelcomeMessage
62 62
63}; 63};
64 64
65/**
66 * Basically a WELCOME message, but with the purpose
67 * of giving the waiting peer a client handle to use
68 */
69struct TCP_NAT_ProbeMessage
70{
71 /**
72 * Type is GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE.
73 */
74 struct GNUNET_MessageHeader header;
75
76 /**
77 * Identity of the sender of the message.
78 */
79 struct GNUNET_PeerIdentity clientIdentity;
80
81};
82
83/**
84 * Context for sending a NAT probe via TCP.
85 */
86struct TCPProbeContext
87{
88 /**
89 * Probe connection.
90 */
91 struct GNUNET_CONNECTION_Handle *sock;
92
93 /**
94 * Message to be sent.
95 */
96 struct TCP_NAT_ProbeMessage message;
97
98 /**
99 * Handle to the transmission.
100 */
101 struct GNUNET_CONNECTION_TransmitHandle *transmit_handle;
102
103 /**
104 * Transport plugin handle.
105 */
106 struct Plugin *plugin;
107};
108
65 109
66/** 110/**
67 * Network format for IPv4 addresses. 111 * Network format for IPv4 addresses.
@@ -98,7 +142,6 @@ struct IPv6TcpAddress
98 142
99}; 143};
100 144
101
102/** 145/**
103 * Encapsulation of all of the state of the plugin. 146 * Encapsulation of all of the state of the plugin.
104 */ 147 */
@@ -248,6 +291,26 @@ struct Plugin
248 */ 291 */
249 struct GNUNET_CONNECTION_Handle *lsock; 292 struct GNUNET_CONNECTION_Handle *lsock;
250 293
294 /*
295 * stdout pipe handle for the gnunet-nat-server process
296 */
297 struct GNUNET_DISK_PipeHandle *server_stdout;
298
299 /*
300 * stdout file handle (for reading) for the gnunet-nat-server process
301 */
302 const struct GNUNET_DISK_FileHandle *server_stdout_handle;
303
304 /**
305 * ID of select gnunet-nat-server stdout read task
306 */
307 GNUNET_SCHEDULER_TaskIdentifier server_read_task;
308
309 /**
310 * The process id of the server process (if behind NAT)
311 */
312 pid_t server_pid;
313
251 /** 314 /**
252 * List of open TCP sessions. 315 * List of open TCP sessions.
253 */ 316 */
@@ -275,6 +338,22 @@ struct Plugin
275 struct GNUNET_RESOLVER_RequestHandle *hostname_dns; 338 struct GNUNET_RESOLVER_RequestHandle *hostname_dns;
276 339
277 /** 340 /**
341 * Map of peers we have tried to contact behind a NAT
342 */
343 struct GNUNET_CONTAINER_MultiHashMap *nat_wait_conns;
344
345 /**
346 * The external address given to us by the user. Must be actual
347 * outside visible address for NAT punching to work.
348 */
349 char *external_address;
350
351 /**
352 * The internal address given to us by the user (or discovered).
353 */
354 char *internal_address;
355
356 /**
278 * ID of task used to update our addresses when one expires. 357 * ID of task used to update our addresses when one expires.
279 */ 358 */
280 GNUNET_SCHEDULER_TaskIdentifier address_update_task; 359 GNUNET_SCHEDULER_TaskIdentifier address_update_task;
@@ -290,6 +369,16 @@ struct Plugin
290 */ 369 */
291 uint16_t adv_port; 370 uint16_t adv_port;
292 371
372 /**
373 * Is this transport configured to be behind a NAT?
374 */
375 int behind_nat;
376
377 /**
378 * Is this transport configured to allow connections to NAT'd peers?
379 */
380 int allow_nat;
381
293}; 382};
294 383
295 384
@@ -373,25 +462,31 @@ find_session_by_client (struct Plugin *plugin,
373 return ret; 462 return ret;
374} 463}
375 464
376
377/** 465/**
378 * Create a new session. Also queues a welcome message. 466 * Create a new session. Also queues a welcome message.
379 * 467 *
380 * @param plugin us 468 * @param plugin us
381 * @param target peer to connect to 469 * @param target peer to connect to
382 * @param client client to use 470 * @param client client to use
471 * @param is_nat this a NAT session, we should wait for a client to
472 * connect to us from an address, then assign that to
473 * the session
383 * @return new session object 474 * @return new session object
384 */ 475 */
385static struct Session * 476static struct Session *
386create_session (struct Plugin *plugin, 477create_session (struct Plugin *plugin,
387 const struct GNUNET_PeerIdentity *target, 478 const struct GNUNET_PeerIdentity *target,
388 struct GNUNET_SERVER_Client *client) 479 struct GNUNET_SERVER_Client *client, int is_nat)
389{ 480{
390 struct Session *ret; 481 struct Session *ret;
391 struct PendingMessage *pm; 482 struct PendingMessage *pm;
392 struct WelcomeMessage welcome; 483 struct WelcomeMessage welcome;
393 484
394 GNUNET_assert (client != NULL); 485 if (is_nat != GNUNET_YES)
486 GNUNET_assert (client != NULL);
487 else
488 GNUNET_assert (client == NULL);
489
395#if DEBUG_TCP 490#if DEBUG_TCP
396 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 491 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
397 "tcp", 492 "tcp",
@@ -401,8 +496,11 @@ create_session (struct Plugin *plugin,
401 ret = GNUNET_malloc (sizeof (struct Session)); 496 ret = GNUNET_malloc (sizeof (struct Session));
402 ret->last_activity = GNUNET_TIME_absolute_get (); 497 ret->last_activity = GNUNET_TIME_absolute_get ();
403 ret->plugin = plugin; 498 ret->plugin = plugin;
404 ret->next = plugin->sessions; 499 if (is_nat != GNUNET_YES) /* If not a NAT WAIT conn, add it to global list */
405 plugin->sessions = ret; 500 {
501 ret->next = plugin->sessions;
502 plugin->sessions = ret;
503 }
406 ret->client = client; 504 ret->client = client;
407 ret->target = *target; 505 ret->target = *target;
408 ret->expecting_welcome = GNUNET_YES; 506 ret->expecting_welcome = GNUNET_YES;
@@ -421,10 +519,11 @@ create_session (struct Plugin *plugin,
421 GNUNET_CONTAINER_DLL_insert (ret->pending_messages_head, 519 GNUNET_CONTAINER_DLL_insert (ret->pending_messages_head,
422 ret->pending_messages_tail, 520 ret->pending_messages_tail,
423 pm); 521 pm);
424 GNUNET_STATISTICS_update (plugin->env->stats, 522 if (is_nat != GNUNET_YES)
425 gettext_noop ("# TCP sessions active"), 523 GNUNET_STATISTICS_update (plugin->env->stats,
426 1, 524 gettext_noop ("# TCP sessions active"),
427 GNUNET_NO); 525 1,
526 GNUNET_NO);
428 return ret; 527 return ret;
429} 528}
430 529
@@ -724,6 +823,54 @@ select_better_session (struct Session *s1,
724 823
725 824
726/** 825/**
826 * We learned about a peer (possibly behind NAT) so run the
827 * gnunet-nat-client to send dummy ICMP responses
828 *
829 * @param plugin the plugin for this transport
830 * @param addr the address of the peer
831 * @param addrlen the length of the address
832 */
833void
834run_gnunet_nat_client (struct Plugin *plugin, const char *addr, size_t addrlen)
835{
836 char inet4[INET_ADDRSTRLEN];
837 char *address_as_string;
838 char *port_as_string;
839 pid_t pid;
840 const struct sockaddr *sa = (const struct sockaddr *)addr;
841
842 if (addrlen < sizeof (struct sockaddr))
843 return;
844 switch (sa->sa_family)
845 {
846 case AF_INET:
847 if (addrlen != sizeof (struct sockaddr_in))
848 return;
849 inet_ntop (AF_INET,
850 &((struct sockaddr_in *) sa)->sin_addr,
851 inet4, INET_ADDRSTRLEN);
852 address_as_string = GNUNET_strdup (inet4);
853 break;
854 case AF_INET6:
855 default:
856 return;
857 }
858
859 GNUNET_asprintf(&port_as_string, "%d", plugin->adv_port);
860#if DEBUG_UDP_NAT
861 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
862 _("Running gnunet-nat-client with arguments: %s %s %d\n"), plugin->external_address, address_as_string, plugin->adv_port);
863#endif
864
865 /* Start the client process */
866 pid = GNUNET_OS_start_process(NULL, NULL, "gnunet-nat-client", "gnunet-nat-client", plugin->external_address, address_as_string, port_as_string, NULL);
867 GNUNET_free(address_as_string);
868 GNUNET_free(port_as_string);
869 GNUNET_OS_process_wait (pid);
870}
871
872
873/**
727 * Function that can be used by the transport service to transmit 874 * Function that can be used by the transport service to transmit
728 * a message using the plugin. Note that in the case of a 875 * a message using the plugin. Note that in the case of a
729 * peer disconnecting, the continuation MUST be called 876 * peer disconnecting, the continuation MUST be called
@@ -757,7 +904,7 @@ select_better_session (struct Session *s1,
757 * @param cont_cls closure for cont 904 * @param cont_cls closure for cont
758 * @return number of bytes used (on the physical network, with overheads); 905 * @return number of bytes used (on the physical network, with overheads);
759 * -1 on hard errors (i.e. address invalid); 0 is a legal value 906 * -1 on hard errors (i.e. address invalid); 0 is a legal value
760 * and does NOT mean that the message was not transmitted (DV) 907 * and does NOT mean that the message was not transmitted (DV and NAT)
761 */ 908 */
762static ssize_t 909static ssize_t
763tcp_plugin_send (void *cls, 910tcp_plugin_send (void *cls,
@@ -784,6 +931,7 @@ tcp_plugin_send (void *cls,
784 struct sockaddr_in6 a6; 931 struct sockaddr_in6 a6;
785 const struct IPv4TcpAddress *t4; 932 const struct IPv4TcpAddress *t4;
786 const struct IPv6TcpAddress *t6; 933 const struct IPv6TcpAddress *t6;
934 unsigned int is_natd;
787 935
788 GNUNET_STATISTICS_update (plugin->env->stats, 936 GNUNET_STATISTICS_update (plugin->env->stats,
789 gettext_noop ("# bytes TCP was asked to transmit"), 937 gettext_noop ("# bytes TCP was asked to transmit"),
@@ -792,6 +940,7 @@ tcp_plugin_send (void *cls,
792 /* FIXME: we could do this cheaper with a hash table 940 /* FIXME: we could do this cheaper with a hash table
793 where we could restrict the iteration to entries that match 941 where we could restrict the iteration to entries that match
794 the target peer... */ 942 the target peer... */
943 is_natd = GNUNET_NO;
795 if (session == NULL) 944 if (session == NULL)
796 { 945 {
797 cand_session = NULL; 946 cand_session = NULL;
@@ -860,6 +1009,8 @@ tcp_plugin_send (void *cls,
860#endif 1009#endif
861 a6.sin6_family = AF_INET6; 1010 a6.sin6_family = AF_INET6;
862 a6.sin6_port = t6->t6_port; 1011 a6.sin6_port = t6->t6_port;
1012 if (t6->t6_port == 0)
1013 is_natd = GNUNET_YES;
863 memcpy (&a6.sin6_addr, 1014 memcpy (&a6.sin6_addr,
864 &t6->ipv6_addr, 1015 &t6->ipv6_addr,
865 sizeof (struct in6_addr)); 1016 sizeof (struct in6_addr));
@@ -876,6 +1027,8 @@ tcp_plugin_send (void *cls,
876#endif 1027#endif
877 a4.sin_family = AF_INET; 1028 a4.sin_family = AF_INET;
878 a4.sin_port = t4->t_port; 1029 a4.sin_port = t4->t_port;
1030 if (t4->t_port == 0)
1031 is_natd = GNUNET_YES;
879 a4.sin_addr.s_addr = t4->ipv4_addr; 1032 a4.sin_addr.s_addr = t4->ipv4_addr;
880 sb = &a4; 1033 sb = &a4;
881 sbs = sizeof (a4); 1034 sbs = sizeof (a4);
@@ -889,6 +1042,47 @@ tcp_plugin_send (void *cls,
889 GNUNET_break (0); 1042 GNUNET_break (0);
890 return -1; 1043 return -1;
891 } 1044 }
1045
1046 if ((is_natd == GNUNET_YES) && (addrlen == sizeof (struct IPv6TcpAddress)))
1047 return -1; /* NAT client only works with IPv4 addresses */
1048
1049 if ( (plugin->allow_nat == GNUNET_YES) && (is_natd == GNUNET_YES) &&
1050 (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(plugin->nat_wait_conns, &target->hashPubKey)))
1051 {
1052 session = create_session (plugin,
1053 target,
1054 NULL, is_natd);
1055
1056 /* create new message entry */
1057 pm = GNUNET_malloc (sizeof (struct PendingMessage) + msgbuf_size);
1058 pm->msg = (const char*) &pm[1];
1059 memcpy (&pm[1], msg, msgbuf_size);
1060 pm->message_size = msgbuf_size;
1061 pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1062 pm->transmit_cont = cont;
1063 pm->transmit_cont_cls = cont_cls;
1064
1065 /* append pm to pending_messages list */
1066 GNUNET_CONTAINER_DLL_insert_after (session->pending_messages_head,
1067 session->pending_messages_tail,
1068 session->pending_messages_tail,
1069 pm);
1070
1071 GNUNET_CONTAINER_multihashmap_put(plugin->nat_wait_conns, &target->hashPubKey, session, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
1072 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1073 "tcp",
1074 "Created NAT WAIT connection to `%4s' at `%s'\n",
1075 GNUNET_i2s (target),
1076 GNUNET_a2s (sb, sbs));
1077
1078 run_gnunet_nat_client(plugin, addr, addrlen);
1079 return 0;
1080 }
1081 else if ((plugin->allow_nat == GNUNET_YES) && (is_natd == GNUNET_YES) && (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(plugin->nat_wait_conns, &target->hashPubKey)))
1082 {
1083 /* Only do one NAT punch attempt per peer identity */
1084 return -1;
1085 }
892 sa = GNUNET_CONNECTION_create_from_sockaddr (plugin->env->sched, 1086 sa = GNUNET_CONNECTION_create_from_sockaddr (plugin->env->sched,
893 af, sb, sbs, 1087 af, sb, sbs,
894 GNUNET_SERVER_MAX_MESSAGE_SIZE); 1088 GNUNET_SERVER_MAX_MESSAGE_SIZE);
@@ -917,7 +1111,7 @@ tcp_plugin_send (void *cls,
917 session = create_session (plugin, 1111 session = create_session (plugin,
918 target, 1112 target,
919 GNUNET_SERVER_connect_socket (plugin->server, 1113 GNUNET_SERVER_connect_socket (plugin->server,
920 sa)); 1114 sa), is_natd);
921 session->connect_addr = GNUNET_malloc (addrlen); 1115 session->connect_addr = GNUNET_malloc (addrlen);
922 memcpy (session->connect_addr, 1116 memcpy (session->connect_addr,
923 addr, 1117 addr,
@@ -1194,6 +1388,107 @@ tcp_plugin_check_address (void *cls, void *addr, size_t addrlen)
1194 return GNUNET_OK; 1388 return GNUNET_OK;
1195} 1389}
1196 1390
1391/**
1392 * We've received a nat probe from this peer via TCP. Finish
1393 * creating the client session and resume sending of queued
1394 * messages.
1395 *
1396 * @param cls closure
1397 * @param client identification of the client
1398 * @param message the actual message
1399 */
1400static void
1401handle_tcp_nat_probe (void *cls,
1402 struct GNUNET_SERVER_Client *client,
1403 const struct GNUNET_MessageHeader *message)
1404{
1405 struct Plugin *plugin = cls;
1406 struct Session *session;
1407 struct TCP_NAT_ProbeMessage *tcp_nat_probe;
1408 size_t alen;
1409 void *vaddr;
1410 struct IPv4TcpAddress *t4;
1411 struct IPv6TcpAddress *t6;
1412 const struct sockaddr_in *s4;
1413 const struct sockaddr_in6 *s6;
1414
1415 /* We have received a TCP NAT probe, meaning we (hopefully) initiated
1416 * a connection to this peer by running gnunet-nat-client. This peer
1417 * received the punch message and now wants us to use the new connection
1418 * as the default for that peer. Do so and then send a WELCOME message
1419 * so we can really be connected!
1420 */
1421 if (ntohs(message->size) != sizeof(struct TCP_NAT_ProbeMessage))
1422 {
1423 GNUNET_break_op(0);
1424 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1425 return;
1426 }
1427 tcp_nat_probe = (struct TCP_NAT_ProbeMessage *)message;
1428
1429 if (GNUNET_CONTAINER_multihashmap_contains(plugin->nat_wait_conns, &tcp_nat_probe->clientIdentity.hashPubKey) == GNUNET_YES)
1430 {
1431 session = GNUNET_CONTAINER_multihashmap_get(plugin->nat_wait_conns, &tcp_nat_probe->clientIdentity.hashPubKey);
1432 GNUNET_assert(session != NULL);
1433 GNUNET_SERVER_client_keep (client);
1434 session->client = client;
1435
1436 if (GNUNET_OK ==
1437 GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
1438 {
1439#if DEBUG_TCP
1440 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1441 "tcp",
1442 "Found address `%s' for incoming connection %p\n",
1443 GNUNET_a2s (vaddr, alen),
1444 client);
1445#endif
1446 if (alen == sizeof (struct sockaddr_in))
1447 {
1448 s4 = vaddr;
1449 t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress));
1450 t4->t_port = s4->sin_port;
1451 t4->ipv4_addr = s4->sin_addr.s_addr;
1452 session->connect_addr = t4;
1453 session->connect_alen = sizeof (struct IPv4TcpAddress);
1454 }
1455 else if (alen == sizeof (struct sockaddr_in6))
1456 {
1457 s6 = vaddr;
1458 t6 = GNUNET_malloc (sizeof (struct IPv6TcpAddress));
1459 t6->t6_port = s6->sin6_port;
1460 memcpy (&t6->ipv6_addr,
1461 &s6->sin6_addr,
1462 sizeof (struct in6_addr));
1463 session->connect_addr = t6;
1464 session->connect_alen = sizeof (struct IPv6TcpAddress);
1465 }
1466
1467 session->connect_addr = GNUNET_malloc (alen);
1468 memcpy (session->connect_addr,
1469 vaddr,
1470 alen);
1471 session->connect_alen = alen;
1472 GNUNET_free (vaddr);
1473 }
1474 else
1475 {
1476 /* FIXME: free partial session? */
1477 }
1478
1479
1480 session->next = plugin->sessions;
1481 plugin->sessions = session;
1482
1483 GNUNET_STATISTICS_update (plugin->env->stats,
1484 gettext_noop ("# TCP sessions active"),
1485 1,
1486 GNUNET_NO);
1487 /*GNUNET_SERVER_connect_socket (plugin->server,
1488 client);*/
1489 }
1490 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1491}
1197 1492
1198/** 1493/**
1199 * We've received a welcome from this peer via TCP. Possibly create a 1494 * We've received a welcome from this peer via TCP. Possibly create a
@@ -1235,7 +1530,7 @@ handle_tcp_welcome (void *cls,
1235 { 1530 {
1236 GNUNET_SERVER_client_keep (client); 1531 GNUNET_SERVER_client_keep (client);
1237 session = create_session (plugin, 1532 session = create_session (plugin,
1238 &wm->clientIdentity, client); 1533 &wm->clientIdentity, client, GNUNET_NO);
1239 session->inbound = GNUNET_YES; 1534 session->inbound = GNUNET_YES;
1240 if (GNUNET_OK == 1535 if (GNUNET_OK ==
1241 GNUNET_SERVER_client_get_address (client, &vaddr, &alen)) 1536 GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
@@ -1342,14 +1637,14 @@ handle_tcp_data (void *cls,
1342 struct Session *session; 1637 struct Session *session;
1343 struct GNUNET_TIME_Relative delay; 1638 struct GNUNET_TIME_Relative delay;
1344 1639
1345 if (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME == ntohs(message->type)) 1640 if ((GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME == ntohs(message->type)) || (ntohs(message->type) == GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE))
1346 { 1641 {
1347 /* We don't want to propagate WELCOME messages up! */ 1642 /* We don't want to propagate WELCOME messages up! */
1348 GNUNET_SERVER_receive_done (client, GNUNET_OK); 1643 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1349 return; 1644 return;
1350 } 1645 }
1351 session = find_session_by_client (plugin, client); 1646 session = find_session_by_client (plugin, client);
1352 if ( (NULL == session) || (GNUNET_NO != session->expecting_welcome)) 1647 if ( (NULL == session) || (GNUNET_YES == session->expecting_welcome))
1353 { 1648 {
1354 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); 1649 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1355 return; 1650 return;
@@ -1386,6 +1681,7 @@ handle_tcp_data (void *cls,
1386static struct GNUNET_SERVER_MessageHandler my_handlers[] = { 1681static struct GNUNET_SERVER_MessageHandler my_handlers[] = {
1387 {&handle_tcp_welcome, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME, 1682 {&handle_tcp_welcome, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME,
1388 sizeof (struct WelcomeMessage)}, 1683 sizeof (struct WelcomeMessage)},
1684 {&handle_tcp_nat_probe, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE, sizeof (struct TCP_NAT_ProbeMessage)},
1389 {&handle_tcp_data, NULL, GNUNET_MESSAGE_TYPE_ALL, 0}, 1685 {&handle_tcp_data, NULL, GNUNET_MESSAGE_TYPE_ALL, 0},
1390 {NULL, NULL, 0, 0} 1686 {NULL, NULL, 0, 0}
1391}; 1687};
@@ -1453,7 +1749,10 @@ process_interfaces (void *cls,
1453 if (af == AF_INET) 1749 if (af == AF_INET)
1454 { 1750 {
1455 t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr; 1751 t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
1456 t4.t_port = htons (plugin->adv_port); 1752 if (plugin->behind_nat)
1753 t4.t_port = htons(0);
1754 else
1755 t4.t_port = htons (plugin->adv_port);
1457 arg = &t4; 1756 arg = &t4;
1458 args = sizeof (t4); 1757 args = sizeof (t4);
1459 } 1758 }
@@ -1467,7 +1766,10 @@ process_interfaces (void *cls,
1467 memcpy (&t6.ipv6_addr, 1766 memcpy (&t6.ipv6_addr,
1468 &((struct sockaddr_in6 *) addr)->sin6_addr, 1767 &((struct sockaddr_in6 *) addr)->sin6_addr,
1469 sizeof (struct in6_addr)); 1768 sizeof (struct in6_addr));
1470 t6.t6_port = htons (plugin->adv_port); 1769 if (plugin->behind_nat)
1770 t6.t6_port = htons(0);
1771 else
1772 t6.t6_port = htons (plugin->adv_port);
1471 arg = &t6; 1773 arg = &t6;
1472 args = sizeof (t6); 1774 args = sizeof (t6);
1473 } 1775 }
@@ -1508,9 +1810,267 @@ process_hostname_ips (void *cls,
1508 plugin->hostname_dns = NULL; 1810 plugin->hostname_dns = NULL;
1509 return; 1811 return;
1510 } 1812 }
1813 /* FIXME: Can we figure out our external address here so it doesn't need to be user specified? */
1511 process_interfaces (plugin, "<hostname>", GNUNET_YES, addr, addrlen); 1814 process_interfaces (plugin, "<hostname>", GNUNET_YES, addr, addrlen);
1512} 1815}
1513 1816
1817/**
1818 * We can now send a probe message, copy into buffer to really send.
1819 *
1820 * @param cls closure, a struct TCPProbeContext
1821 * @param size max size to copy
1822 * @param buf buffer to copy message to
1823 */
1824static size_t notify_send_probe (void *cls,
1825 size_t size, void *buf)
1826{
1827 struct TCPProbeContext *tcp_probe_ctx = cls;
1828
1829 if (buf == NULL)
1830 {
1831 return 0;
1832 }
1833
1834 GNUNET_assert(size >= sizeof(tcp_probe_ctx->message));
1835 memcpy(buf, &tcp_probe_ctx->message, sizeof(tcp_probe_ctx->message));
1836 GNUNET_SERVER_connect_socket (tcp_probe_ctx->plugin->server,
1837 tcp_probe_ctx->sock);
1838
1839 GNUNET_free(tcp_probe_ctx);
1840 return sizeof(tcp_probe_ctx->message);
1841}
1842
1843/*
1844 * @param cls the plugin handle
1845 * @param tc the scheduling context (for rescheduling this function again)
1846 *
1847 * We have been notified that gnunet-nat-server has written something to stdout.
1848 * Handle the output, then reschedule this function to be called again once
1849 * more is available.
1850 *
1851 */
1852static void
1853tcp_plugin_server_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1854{
1855 struct Plugin *plugin = cls;
1856 char mybuf[40];
1857 ssize_t bytes;
1858 memset(&mybuf, 0, sizeof(mybuf));
1859 int i;
1860 int port;
1861 char *port_start;
1862 struct sockaddr_in in_addr;
1863 struct TCPProbeContext *tcp_probe_ctx;
1864 struct GNUNET_CONNECTION_Handle *sock;
1865
1866 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
1867 return;
1868
1869 bytes = GNUNET_DISK_file_read(plugin->server_stdout_handle, &mybuf, sizeof(mybuf));
1870
1871 if (bytes < 1)
1872 {
1873#if DEBUG_UDP_NAT
1874 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
1875 _("Finished reading from server stdout with code: %d\n"), bytes);
1876#endif
1877 return;
1878 }
1879
1880 port = 0;
1881 port_start = NULL;
1882 for (i = 0; i < sizeof(mybuf); i++)
1883 {
1884 if (mybuf[i] == '\n')
1885 mybuf[i] = '\0';
1886
1887 if ((mybuf[i] == ':') && (i + 1 < sizeof(mybuf)))
1888 {
1889 mybuf[i] = '\0';
1890 port_start = &mybuf[i + 1];
1891 }
1892 }
1893
1894 if (port_start != NULL)
1895 port = atoi(port_start);
1896 else
1897 {
1898 plugin->server_read_task =
1899 GNUNET_SCHEDULER_add_read_file (plugin->env->sched,
1900 GNUNET_TIME_UNIT_FOREVER_REL,
1901 plugin->server_stdout_handle, &tcp_plugin_server_read, plugin);
1902 return;
1903 }
1904
1905#if DEBUG_UDP_NAT
1906 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
1907 _("nat-server-read read: %s port %d\n"), &mybuf, port);
1908#endif
1909
1910
1911 if (inet_pton(AF_INET, &mybuf[0], &in_addr.sin_addr) != 1)
1912 {
1913
1914 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "tcp",
1915 _("nat-server-read malformed address\n"), &mybuf, port);
1916
1917 plugin->server_read_task =
1918 GNUNET_SCHEDULER_add_read_file (plugin->env->sched,
1919 GNUNET_TIME_UNIT_FOREVER_REL,
1920 plugin->server_stdout_handle, &tcp_plugin_server_read, plugin);
1921 return;
1922 }
1923
1924 /**
1925 * We have received an ICMP response, ostensibly from a non-NAT'd peer
1926 * that wants to connect to us! Send a message to establish a connection.
1927 */
1928 sock = GNUNET_CONNECTION_create_from_sockaddr (plugin->env->sched, AF_INET, (struct sockaddr *)&in_addr,
1929 sizeof(in_addr), GNUNET_SERVER_MAX_MESSAGE_SIZE);
1930
1931 if (sock == NULL)
1932 {
1933 plugin->server_read_task =
1934 GNUNET_SCHEDULER_add_read_file (plugin->env->sched,
1935 GNUNET_TIME_UNIT_FOREVER_REL,
1936 plugin->server_stdout_handle, &tcp_plugin_server_read, plugin);
1937 return;
1938 }
1939 else
1940 {
1941 tcp_probe_ctx = GNUNET_malloc(sizeof(struct TCPProbeContext));
1942
1943 tcp_probe_ctx->message.header.size = htons(sizeof(struct TCP_NAT_ProbeMessage));
1944 tcp_probe_ctx->message.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE);
1945 memcpy(&tcp_probe_ctx->message.clientIdentity, plugin->env->my_identity, sizeof(struct GNUNET_PeerIdentity));
1946 tcp_probe_ctx->plugin = plugin;
1947
1948 tcp_probe_ctx->transmit_handle = GNUNET_CONNECTION_notify_transmit_ready (sock,
1949 ntohs(tcp_probe_ctx->message.header.size),
1950 GNUNET_TIME_UNIT_FOREVER_REL,
1951 &notify_send_probe, tcp_probe_ctx);
1952
1953 }
1954
1955 plugin->server_read_task =
1956 GNUNET_SCHEDULER_add_read_file (plugin->env->sched,
1957 GNUNET_TIME_UNIT_FOREVER_REL,
1958 plugin->server_stdout_handle, &tcp_plugin_server_read, plugin);
1959}
1960
1961/**
1962 * Start the gnunet-nat-server process for users behind NAT.
1963 *
1964 * @param plugin the transport plugin
1965 *
1966 * @return GNUNET_YES if process was started, GNUNET_SYSERR on error
1967 */
1968static int
1969tcp_transport_start_nat_server(struct Plugin *plugin)
1970{
1971
1972 plugin->server_stdout = GNUNET_DISK_pipe(GNUNET_YES);
1973 if (plugin->server_stdout == NULL)
1974 return GNUNET_SYSERR;
1975
1976#if DEBUG_TCP_NAT
1977 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1978 "tcp",
1979 "Starting gnunet-nat-server process cmd: %s %s\n", "gnunet-nat-server", plugin->internal_address);
1980#endif
1981 /* Start the server process */
1982 plugin->server_pid = GNUNET_OS_start_process(NULL, plugin->server_stdout, "gnunet-nat-server", "gnunet-nat-server", plugin->internal_address, NULL);
1983 if (plugin->server_pid == GNUNET_SYSERR)
1984 {
1985#if DEBUG_TCP_NAT
1986 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1987 "tcp",
1988 "Failed to start gnunet-nat-server process\n");
1989#endif
1990 return GNUNET_SYSERR;
1991 }
1992 /* Close the write end of the read pipe */
1993 GNUNET_DISK_pipe_close_end(plugin->server_stdout, GNUNET_DISK_PIPE_END_WRITE);
1994
1995 plugin->server_stdout_handle = GNUNET_DISK_pipe_handle(plugin->server_stdout, GNUNET_DISK_PIPE_END_READ);
1996 plugin->server_read_task =
1997 GNUNET_SCHEDULER_add_read_file (plugin->env->sched,
1998 GNUNET_TIME_UNIT_FOREVER_REL,
1999 plugin->server_stdout_handle, &tcp_plugin_server_read, plugin);
2000 return GNUNET_YES;
2001}
2002
2003/**
2004 * Return the actual path to a file found in the current
2005 * PATH environment variable.
2006 *
2007 * @param binary the name of the file to find
2008 */
2009static char *
2010get_path_from_PATH (char *binary)
2011{
2012 char *path;
2013 char *pos;
2014 char *end;
2015 char *buf;
2016 const char *p;
2017
2018 p = getenv ("PATH");
2019 if (p == NULL)
2020 return NULL;
2021 path = GNUNET_strdup (p); /* because we write on it */
2022 buf = GNUNET_malloc (strlen (path) + 20);
2023 pos = path;
2024
2025 while (NULL != (end = strchr (pos, ':')))
2026 {
2027 *end = '\0';
2028 sprintf (buf, "%s/%s", pos, binary);
2029 if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
2030 {
2031 GNUNET_free (path);
2032 return buf;
2033 }
2034 pos = end + 1;
2035 }
2036 sprintf (buf, "%s/%s", pos, binary);
2037 if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
2038 {
2039 GNUNET_free (path);
2040 return buf;
2041 }
2042 GNUNET_free (buf);
2043 GNUNET_free (path);
2044 return NULL;
2045}
2046
2047/**
2048 * Check whether the suid bit is set on a file.
2049 * Attempts to find the file using the current
2050 * PATH environment variable as a search path.
2051 *
2052 * @param binary the name of the file to check
2053 */
2054static int
2055check_gnunet_nat_binary(char *binary)
2056{
2057 struct stat statbuf;
2058 char *p;
2059
2060 p = get_path_from_PATH (binary);
2061 if (p == NULL)
2062 return GNUNET_NO;
2063 if (0 != STAT (p, &statbuf))
2064 {
2065 GNUNET_free (p);
2066 return GNUNET_SYSERR;
2067 }
2068 GNUNET_free (p);
2069 if ( (0 != (statbuf.st_mode & S_ISUID)) &&
2070 (statbuf.st_uid == 0) )
2071 return GNUNET_YES;
2072 return GNUNET_NO;
2073}
1514 2074
1515/** 2075/**
1516 * Entry point for the plugin. 2076 * Entry point for the plugin.
@@ -1528,6 +2088,10 @@ libgnunet_plugin_transport_tcp_init (void *cls)
1528 unsigned long long aport; 2088 unsigned long long aport;
1529 unsigned long long bport; 2089 unsigned long long bport;
1530 unsigned int i; 2090 unsigned int i;
2091 int behind_nat;
2092 int allow_nat;
2093 char *internal_address;
2094 char *external_address;
1531 2095
1532 service = GNUNET_SERVICE_start ("transport-tcp", env->sched, env->cfg); 2096 service = GNUNET_SERVICE_start ("transport-tcp", env->sched, env->cfg);
1533 if (service == NULL) 2097 if (service == NULL)
@@ -1539,6 +2103,73 @@ libgnunet_plugin_transport_tcp_init (void *cls)
1539 "tcp"); 2103 "tcp");
1540 return NULL; 2104 return NULL;
1541 } 2105 }
2106
2107 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2108 "transport-tcp",
2109 "BEHIND_NAT"))
2110 {
2111 /* We are behind nat (according to the user) */
2112 if (check_gnunet_nat_binary("gnunet-nat-server") == GNUNET_YES)
2113 behind_nat = GNUNET_YES;
2114 else
2115 {
2116 behind_nat = GNUNET_NO;
2117 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "tcp", "Configuration specified you are behind a NAT, but gnunet-nat-server is not installed properly (suid bit not set)!\n");
2118 }
2119 }
2120 else
2121 behind_nat = GNUNET_NO; /* We are not behind nat! */
2122
2123 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2124 "transport-tcp",
2125 "ALLOW_NAT"))
2126 {
2127 if (check_gnunet_nat_binary("gnunet-nat-client") == GNUNET_YES)
2128 allow_nat = GNUNET_YES; /* We will try to connect to NAT'd peers */
2129 else
2130 {
2131 allow_nat = GNUNET_NO;
2132 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "tcp", "Configuration specified you want to connect to NAT'd peers, but gnunet-nat-client is not installed properly (suid bit not set)!\n");
2133 }
2134
2135 }
2136 else
2137 allow_nat = GNUNET_NO; /* We don't want to try to help NAT'd peers */
2138
2139 external_address = NULL;
2140 if (((GNUNET_YES == behind_nat) || (GNUNET_YES == allow_nat)) && (GNUNET_OK !=
2141 GNUNET_CONFIGURATION_get_value_string (env->cfg,
2142 "transport-tcp",
2143 "EXTERNAL_ADDRESS",
2144 &external_address)))
2145 {
2146 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2147 "tcp",
2148 _
2149 ("Require EXTERNAL_ADDRESS for service `%s' in configuration (either BEHIND_NAT or ALLOW_NAT set to YES)!\n"),
2150 "transport-tcp");
2151 GNUNET_SERVICE_stop (service);
2152 return NULL;
2153 }
2154
2155 internal_address = NULL;
2156 if ((GNUNET_YES == behind_nat) && (GNUNET_OK !=
2157 GNUNET_CONFIGURATION_get_value_string (env->cfg,
2158 "transport-tcp",
2159 "INTERNAL_ADDRESS",
2160 &internal_address)))
2161 {
2162 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2163 "tcp",
2164 _
2165 ("Require INTERNAL_ADDRESS for service `%s' in configuration!\n"),
2166 "transport-tcp");
2167 GNUNET_SERVICE_stop (service);
2168 GNUNET_free_non_null(external_address);
2169 return NULL;
2170 }
2171
2172
1542 aport = 0; 2173 aport = 0;
1543 if ((GNUNET_OK != 2174 if ((GNUNET_OK !=
1544 GNUNET_CONFIGURATION_get_value_number (env->cfg, 2175 GNUNET_CONFIGURATION_get_value_number (env->cfg,
@@ -1557,14 +2188,44 @@ libgnunet_plugin_transport_tcp_init (void *cls)
1557 _ 2188 _
1558 ("Require valid port number for service `%s' in configuration!\n"), 2189 ("Require valid port number for service `%s' in configuration!\n"),
1559 "transport-tcp"); 2190 "transport-tcp");
2191 GNUNET_free_non_null(external_address);
2192 GNUNET_free_non_null(internal_address);
1560 GNUNET_SERVICE_stop (service); 2193 GNUNET_SERVICE_stop (service);
1561 return NULL; 2194 return NULL;
1562 } 2195 }
2196
2197 if (behind_nat)
2198 {
2199 if (GNUNET_YES != tcp_transport_start_nat_server(plugin))
2200 {
2201 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2202 "tcp",
2203 _
2204 ("Failed to start %s required for NAT in %s!\n"),
2205 "gnunet-nat-server"
2206 "transport-tcp");
2207 GNUNET_free_non_null(external_address);
2208 GNUNET_free_non_null(internal_address);
2209 GNUNET_SERVICE_stop (service);
2210 return NULL;
2211 }
2212 }
2213
2214 if (allow_nat)
2215 {
2216 plugin->nat_wait_conns = GNUNET_CONTAINER_multihashmap_create(100);
2217 GNUNET_assert(plugin->nat_wait_conns != NULL);
2218 }
2219
1563 if (aport == 0) 2220 if (aport == 0)
1564 aport = bport; 2221 aport = bport;
1565 plugin = GNUNET_malloc (sizeof (struct Plugin)); 2222 plugin = GNUNET_malloc (sizeof (struct Plugin));
1566 plugin->open_port = bport; 2223 plugin->open_port = bport;
1567 plugin->adv_port = aport; 2224 plugin->adv_port = aport;
2225 plugin->external_address = external_address;
2226 plugin->internal_address = internal_address;
2227 plugin->behind_nat = behind_nat;
2228 plugin->allow_nat = allow_nat;
1568 plugin->env = env; 2229 plugin->env = env;
1569 plugin->lsock = NULL; 2230 plugin->lsock = NULL;
1570 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); 2231 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
diff --git a/src/transport/plugin_transport_udp.c b/src/transport/plugin_transport_udp.c
index f36f587ad..10bbf2972 100644
--- a/src/transport/plugin_transport_udp.c
+++ b/src/transport/plugin_transport_udp.c
@@ -1296,15 +1296,14 @@ udp_transport_server_start (void *cls)
1296 socklen_t addrlen; 1296 socklen_t addrlen;
1297 int sockets_created; 1297 int sockets_created;
1298 1298
1299 /* Pipe to read from started processes stdout (on read end) */
1300 plugin->server_stdout = GNUNET_DISK_pipe(GNUNET_YES);
1301
1302 sockets_created = 0; 1299 sockets_created = 0;
1303 if (plugin->server_stdout == NULL)
1304 return sockets_created;
1305 1300
1306 if (plugin->behind_nat == GNUNET_YES) 1301 if (plugin->behind_nat == GNUNET_YES)
1307 { 1302 {
1303 /* Pipe to read from started processes stdout (on read end) */
1304 plugin->server_stdout = GNUNET_DISK_pipe(GNUNET_YES);
1305 if (plugin->server_stdout == NULL)
1306 return sockets_created;
1308#if DEBUG_UDP_NAT 1307#if DEBUG_UDP_NAT
1309 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 1308 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1310 "udp", 1309 "udp",
@@ -1410,32 +1409,6 @@ udp_transport_server_start (void *cls)
1410 1409
1411 1410
1412/** 1411/**
1413 * Check if the given port is plausible (must be either
1414 * our listen port or our advertised port). If it is
1415 * neither, we return one of these two ports at random.
1416 *
1417 * @return either in_port or a more plausible port
1418 */
1419static uint16_t
1420check_port (struct Plugin *plugin, uint16_t in_port)
1421{
1422
1423 /* FIXME: remember what ports we are using to better respond to this */
1424 return in_port;
1425 /*
1426 for (i = plugin->starting_port; i < plugin->num_ports + plugin->starting_port; i++)
1427 {
1428 if (in_port == i)
1429 return in_port;
1430 }
1431
1432 return GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1433 plugin->num_ports) + plugin->starting_port;
1434 */
1435}
1436
1437
1438/**
1439 * Another peer has suggested an address for this peer and transport 1412 * Another peer has suggested an address for this peer and transport
1440 * plugin. Check that this could be a valid address. This function 1413 * plugin. Check that this could be a valid address. This function
1441 * is not expected to 'validate' the address in the sense of trying to 1414 * is not expected to 'validate' the address in the sense of trying to
@@ -1468,12 +1441,12 @@ udp_check_address (void *cls, void *addr, size_t addrlen)
1468 if (addrlen == sizeof (struct sockaddr_in)) 1441 if (addrlen == sizeof (struct sockaddr_in))
1469 { 1442 {
1470 v4 = (struct sockaddr_in *) buf; 1443 v4 = (struct sockaddr_in *) buf;
1471 v4->sin_port = htons (check_port (plugin, ntohs (v4->sin_port))); 1444 v4->sin_port = htons (plugin->port);
1472 } 1445 }
1473 else 1446 else
1474 { 1447 {
1475 v6 = (struct sockaddr_in6 *) buf; 1448 v6 = (struct sockaddr_in6 *) buf;
1476 v6->sin6_port = htons (check_port (plugin, ntohs (v6->sin6_port))); 1449 v6->sin6_port = htons (plugin->port);
1477 } 1450 }
1478 1451
1479#if DEBUG_UDP_NAT 1452#if DEBUG_UDP_NAT
diff --git a/src/transport/test_transport_api_tcp_peer1.conf b/src/transport/test_transport_api_tcp_peer1.conf
index f5bdc2063..e3778cd8e 100644
--- a/src/transport/test_transport_api_tcp_peer1.conf
+++ b/src/transport/test_transport_api_tcp_peer1.conf
@@ -39,6 +39,7 @@ ACCEPT_FROM6 = ::1;
39ACCEPT_FROM = 127.0.0.1; 39ACCEPT_FROM = 127.0.0.1;
40NEIGHBOUR_LIMIT = 50 40NEIGHBOUR_LIMIT = 50
41BINARY = gnunet-service-transport 41BINARY = gnunet-service-transport
42#BINARY = /home/mrwiggles/documents/research/gnunet/gnunet-ng/src/transport/.libs/gnunet-service-transport
42CONFIG = $DEFAULTCONFIG 43CONFIG = $DEFAULTCONFIG
43HOME = $SERVICEHOME 44HOME = $SERVICEHOME
44HOSTNAME = localhost 45HOSTNAME = localhost
diff --git a/src/transport/test_transport_api_tcp_peer2.conf b/src/transport/test_transport_api_tcp_peer2.conf
index f2c22e336..6362ebd27 100644
--- a/src/transport/test_transport_api_tcp_peer2.conf
+++ b/src/transport/test_transport_api_tcp_peer2.conf
@@ -33,17 +33,18 @@ MINIMUM-FRIENDS = 0
33[transport] 33[transport]
34PLUGINS = tcp 34PLUGINS = tcp
35DEBUG = NO 35DEBUG = NO
36# PREFIX = 36#DEBUG = YES
37ACCEPT_FROM6 = ::1; 37ACCEPT_FROM6 = ::1;
38ACCEPT_FROM = 127.0.0.1; 38ACCEPT_FROM = 127.0.0.1;
39NEIGHBOUR_LIMIT = 50 39NEIGHBOUR_LIMIT = 50
40#BINARY = /home/mrwiggles/documents/research/gnunet/gnunet-ng/src/transport/.libs/gnunet-service-transport
40BINARY = gnunet-service-transport 41BINARY = gnunet-service-transport
41CONFIG = $DEFAULTCONFIG 42CONFIG = $DEFAULTCONFIG
42HOME = $SERVICEHOME 43HOME = $SERVICEHOME
43HOSTNAME = localhost 44HOSTNAME = localhost
44PORT = 22365 45PORT = 22365
45UNIXPATH = /tmp/gnunet-p2-service-transport.sock 46UNIXPATH = /tmp/gnunet-p2-service-transport.sock
46#PREFIX = xterm -T transport1 -e gdb --command=cmd --args 47#PREFIX = xterm -T transport2 -e gdb --command=cmd --args
47#PREFIX = valgrind --leak-check=full 48#PREFIX = valgrind --leak-check=full
48 49
49[peerinfo] 50[peerinfo]