From: Christian Grothoff Date: Wed, 10 Nov 2010 08:08:37 +0000 (+0000) Subject: sockets patch from #1616 X-Git-Tag: initial-import-from-subversion-38251~19782 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=c096eaed42b83fcd4afc87d3d24fe31975a4b62a;p=oweals%2Fgnunet.git sockets patch from #1616 --- diff --git a/src/arm/gnunet-service-arm_interceptor.c b/src/arm/gnunet-service-arm_interceptor.c index 56268b2d0..2dd4f3703 100644 --- a/src/arm/gnunet-service-arm_interceptor.c +++ b/src/arm/gnunet-service-arm_interceptor.c @@ -187,9 +187,15 @@ struct ForwardedConnection * Have we ever successfully written data to the service? */ int first_write_done; -}; + /** + * Service connection attempts + */ + struct ServiceListeningInfo *service_connect_ipv4; + struct ServiceListeningInfo *service_connect_ipv6; +}; + /** * Array with the names of the services started by default. */ @@ -694,6 +700,10 @@ receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) &forwardToService, fc); } +static void fc_acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc, int is_ipv4, int is_ipv6); +static void fc_acceptConnection_ipv4 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); +static void fc_acceptConnection_ipv6 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); +static int service_try_to_connect (struct sockaddr *addr, socklen_t addrlen, struct ForwardedConnection *fc); /** * @@ -704,99 +714,248 @@ start_forwarding (void *cls, { struct ForwardedConnection *fc = cls; struct GNUNET_TIME_Relative rem; + int fail = 0; + int failures = 0; + int is_zero = 0, is_ipv6 = 0, is_ipv4 = 0; + struct sockaddr_in *target_ipv4; + struct sockaddr_in6 *target_ipv6; + + int free_ipv4 = 1, free_ipv6 = 1; + char listen_address[128]; + uint16_t listening_port; fc->start_task = GNUNET_SCHEDULER_NO_TASK; if ( (NULL != tc) && (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Unable to forward to service `%s': shutdown\n"), - fc->listen_info->serviceName); - closeClientAndServiceSockets (fc, REASON_ERROR); - return; - } + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Unable to forward to service `%s': shutdown\n"), + fc->listen_info->serviceName); + closeClientAndServiceSockets (fc, REASON_ERROR); + return; + } rem = GNUNET_TIME_absolute_get_remaining (fc->timeout); if (rem.rel_value == 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Unable to forward to service `%s': timeout before connect\n"), + fc->listen_info->serviceName); + closeClientAndServiceSockets (fc, REASON_ERROR); + return; + } + target_ipv4 = GNUNET_malloc (sizeof (struct sockaddr_in)); + target_ipv6 = GNUNET_malloc (sizeof (struct sockaddr_in6)); + + switch (fc->listen_info->service_addr->sa_family) + { + case AF_INET: + inet_ntop (fc->listen_info->service_addr->sa_family, (const void *) &((struct sockaddr_in *) fc->listen_info->service_addr)->sin_addr, listen_address, INET_ADDRSTRLEN); + if (strncmp (listen_address, "0.0.0.0:", 8) == 0 || strncmp (listen_address, "0.0.0.0", 7) == 0) + is_zero = 1; + is_ipv4 = 1; + listening_port = ((struct sockaddr_in *)fc->listen_info->service_addr)->sin_port; + break; + case AF_INET6: + inet_ntop (fc->listen_info->service_addr->sa_family, (const void *) &((struct sockaddr_in6 *) fc->listen_info->service_addr)->sin6_addr, listen_address, INET6_ADDRSTRLEN); + if (strncmp (listen_address, "[::]:", 5) == 0 || strncmp (listen_address, "::", 2) == 0) + is_zero = 1; + is_ipv6 = 1; + listening_port = ((struct sockaddr_in6 *)fc->listen_info->service_addr)->sin6_port; + break; + default: + break; + } + + fc->service_connect_ipv4 = NULL; + fc->service_connect_ipv6 = NULL; + if (is_zero) + { + /* connect to [::1] and 127.0.0.1 instead of [::] and 0.0.0.0 */ + inet_pton (AF_INET, "127.0.0.1", &target_ipv4->sin_addr); + target_ipv4->sin_family = AF_INET; + target_ipv4->sin_port = listening_port; + is_ipv4 = 1; + free_ipv4 = 0; + + inet_pton (AF_INET6, "0:0:0:0:0:0:0:1", &target_ipv6->sin6_addr); + target_ipv6->sin6_family = AF_INET6; + target_ipv6->sin6_port = listening_port; + is_ipv6 = 1; + free_ipv6 = 0; + } + else + { + if (is_ipv4) + memcpy (target_ipv4, fc->listen_info->service_addr, sizeof (struct sockaddr_in)); + else if (is_ipv6) + memcpy (target_ipv6, fc->listen_info->service_addr, sizeof (struct sockaddr_in6)); + } + + if (is_ipv4) + failures += free_ipv4 = service_try_to_connect ((struct sockaddr *) target_ipv4, sizeof (struct sockaddr_in), fc); + if (is_ipv6) + failures += free_ipv6 = service_try_to_connect ((struct sockaddr *) target_ipv6, sizeof (struct sockaddr_in6), fc); + + if (is_ipv4 + is_ipv6 <= failures) + fail = 1; + + if (free_ipv4) + GNUNET_free (target_ipv4); + if (free_ipv6) + GNUNET_free (target_ipv6); + + if (fail) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to start service `%s': %s\n"), + fc->listen_info->serviceName, + STRERROR (errno)); + closeClientAndServiceSockets (fc, REASON_ERROR); + return; + } +} + +static void +fc_acceptConnection_ipv4 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + fc_acceptConnection (cls, tc, 1, 0); +} + +static void +fc_acceptConnection_ipv6 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + fc_acceptConnection (cls, tc, 0, 1); +} + +static void +fc_acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc, int is_ipv4, int is_ipv6) +{ + struct ForwardedConnection *fc = cls; + struct ServiceListeningInfo *sli; + + if (is_ipv4) + sli = fc->service_connect_ipv4; + else if (is_ipv6) + sli = fc->service_connect_ipv6; + else + GNUNET_break (0); + + if ((tc->reason & (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT | GNUNET_SCHEDULER_REASON_PREREQ_DONE)) || ((tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY) && fc->armServiceSocket)) + { + GNUNET_NETWORK_socket_close (sli->listeningSocket); + if (is_ipv4) + fc->service_connect_ipv4 = NULL; + else if (is_ipv6) + fc->service_connect_ipv6 = NULL; + } + else if (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY) + { +#if DEBUG_SERVICE_MANAGER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connected to service, now starting forwarding\n"); +#endif + fc->armServiceSocket = sli->listeningSocket; + if ((is_ipv4 && fc->service_connect_ipv6 != NULL)) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Unable to forward to service `%s': timeout before connect\n"), - fc->listen_info->serviceName); - closeClientAndServiceSockets (fc, REASON_ERROR); - return; - } - fc->armServiceSocket = - GNUNET_NETWORK_socket_create (fc->listen_info->service_addr->sa_family, - SOCK_STREAM, 0); - if (NULL == fc->armServiceSocket) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to start service `%s': %s\n"), - fc->listen_info->serviceName, - STRERROR (errno)); - closeClientAndServiceSockets (fc, REASON_ERROR); - return; + GNUNET_SCHEDULER_cancel (fc->service_connect_ipv6->acceptTask); + fc->service_connect_ipv6->acceptTask = GNUNET_SCHEDULER_add_now (fc_acceptConnection_ipv6, fc); } - if ( (GNUNET_SYSERR == - GNUNET_NETWORK_socket_connect (fc->armServiceSocket, - fc->listen_info->service_addr, - fc->listen_info->service_addr_len)) && - (errno != EINPROGRESS) ) + else if (is_ipv6 && fc->service_connect_ipv4 != NULL) { - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (fc->armServiceSocket)); - fc->armServiceSocket = NULL; - fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2); - #if DEBUG_SERVICE_MANAGER - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Failed to connected to service `%s' at `%s', will try again in %llu ms\n", - fc->listen_info->serviceName, - GNUNET_a2s (fc->listen_info->service_addr, - fc->listen_info->service_addr_len), - (unsigned long long) GNUNET_TIME_relative_min (fc->back_off, - rem).rel_value); -#endif - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task); - fc->start_task - = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_min (fc->back_off, - rem), - &start_forwarding, - fc); - return; + GNUNET_SCHEDULER_cancel (fc->service_connect_ipv4->acceptTask); + fc->service_connect_ipv4->acceptTask = GNUNET_SCHEDULER_add_now (fc_acceptConnection_ipv4, fc); } -#if DEBUG_SERVICE_MANAGER - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Connected to service, now starting forwarding\n"); -#endif - if (fc->client_to_service_task == GNUNET_SCHEDULER_NO_TASK) + GNUNET_free (fc->listen_info->service_addr); + fc->listen_info->service_addr = sli->service_addr; + fc->listen_info->service_addr_len = sli->service_addr_len; + /*fc->listen_info->listeningSocket is it closed already?*/ + if (fc->client_to_service_task == GNUNET_SCHEDULER_NO_TASK) { if (fc->client_to_service_bufferDataLength == 0) - fc->client_to_service_task = - GNUNET_SCHEDULER_add_read_net ( + fc->client_to_service_task = + GNUNET_SCHEDULER_add_read_net ( GNUNET_TIME_UNIT_FOREVER_REL, fc->armClientSocket, &receiveFromClient, fc); else - fc->client_to_service_task = - GNUNET_SCHEDULER_add_write_net ( + fc->client_to_service_task = + GNUNET_SCHEDULER_add_write_net ( GNUNET_TIME_UNIT_FOREVER_REL, fc->armServiceSocket, &forwardToService, fc); } - if (fc->service_to_client_task == GNUNET_SCHEDULER_NO_TASK) + if (fc->service_to_client_task == GNUNET_SCHEDULER_NO_TASK) { if (fc->service_to_client_bufferDataLength == 0) - fc->service_to_client_task = - GNUNET_SCHEDULER_add_read_net ( + fc->service_to_client_task = + GNUNET_SCHEDULER_add_read_net ( GNUNET_TIME_UNIT_FOREVER_REL, fc->armServiceSocket, &receiveFromService, fc); else - fc->service_to_client_task = - GNUNET_SCHEDULER_add_write_net ( + fc->service_to_client_task = + GNUNET_SCHEDULER_add_write_net ( GNUNET_TIME_UNIT_FOREVER_REL, fc->armClientSocket, &forwardToClient, fc); } + } + else + { + GNUNET_break (0); + } + GNUNET_free (sli); +} + +static int +service_try_to_connect (struct sockaddr *addr, socklen_t addrlen, struct ForwardedConnection *fc) +{ + struct GNUNET_NETWORK_Handle *sock; + struct ServiceListeningInfo *serviceListeningInfo; + + sock = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0); + if (sock == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to create a socket\n"); + return 1; + } + + if ( (GNUNET_SYSERR == GNUNET_NETWORK_socket_connect (sock, addr, addrlen)) && + (errno != EINPROGRESS) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect\n"); + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); + return 1; + } + + serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo)); + serviceListeningInfo->serviceName = NULL; + serviceListeningInfo->service_addr = addr; + serviceListeningInfo->service_addr_len = addrlen; + serviceListeningInfo->listeningSocket = sock; + + switch (addrlen) + { + case sizeof (struct sockaddr_in): + fc->service_connect_ipv4 = serviceListeningInfo; + serviceListeningInfo->acceptTask = + GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, + serviceListeningInfo->listeningSocket, + &fc_acceptConnection_ipv4, fc); + break; + case sizeof (struct sockaddr_in6): + fc->service_connect_ipv6 = serviceListeningInfo; + serviceListeningInfo->acceptTask = + GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, + serviceListeningInfo->listeningSocket, + &fc_acceptConnection_ipv6, fc); + break; + default: + GNUNET_break (0); + return 1; + break; + } + return 0; }