/** * Create and initialize a listen socket for the server. * * @return NULL on error, otherwise the listen socket */ static struct GNUNET_NETWORK_Handle * open_listen_socket () { const static int on = 1; struct sockaddr_in sa; struct GNUNET_NETWORK_Handle *desc; memset (&sa, 0, sizeof (sa)); #if HAVE_SOCKADDR_IN_SIN_LEN sa.sin_len = sizeof (sa); #endif sa.sin_family = AF_INET; sa.sin_port = htons (PORT); desc = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0); GNUNET_assert (desc != 0); if (GNUNET_NETWORK_socket_setsockopt (desc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK) GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "setsockopt"); if (GNUNET_OK != GNUNET_NETWORK_socket_bind (desc, (const struct sockaddr *) &sa, sizeof (sa))) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "bind"); GNUNET_assert (0); } GNUNET_NETWORK_socket_listen (desc, 5); return desc; }
/** * Create an IPv4 listen socket bound to our port. * * @return NULL on error */ static struct GNUNET_NETWORK_Handle * bind_v4 () { struct GNUNET_NETWORK_Handle *ls; struct sockaddr_in sa4; int eno; memset (&sa4, 0, sizeof (sa4)); sa4.sin_family = AF_INET; sa4.sin_port = htons (port); #if HAVE_SOCKADDR_IN_SIN_LEN sa4.sin_len = sizeof (sa4); #endif ls = GNUNET_NETWORK_socket_create (AF_INET, SOCK_DGRAM, 0); if (NULL == ls) return NULL; if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa4, sizeof (sa4))) { eno = errno; GNUNET_NETWORK_socket_close (ls); errno = eno; return NULL; } return ls; }
/** * Open source port for sending DNS requests * * @param af AF_INET or AF_INET6 * @return GNUNET_OK on success */ static struct GNUNET_NETWORK_Handle * open_socket (int af) { struct sockaddr_in a4; struct sockaddr_in6 a6; struct sockaddr *sa; socklen_t alen; struct GNUNET_NETWORK_Handle *ret; ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0); if (NULL == ret) return NULL; switch (af) { case AF_INET: memset (&a4, 0, alen = sizeof (struct sockaddr_in)); sa = (struct sockaddr *) &a4; break; case AF_INET6: memset (&a6, 0, alen = sizeof (struct sockaddr_in6)); sa = (struct sockaddr *) &a6; break; default: GNUNET_break (0); GNUNET_NETWORK_socket_close (ret); return NULL; } sa->sa_family = af; if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret, sa, alen)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Could not bind to any port: %s\n"), STRERROR (errno)); GNUNET_NETWORK_socket_close (ret); return NULL; } return ret; }
/** * Test if the service is running. If we are given a UNIXPATH or a local address, * we do this NOT by trying to connect to the service, but by trying to BIND to * the same port. If the BIND fails, we know the service is running. * * @param service name of the service to wait for * @param cfg configuration to use * @param timeout how long to wait at most * @param task task to run if service is running * (reason will be "PREREQ_DONE" (service running) * or "TIMEOUT" (service not known to be running)) * @param task_cls closure for task */ void GNUNET_CLIENT_service_test (const char *service, const struct GNUNET_CONFIGURATION_Handle *cfg, struct GNUNET_TIME_Relative timeout, GNUNET_SCHEDULER_Task task, void *task_cls) { char *hostname; unsigned long long port; struct GNUNET_NETWORK_Handle *sock; struct GNUNET_CLIENT_Connection *client; LOG (GNUNET_ERROR_TYPE_DEBUG, "Testing if service `%s' is running.\n", service); #ifdef AF_UNIX { /* probe UNIX support */ struct sockaddr_un s_un; size_t slen; char *unixpath; unixpath = NULL; if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, service, "UNIXPATH", &unixpath)) && (0 < strlen (unixpath))) /* We have a non-NULL unixpath, does that mean it's valid? */ { if (strlen (unixpath) >= sizeof (s_un.sun_path)) { LOG (GNUNET_ERROR_TYPE_WARNING, _("UNIXPATH `%s' too long, maximum length is %llu\n"), unixpath, (unsigned long long) sizeof (s_un.sun_path)); unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath); LOG (GNUNET_ERROR_TYPE_INFO, _("Using `%s' instead\n"), unixpath); } } if (NULL != unixpath) { sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0); if (NULL != sock) { memset (&s_un, 0, sizeof (s_un)); s_un.sun_family = AF_UNIX; slen = strlen (unixpath) + 1; if (slen >= sizeof (s_un.sun_path)) slen = sizeof (s_un.sun_path) - 1; memcpy (s_un.sun_path, unixpath, slen); s_un.sun_path[slen] = '\0'; slen = sizeof (struct sockaddr_un); #if LINUX s_un.sun_path[0] = '\0'; #endif #if HAVE_SOCKADDR_IN_SIN_LEN s_un.sun_len = (u_char) slen; #endif if (GNUNET_OK != GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) &s_un, slen)) { /* failed to bind => service must be running */ GNUNET_free (unixpath); (void) GNUNET_NETWORK_socket_close (sock); GNUNET_SCHEDULER_add_continuation (task, task_cls, GNUNET_SCHEDULER_REASON_PREREQ_DONE); return; } (void) GNUNET_NETWORK_socket_close (sock); /* let's try IP */ } } GNUNET_free_non_null (unixpath); } #endif hostname = NULL; if ((GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, service, "PORT", &port)) || (port > 65535) || (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, service, "HOSTNAME", &hostname))) { /* UNIXPATH failed (if possible) AND IP failed => error */ service_test_error (task, task_cls); return; } if (0 == strcmp ("localhost", hostname) #if !LINUX && 0 #endif ) { /* can test using 'bind' */ struct sockaddr_in s_in; memset (&s_in, 0, sizeof (s_in)); #if HAVE_SOCKADDR_IN_SIN_LEN s_in.sin_len = sizeof (struct sockaddr_in); #endif s_in.sin_family = AF_INET; s_in.sin_port = htons (port); sock = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0); if (NULL != sock) { if (GNUNET_OK != GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) &s_in, sizeof (s_in))) { /* failed to bind => service must be running */ GNUNET_free (hostname); (void) GNUNET_NETWORK_socket_close (sock); GNUNET_SCHEDULER_add_continuation (task, task_cls, GNUNET_SCHEDULER_REASON_PREREQ_DONE); return; } (void) GNUNET_NETWORK_socket_close (sock); } } if (0 == strcmp ("ip6-localhost", hostname) #if !LINUX && 0 #endif ) { /* can test using 'bind' */ struct sockaddr_in6 s_in6; memset (&s_in6, 0, sizeof (s_in6)); #if HAVE_SOCKADDR_IN_SIN_LEN s_in6.sin6_len = sizeof (struct sockaddr_in6); #endif s_in6.sin6_family = AF_INET6; s_in6.sin6_port = htons (port); sock = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_STREAM, 0); if (NULL != sock) { if (GNUNET_OK != GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) &s_in6, sizeof (s_in6))) { /* failed to bind => service must be running */ GNUNET_free (hostname); (void) GNUNET_NETWORK_socket_close (sock); GNUNET_SCHEDULER_add_continuation (task, task_cls, GNUNET_SCHEDULER_REASON_PREREQ_DONE); return; } (void) GNUNET_NETWORK_socket_close (sock); } } if (((0 == strcmp ("localhost", hostname)) || (0 == strcmp ("ip6-localhost", hostname))) #if !LINUX && 0 #endif ) { /* all binds succeeded => claim service not running right now */ GNUNET_free_non_null (hostname); service_test_error (task, task_cls); return; } GNUNET_free_non_null (hostname); /* non-localhost, try 'connect' method */ client = GNUNET_CLIENT_connect (service, cfg); if (NULL == client) { LOG (GNUNET_ERROR_TYPE_INFO, _("Could not connect to service `%s', must not be running.\n"), service); service_test_error (task, task_cls); return; } client->test_cb = task; client->test_cb_cls = task_cls; client->test_deadline = GNUNET_TIME_relative_to_absolute (timeout); if (NULL == GNUNET_CLIENT_notify_transmit_ready (client, sizeof (struct GNUNET_MessageHeader), timeout, GNUNET_YES, &write_test, client)) { LOG (GNUNET_ERROR_TYPE_WARNING, _("Failure to transmit request to service `%s'\n"), service); service_test_error (task, task_cls); GNUNET_CLIENT_disconnect (client); return; } }
/** * Test if the service is running. If we are given a UNIXPATH or a * local address, we do this NOT by trying to connect to the service, * but by trying to BIND to the same port. If the BIND fails, we know * the service is running. * * @param service name of the service to wait for * @param cfg configuration to use * @param timeout how long to wait at most * @param cb function to call with the result * @param cb_cls closure for @a cb * @return handle to cancel the test */ struct GNUNET_CLIENT_TestHandle * GNUNET_CLIENT_service_test (const char *service, const struct GNUNET_CONFIGURATION_Handle *cfg, struct GNUNET_TIME_Relative timeout, GNUNET_CLIENT_TestResultCallback cb, void *cb_cls) { struct GNUNET_CLIENT_TestHandle *th; char *hostname; unsigned long long port; struct GNUNET_NETWORK_Handle *sock; th = GNUNET_new (struct GNUNET_CLIENT_TestHandle); th->cb = cb; th->cb_cls = cb_cls; th->test_deadline = GNUNET_TIME_relative_to_absolute (timeout); LOG (GNUNET_ERROR_TYPE_DEBUG, "Testing if service `%s' is running.\n", service); #ifdef AF_UNIX { /* probe UNIX support */ struct sockaddr_un s_un; char *unixpath; int abstract; unixpath = NULL; if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg, service, "UNIXPATH", &unixpath)) && (0 < strlen (unixpath))) /* We have a non-NULL unixpath, does that mean it's valid? */ { if (strlen (unixpath) >= sizeof (s_un.sun_path)) { LOG (GNUNET_ERROR_TYPE_WARNING, _("UNIXPATH `%s' too long, maximum length is %llu\n"), unixpath, (unsigned long long) sizeof (s_un.sun_path)); unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath); LOG (GNUNET_ERROR_TYPE_INFO, _("Using `%s' instead\n"), unixpath); } } #ifdef LINUX abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg, "TESTING", "USE_ABSTRACT_SOCKETS"); #else abstract = GNUNET_NO; #endif if ((NULL != unixpath) && (GNUNET_YES != abstract)) { if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (unixpath)) GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "mkdir", unixpath); } if (NULL != unixpath) { sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0); if (NULL != sock) { memset (&s_un, 0, sizeof (s_un)); s_un.sun_family = AF_UNIX; strncpy (s_un.sun_path, unixpath, sizeof (s_un.sun_path) - 1); if (GNUNET_YES == abstract) s_un.sun_path[0] = '\0'; #if HAVE_SOCKADDR_IN_SIN_LEN s_un.sun_len = (u_char) sizeof (struct sockaddr_un); #endif if (GNUNET_OK != GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) &s_un, sizeof (struct sockaddr_un))) { /* failed to bind => service must be running */ GNUNET_free (unixpath); (void) GNUNET_NETWORK_socket_close (sock); service_test_report (th, GNUNET_YES); return th; } (void) GNUNET_NETWORK_socket_close (sock); /* let's try IP */ } } GNUNET_free_non_null (unixpath); } #endif hostname = NULL; if ((GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, service, "PORT", &port)) || (port > 65535) || (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, service, "HOSTNAME", &hostname))) { /* UNIXPATH failed (if possible) AND IP failed => error */ service_test_report (th, GNUNET_SYSERR); return th; } if (0 == strcmp ("localhost", hostname) #if !LINUX && 0 #endif ) { /* can test using 'bind' */ struct sockaddr_in s_in; memset (&s_in, 0, sizeof (s_in)); #if HAVE_SOCKADDR_IN_SIN_LEN s_in.sin_len = sizeof (struct sockaddr_in); #endif s_in.sin_family = AF_INET; s_in.sin_port = htons (port); sock = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0); if (NULL != sock) { if (GNUNET_OK != GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) &s_in, sizeof (s_in))) { /* failed to bind => service must be running */ GNUNET_free (hostname); (void) GNUNET_NETWORK_socket_close (sock); service_test_report (th, GNUNET_YES); return th; } (void) GNUNET_NETWORK_socket_close (sock); } } if (0 == strcmp ("ip6-localhost", hostname) #if !LINUX && 0 #endif ) { /* can test using 'bind' */ struct sockaddr_in6 s_in6; memset (&s_in6, 0, sizeof (s_in6)); #if HAVE_SOCKADDR_IN_SIN_LEN s_in6.sin6_len = sizeof (struct sockaddr_in6); #endif s_in6.sin6_family = AF_INET6; s_in6.sin6_port = htons (port); sock = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_STREAM, 0); if (NULL != sock) { if (GNUNET_OK != GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) &s_in6, sizeof (s_in6))) { /* failed to bind => service must be running */ GNUNET_free (hostname); (void) GNUNET_NETWORK_socket_close (sock); service_test_report (th, GNUNET_YES); return th; } (void) GNUNET_NETWORK_socket_close (sock); } } if (((0 == strcmp ("localhost", hostname)) || (0 == strcmp ("ip6-localhost", hostname))) #if !LINUX && 0 #endif ) { /* all binds succeeded => claim service not running right now */ GNUNET_free_non_null (hostname); service_test_report (th, GNUNET_NO); return th; } GNUNET_free_non_null (hostname); /* non-localhost, try 'connect' method */ th->client = GNUNET_CLIENT_connect (service, cfg); if (NULL == th->client) { LOG (GNUNET_ERROR_TYPE_INFO, _("Could not connect to service `%s', configuration broken.\n"), service); service_test_report (th, GNUNET_SYSERR); return th; } th->th = GNUNET_CLIENT_notify_transmit_ready (th->client, sizeof (struct GNUNET_MessageHeader), timeout, GNUNET_YES, &write_test, th); if (NULL == th->th) { LOG (GNUNET_ERROR_TYPE_WARNING, _("Failure to transmit request to service `%s'\n"), service); service_test_report (th, GNUNET_SYSERR); return th; } return th; }
/** * Creating a listening socket for each of the service's addresses and * wait for the first incoming connection to it * * @param sa address associated with the service * @param addr_len length of sa * @param sl service entry for the service in question */ static void create_listen_socket (struct sockaddr *sa, socklen_t addr_len, struct ServiceList *sl) { static int on = 1; struct GNUNET_NETWORK_Handle *sock; struct ServiceListeningInfo *sli; switch (sa->sa_family) { case AF_INET: sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0); break; case AF_INET6: sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); break; case AF_UNIX: if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0) /* Do not bind to blank UNIX path! */ return; sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0); break; default: GNUNET_break (0); sock = NULL; errno = EAFNOSUPPORT; break; } if (NULL == sock) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Unable to create socket for service `%s': %s\n"), sl->name, STRERROR (errno)); GNUNET_free (sa); return; } if (GNUNET_NETWORK_socket_setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK) GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "setsockopt"); #ifdef IPV6_V6ONLY if ((sa->sa_family == AF_INET6) && (GNUNET_NETWORK_socket_setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK)) GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "setsockopt"); #endif if (GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) sa, addr_len) != GNUNET_OK) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Unable to bind listening socket for service `%s' to address `%s': %s\n"), sl->name, GNUNET_a2s (sa, addr_len), STRERROR (errno)); GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); GNUNET_free (sa); return; } if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen"); GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); GNUNET_free (sa); return; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("ARM now monitors connections to service `%s' at `%s'\n"), sl->name, GNUNET_a2s (sa, addr_len)); sli = GNUNET_malloc (sizeof (struct ServiceListeningInfo)); sli->service_addr = sa; sli->service_addr_len = addr_len; sli->listen_socket = sock; sli->sl = sl; sli->accept_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock, &accept_connection, sli); GNUNET_CONTAINER_DLL_insert (sl->listen_head, sl->listen_tail, sli); }
/** * Main function that will be run. * * @param cls closure * @param args remaining command-line arguments * @param cfgfile name of the configuration file used (for saving, can be NULL!) * @param c configuration */ static void run (void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *c) { uint8_t af; struct sockaddr *local_sa; struct sockaddr *remote_sa; socklen_t local_len; size_t remote_len; if (use_tcp && use_udp) { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Cannot use TCP and UDP\n"); global_ret = 1; return; } proto = 0; if (use_tcp) proto = IPPROTO_TCP; if (use_udp) proto = IPPROTO_UDP; GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); if (0 == proto) { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Must specify either TCP or UDP\n"); global_ret = 1; return; } if (NULL != local_addr) { local_len = (socklen_t) GNUNET_STRINGS_parse_socket_addr (local_addr, &af, &local_sa); if (0 == local_len) { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Invalid socket address `%s'\n", local_addr); global_ret = 1; return; } } if (NULL != remote_addr) { remote_len = GNUNET_STRINGS_parse_socket_addr (remote_addr, &af, &remote_sa); if (0 == remote_len) { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Invalid socket address `%s'\n", remote_addr); global_ret = 1; return; } } if (NULL != local_addr) { if (NULL == section_name) section_name = GNUNET_strdup ("undefined"); nh = GNUNET_NAT_register (c, section_name, proto, 1, (const struct sockaddr **) &local_sa, &local_len, &address_cb, (listen_reversal) ? &reversal_cb : NULL, NULL); } else if (listen_reversal) { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Use of `-W` only effective in combination with `-i`\n"); global_ret = 1; GNUNET_SCHEDULER_shutdown (); return; } if (NULL != remote_addr) { int ret; if ( (NULL == nh) || (sizeof (struct sockaddr_in) != local_len) ) { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Require IPv4 local address to initiate connection reversal\n"); global_ret = 1; GNUNET_SCHEDULER_shutdown (); return; } if (sizeof (struct sockaddr_in) != remote_len) { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Require IPv4 reversal target address\n"); global_ret = 1; GNUNET_SCHEDULER_shutdown (); return; } GNUNET_assert (AF_INET == local_sa->sa_family); GNUNET_assert (AF_INET == remote_sa->sa_family); ret = GNUNET_NAT_request_reversal (nh, (const struct sockaddr_in *) local_sa, (const struct sockaddr_in *) remote_sa); switch (ret) { case GNUNET_SYSERR: GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Connection reversal internal error\n"); break; case GNUNET_NO: GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Connection reversal unavailable\n"); break; case GNUNET_OK: /* operation in progress */ break; } } if (do_stun) { if (NULL == local_addr) { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Require local address to support STUN requests\n"); global_ret = 1; GNUNET_SCHEDULER_shutdown (); return; } if (IPPROTO_UDP != proto) { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "STUN only supported over UDP\n"); global_ret = 1; GNUNET_SCHEDULER_shutdown (); return; } ls = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, IPPROTO_UDP); if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls, local_sa, local_len)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to bind to %s: %s\n", GNUNET_a2s (local_sa, local_len), STRERROR (errno)); global_ret = 1; GNUNET_SCHEDULER_shutdown (); return; } rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, ls, &stun_read_task, NULL); } test_finished (); }
/** * Start testing if NAT traversal works using the * given configuration (IPv4-only). * * ALL failures are reported directly to the report callback * * @param cfg configuration for the NAT traversal * @param is_tcp #GNUNET_YES to test TCP, #GNUNET_NO to test UDP * @param bnd_port port to bind to, 0 for connection reversal * @param adv_port externally advertised port to use * @param timeout delay after which the test should be aborted * @param report function to call with the result of the test * @param report_cls closure for @a report * @return handle to cancel NAT test or NULL. The error is always indicated via the report callback */ struct GNUNET_NAT_Test * GNUNET_NAT_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg, int is_tcp, uint16_t bnd_port, uint16_t adv_port, struct GNUNET_TIME_Relative timeout, GNUNET_NAT_TestCallback report, void *report_cls) { struct GNUNET_NAT_Test *nh; struct sockaddr_in sa; const struct sockaddr *addrs[] = { (const struct sockaddr *) &sa }; const socklen_t addrlens[] = { sizeof (sa) }; memset (&sa, 0, sizeof (sa)); sa.sin_family = AF_INET; sa.sin_port = htons (bnd_port); #if HAVE_SOCKADDR_IN_SIN_LEN sa.sin_len = sizeof (sa); #endif nh = GNUNET_new (struct GNUNET_NAT_Test); nh->cfg = cfg; nh->is_tcp = is_tcp; nh->data = bnd_port; nh->adv_port = adv_port; nh->report = report; nh->report_cls = report_cls; nh->status = GNUNET_NAT_ERROR_SUCCESS; if (0 == bnd_port) { nh->nat = GNUNET_NAT_register (cfg, is_tcp, 0, 0, NULL, NULL, &addr_cb, &reversal_cb, nh, NULL); } else { nh->lsock = GNUNET_NETWORK_socket_create (AF_INET, (is_tcp == GNUNET_YES) ? SOCK_STREAM : SOCK_DGRAM, 0); if ((nh->lsock == NULL) || (GNUNET_OK != GNUNET_NETWORK_socket_bind (nh->lsock, (const struct sockaddr *) &sa, sizeof (sa)))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to create listen socket bound to `%s' for NAT test: %s\n"), GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)), STRERROR (errno)); if (NULL != nh->lsock) { GNUNET_NETWORK_socket_close (nh->lsock); nh->lsock = NULL; } nh->status = GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR; nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout, nh); return nh; } if (GNUNET_YES == is_tcp) { GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_listen (nh->lsock, 5)); nh->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, nh->lsock, &do_accept, nh); } else { nh->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, nh->lsock, &do_udp_read, nh); } LOG (GNUNET_ERROR_TYPE_INFO, "NAT test listens on port %u (%s)\n", bnd_port, (GNUNET_YES == is_tcp) ? "tcp" : "udp"); nh->nat = GNUNET_NAT_register (cfg, is_tcp, adv_port, 1, addrs, addrlens, &addr_cb, NULL, nh, NULL); if (NULL == nh->nat) { LOG (GNUNET_ERROR_TYPE_INFO, _("NAT test failed to start NAT library\n")); if (NULL != nh->ltask) { GNUNET_SCHEDULER_cancel (nh->ltask); nh->ltask = NULL; } if (NULL != nh->lsock) { GNUNET_NETWORK_socket_close (nh->lsock); nh->lsock = NULL; } nh->status = GNUNET_NAT_ERROR_NAT_REGISTER_FAILED; nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout, nh); return nh; } } nh->ttask = GNUNET_SCHEDULER_add_delayed (timeout, &do_timeout, nh); return nh; }