2 This file is part of GNUnet.
3 (C) 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 arm/gnunet-service-arm_interceptor.c
22 * @brief listen to incoming connections from clients to services,
23 * start services for which incoming an incoming connection occur,
24 * and relay communication between the client and the service for
25 * that first incoming connection.
27 * @author Safey Abdel Halim
28 * @author Christian Grothoff
32 #include "gnunet_service_lib.h"
33 #include "gnunet_configuration_lib.h"
34 #include "gnunet_constants.h"
35 #include "gnunet_client_lib.h"
36 #include "gnunet_container_lib.h"
37 #include "gnunet-service-arm.h"
40 #define DEBUG_SERVICE_MANAGER GNUNET_NO
42 #define BUFFER_SIZE (64 * 1024)
45 * Problem forwarding from client to service.
47 #define REASON_CLIENT_TO_SERVICE 1
50 * Problem forwarding from service to client.
52 #define REASON_SERVICE_TO_CLIENT 2
55 * Problem in both directions.
57 #define REASON_ERROR 3
63 struct ServiceListeningInfo
66 * This is a linked list.
68 struct ServiceListeningInfo *next;
71 * This is a linked list.
73 struct ServiceListeningInfo *prev;
76 * Name of the service being forwarded.
83 struct sockaddr *service_addr;
88 socklen_t service_addr_len;
91 * Our listening socket.
93 struct GNUNET_NETWORK_Handle *listeningSocket;
96 * Task doing the accepting.
98 GNUNET_SCHEDULER_TaskIdentifier acceptTask;
102 * Information of the connection: client-arm-service
104 struct ForwardedConnection
109 struct GNUNET_NETWORK_Handle *armClientSocket;
114 struct GNUNET_NETWORK_Handle *armServiceSocket;
119 struct ServiceListeningInfo *listen_info;
124 char service_to_client_buffer[BUFFER_SIZE];
129 char client_to_service_buffer[BUFFER_SIZE];
134 char client_addr[32];
139 const char *client_to_service_bufferPos;
144 const char *service_to_client_bufferPos;
147 * Timeout for forwarding.
149 struct GNUNET_TIME_Absolute timeout;
152 * Current back-off value.
154 struct GNUNET_TIME_Relative back_off;
157 * Task that tries to initiate forwarding.
159 GNUNET_SCHEDULER_TaskIdentifier start_task;
164 GNUNET_SCHEDULER_TaskIdentifier client_to_service_task;
169 GNUNET_SCHEDULER_TaskIdentifier service_to_client_task;
174 ssize_t client_to_service_bufferDataLength;
179 ssize_t service_to_client_bufferDataLength;
184 socklen_t client_addr_len;
187 * Have we ever successfully written data to the service?
189 int first_write_done;
192 * Service connection attempts for IPv4
194 struct ServiceListeningInfo *service_connect_ipv4;
197 * Service connection attempts for IPv6
199 struct ServiceListeningInfo *service_connect_ipv6;
203 * Array with the names of the services started by default.
205 static char **defaultServicesList;
208 * Size of the defaultServicesList array.
210 static unsigned int numDefaultServices;
215 static const struct GNUNET_CONFIGURATION_Handle *cfg;
220 static struct ServiceListeningInfo *serviceListeningInfoList_head;
225 static struct ServiceListeningInfo *serviceListeningInfoList_tail;
229 * Put the default services represented by a space separated string into an array of strings
231 * @param services space separated string of default services
234 addDefaultServicesToList (const char *services)
240 if (strlen (services) == 0)
242 s = GNUNET_strdup (services);
243 token = strtok (s, " ");
244 while (NULL != token)
246 numDefaultServices++;
247 token = strtok (NULL, " ");
251 defaultServicesList = GNUNET_malloc (numDefaultServices * sizeof (char *));
253 s = GNUNET_strdup (services);
254 token = strtok (s, " ");
255 while (NULL != token)
257 defaultServicesList[i++] = GNUNET_strdup (token);
258 token = strtok (NULL, " ");
261 GNUNET_assert (i == numDefaultServices);
265 * Checks whether the serviceName is in the list of default services
267 * @param serviceName string to check its existance in the list
268 * @return GNUNET_YES if the service is started by default
271 isInDefaultList (const char *serviceName)
274 for (i = 0; i < numDefaultServices; i++)
275 if (strcmp (serviceName, defaultServicesList[i]) == 0)
282 * Close forwarded connection (partial or full).
284 * @param fc connection to close
285 * @param reason which direction to close
288 closeClientAndServiceSockets (struct ForwardedConnection *fc,
291 if (0 != (REASON_SERVICE_TO_CLIENT & reason))
293 #if DEBUG_SERVICE_MANAGER
294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
295 "Stopping forwarding from service to client\n",
296 fc->listen_info->serviceName);
298 if (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK)
300 GNUNET_SCHEDULER_cancel (fc->service_to_client_task);
301 fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
303 if (fc->armClientSocket != NULL)
304 GNUNET_NETWORK_socket_shutdown (fc->armClientSocket,
306 if (fc->armServiceSocket != NULL)
307 GNUNET_NETWORK_socket_shutdown (fc->armServiceSocket,
310 if (0 != (REASON_CLIENT_TO_SERVICE & reason))
312 #if DEBUG_SERVICE_MANAGER
313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
314 "Stopping forwarding from client to service\n",
315 fc->listen_info->serviceName);
317 if (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK)
319 GNUNET_SCHEDULER_cancel ( fc->client_to_service_task);
320 fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
322 if (fc->armClientSocket != NULL)
323 GNUNET_NETWORK_socket_shutdown (fc->armClientSocket,
325 if (fc->armServiceSocket != NULL)
326 GNUNET_NETWORK_socket_shutdown (fc->armServiceSocket,
329 if ( (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK) ||
330 (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK) )
332 #if DEBUG_SERVICE_MANAGER
333 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
334 "Closing forwarding connection (done with both directions)\n");
336 if (fc->start_task != GNUNET_SCHEDULER_NO_TASK)
337 GNUNET_SCHEDULER_cancel ( fc->start_task);
338 if ( (NULL != fc->armClientSocket) &&
340 GNUNET_NETWORK_socket_close (fc->armClientSocket)) )
341 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
342 if ( (NULL != fc->armServiceSocket) &&
344 GNUNET_NETWORK_socket_close (fc->armServiceSocket)) )
345 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
346 GNUNET_free (fc->listen_info->serviceName);
347 GNUNET_free (fc->listen_info->service_addr);
348 GNUNET_free (fc->listen_info);
354 * Read data from the client and then forward it to the service.
356 * @param cls callback data, struct ForwardedConnection for the communication between client and service
360 receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
364 * Receive service messages sent by the service and forward it to client
366 * @param cls callback data, struct ForwardedConnection for the communication between client and service
367 * @param tc scheduler context
370 receiveFromService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
377 start_forwarding (void *cls,
378 const struct GNUNET_SCHEDULER_TaskContext *tc);
383 * Forward messages sent from service to client
385 * @param cls callback data, struct ForwardedConnection for the communication between client and service
389 forwardToClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
391 struct ForwardedConnection *fc = cls;
392 ssize_t numberOfBytesSent;
394 fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
395 if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->write_ready,
396 fc->armClientSocket))
398 fc->service_to_client_task =
399 GNUNET_SCHEDULER_add_write_net (
400 GNUNET_TIME_UNIT_FOREVER_REL,
402 &forwardToClient, fc);
405 /* Forwarding service response to client */
407 GNUNET_NETWORK_socket_send (fc->armClientSocket,
408 fc->service_to_client_bufferPos,
409 fc->service_to_client_bufferDataLength);
410 if (numberOfBytesSent <= 0)
412 if ( (errno != EPIPE) &&
413 (errno != ECONNRESET) )
414 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
415 "Failed to forward %u bytes of data to client: %s\n",
416 fc->service_to_client_bufferDataLength,
418 closeClientAndServiceSockets (fc,
419 REASON_SERVICE_TO_CLIENT);
422 #if DEBUG_SERVICE_MANAGER
423 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
424 "Forwarded %d bytes to client\n",
427 if (numberOfBytesSent < fc->service_to_client_bufferDataLength)
429 fc->service_to_client_bufferPos += numberOfBytesSent;
430 fc->service_to_client_bufferDataLength -= numberOfBytesSent;
431 fc->service_to_client_task =
432 GNUNET_SCHEDULER_add_write_net (
433 GNUNET_TIME_UNIT_FOREVER_REL,
439 fc->service_to_client_task =
440 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
441 fc->armServiceSocket,
448 * Receive service messages sent by the service and forward it to client
450 * @param cls callback data, struct ForwardedConnection for the communication between client and service
451 * @param tc scheduler context
454 receiveFromService (void *cls,
455 const struct GNUNET_SCHEDULER_TaskContext *tc)
457 struct ForwardedConnection *fc = cls;
458 struct GNUNET_TIME_Relative rem;
460 fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
461 if ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
462 (fc->first_write_done != GNUNET_YES) )
464 closeClientAndServiceSockets (fc, REASON_ERROR);
467 if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->read_ready,
468 fc->armServiceSocket))
470 fc->service_to_client_task =
471 GNUNET_SCHEDULER_add_read_net (
472 GNUNET_TIME_UNIT_FOREVER_REL,
473 fc->armServiceSocket,
474 &receiveFromService, fc);
477 fc->service_to_client_bufferPos = fc->service_to_client_buffer;
478 fc->service_to_client_bufferDataLength =
479 GNUNET_NETWORK_socket_recv (fc->armServiceSocket,
480 fc->service_to_client_buffer,
482 if (fc->service_to_client_bufferDataLength <= 0)
484 #if DEBUG_SERVICE_MANAGER
485 if (fc->service_to_client_bufferDataLength == 0)
487 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
488 "Service `%s' stopped sending data.\n",
489 fc->listen_info->serviceName);
492 if (fc->first_write_done != GNUNET_YES)
494 fc->service_to_client_bufferDataLength = 0;
495 GNUNET_break (GNUNET_OK ==
496 GNUNET_NETWORK_socket_close (fc->armServiceSocket));
497 fc->armServiceSocket = NULL;
498 if ( (fc->client_to_service_bufferDataLength > 0) &&
499 (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK) )
501 GNUNET_SCHEDULER_cancel (fc->client_to_service_task);
502 fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
504 fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
505 #if DEBUG_SERVICE_MANAGER
506 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
507 "Failed to connected to service `%s' at `%s', will try again in %llu ms\n",
508 fc->listen_info->serviceName,
509 GNUNET_a2s (fc->listen_info->service_addr,
510 fc->listen_info->service_addr_len),
511 (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
514 rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
515 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
517 = GNUNET_SCHEDULER_add_delayed (
518 GNUNET_TIME_relative_min (fc->back_off,
525 #if DEBUG_SERVICE_MANAGER
526 if (fc->service_to_client_bufferDataLength != 0)
527 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
528 "Error receiving from service: %s\n",
531 closeClientAndServiceSockets (fc, REASON_SERVICE_TO_CLIENT);
535 fc->first_write_done = GNUNET_YES;
536 #if DEBUG_SERVICE_MANAGER
537 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
538 "Received %d bytes for client\n",
539 fc->service_to_client_bufferDataLength);
541 fc->service_to_client_task =
542 GNUNET_SCHEDULER_add_write_net (
543 GNUNET_TIME_UNIT_FOREVER_REL,
545 &forwardToClient, fc);
550 * Forward client message to service
552 * @param cls callback data, struct ForwardedConnection for the communication between client and service
553 * @param tc scheduler context
556 forwardToService (void *cls,
557 const struct GNUNET_SCHEDULER_TaskContext *tc)
559 struct ForwardedConnection *fc = cls;
560 ssize_t numberOfBytesSent;
561 struct GNUNET_TIME_Relative rem;
563 fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
564 if ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
565 (fc->first_write_done != GNUNET_YES) )
567 closeClientAndServiceSockets (fc, REASON_ERROR);
570 if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->write_ready,
571 fc->armServiceSocket))
573 fc->client_to_service_task =
574 GNUNET_SCHEDULER_add_write_net (
575 GNUNET_TIME_UNIT_FOREVER_REL,
576 fc->armServiceSocket,
577 &forwardToService, fc);
581 GNUNET_NETWORK_socket_send (fc->armServiceSocket,
582 fc->client_to_service_bufferPos,
583 fc->client_to_service_bufferDataLength);
584 if (numberOfBytesSent <= 0)
586 if (GNUNET_YES != fc->first_write_done)
588 GNUNET_break (GNUNET_OK ==
589 GNUNET_NETWORK_socket_close (fc->armServiceSocket));
590 fc->armServiceSocket = NULL;
591 if ( (fc->service_to_client_bufferDataLength == 0) &&
592 (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK) )
594 GNUNET_SCHEDULER_cancel (fc->service_to_client_task);
595 fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
597 fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
598 #if DEBUG_SERVICE_MANAGER
599 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
600 "Failed to connect to service `%s' at `%s', will try again in %llu ms\n",
601 fc->listen_info->serviceName,
602 GNUNET_a2s (fc->listen_info->service_addr,
603 fc->listen_info->service_addr_len),
604 (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
607 rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
608 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
610 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_min (fc->back_off,
617 if ( (errno != EPIPE) &&
618 (errno != ECONNRESET) )
619 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
620 "Failed to forward data to service: %s\n",
622 closeClientAndServiceSockets (fc,
623 REASON_CLIENT_TO_SERVICE);
627 #if DEBUG_SERVICE_MANAGER
628 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
629 "Forwarded %d bytes to service\n",
632 fc->first_write_done = GNUNET_YES;
633 if (numberOfBytesSent < fc->client_to_service_bufferDataLength)
635 fc->client_to_service_bufferPos += numberOfBytesSent;
636 fc->client_to_service_bufferDataLength -= numberOfBytesSent;
637 fc->client_to_service_task =
638 GNUNET_SCHEDULER_add_write_net (
639 GNUNET_TIME_UNIT_FOREVER_REL,
640 fc->armServiceSocket,
641 &forwardToService, fc);
644 fc->client_to_service_task =
645 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
647 &receiveFromClient, fc);
652 * Read data from the client and then forward it to the service.
654 * @param cls callback data, struct ForwardedConnection for the communication between client and service
658 receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
660 struct ForwardedConnection *fc = cls;
662 fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
663 if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->read_ready,
664 fc->armClientSocket))
666 fc->client_to_service_task =
667 GNUNET_SCHEDULER_add_read_net (
668 GNUNET_TIME_UNIT_FOREVER_REL,
670 &receiveFromClient, fc);
673 fc->client_to_service_bufferPos = fc->client_to_service_buffer;
674 fc->client_to_service_bufferDataLength =
675 GNUNET_NETWORK_socket_recv (fc->armClientSocket,
676 fc->client_to_service_buffer,
678 if (fc->client_to_service_bufferDataLength <= 0)
680 if (fc->client_to_service_bufferDataLength == 0)
682 #if DEBUG_SERVICE_MANAGER
683 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
684 "Client closed connection with service `%s'\n",
685 fc->listen_info->serviceName);
690 #if DEBUG_SERVICE_MANAGER
691 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
692 "Error receiving from client: %s\n",
696 closeClientAndServiceSockets (fc, REASON_CLIENT_TO_SERVICE);
699 #if DEBUG_SERVICE_MANAGER
700 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
701 "Received %d bytes for service\n",
702 fc->client_to_service_bufferDataLength);
704 if (fc->armServiceSocket != NULL)
705 fc->client_to_service_task =
706 GNUNET_SCHEDULER_add_write_net (
707 GNUNET_TIME_UNIT_FOREVER_REL,
708 fc->armServiceSocket,
709 &forwardToService, fc);
714 fc_acceptConnection_ipv4 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
717 fc_acceptConnection_ipv6 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
721 fc_acceptConnection (void *cls,
722 const struct GNUNET_SCHEDULER_TaskContext *tc,
725 struct ForwardedConnection *fc = cls;
726 struct ServiceListeningInfo *sli;
729 sli = fc->service_connect_ipv4;
731 sli = fc->service_connect_ipv6;
733 if ( (tc->reason & (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT | GNUNET_SCHEDULER_REASON_PREREQ_DONE)) ||
734 ((tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY) && fc->armServiceSocket) )
736 GNUNET_NETWORK_socket_close (sli->listeningSocket);
738 fc->service_connect_ipv4 = NULL;
740 fc->service_connect_ipv6 = NULL;
742 else if (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)
744 #if DEBUG_SERVICE_MANAGER
745 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
746 "Connected to service, now starting forwarding\n");
748 fc->armServiceSocket = sli->listeningSocket;
749 if ( (GNUNET_YES == is_ipv4) && (fc->service_connect_ipv6 != NULL) )
751 GNUNET_SCHEDULER_cancel (fc->service_connect_ipv6->acceptTask);
752 fc->service_connect_ipv6->acceptTask = GNUNET_SCHEDULER_add_now (fc_acceptConnection_ipv6, fc);
754 else if ( (GNUNET_NO == is_ipv4) && (fc->service_connect_ipv4 != NULL) )
756 GNUNET_SCHEDULER_cancel (fc->service_connect_ipv4->acceptTask);
757 fc->service_connect_ipv4->acceptTask = GNUNET_SCHEDULER_add_now (fc_acceptConnection_ipv4, fc);
759 GNUNET_free (fc->listen_info->service_addr);
760 fc->listen_info->service_addr = sli->service_addr;
761 fc->listen_info->service_addr_len = sli->service_addr_len;
762 /* fc->listen_info->listeningSocket is it closed already ?*/
763 if (fc->client_to_service_task == GNUNET_SCHEDULER_NO_TASK)
765 if (fc->client_to_service_bufferDataLength == 0)
766 fc->client_to_service_task =
767 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
769 &receiveFromClient, fc);
771 fc->client_to_service_task =
772 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
773 fc->armServiceSocket,
774 &forwardToService, fc);
776 if (fc->service_to_client_task == GNUNET_SCHEDULER_NO_TASK)
778 if (fc->service_to_client_bufferDataLength == 0)
779 fc->service_to_client_task =
780 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
781 fc->armServiceSocket,
782 &receiveFromService, fc);
784 fc->service_to_client_task =
785 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
787 &forwardToClient, fc);
799 fc_acceptConnection_ipv4 (void *cls,
800 const struct GNUNET_SCHEDULER_TaskContext *tc)
802 fc_acceptConnection (cls, tc, GNUNET_YES);
807 fc_acceptConnection_ipv6 (void *cls,
808 const struct GNUNET_SCHEDULER_TaskContext *tc)
810 fc_acceptConnection (cls, tc, GNUNET_NO);
815 service_try_to_connect (const struct sockaddr *addr,
817 struct ForwardedConnection *fc)
819 struct GNUNET_NETWORK_Handle *sock;
820 struct ServiceListeningInfo *serviceListeningInfo;
822 sock = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0);
825 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to create a socket\n");
829 if ( (GNUNET_SYSERR == GNUNET_NETWORK_socket_connect (sock, addr, addrlen)) &&
830 (errno != EINPROGRESS) )
832 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect\n");
833 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
837 serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
838 serviceListeningInfo->serviceName = NULL;
839 serviceListeningInfo->service_addr = GNUNET_malloc (addrlen);
840 memcpy (serviceListeningInfo->service_addr,
843 serviceListeningInfo->service_addr_len = addrlen;
844 serviceListeningInfo->listeningSocket = sock;
848 case sizeof (struct sockaddr_in):
849 fc->service_connect_ipv4 = serviceListeningInfo;
850 serviceListeningInfo->acceptTask =
851 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
852 serviceListeningInfo->listeningSocket,
853 &fc_acceptConnection_ipv4, fc);
855 case sizeof (struct sockaddr_in6):
856 fc->service_connect_ipv6 = serviceListeningInfo;
857 serviceListeningInfo->acceptTask =
858 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
859 serviceListeningInfo->listeningSocket,
860 &fc_acceptConnection_ipv6, fc);
874 start_forwarding (void *cls,
875 const struct GNUNET_SCHEDULER_TaskContext *tc)
877 struct ForwardedConnection *fc = cls;
878 struct GNUNET_TIME_Relative rem;
883 struct sockaddr_in target_ipv4;
884 struct sockaddr_in6 target_ipv6;
885 const struct sockaddr *v4;
886 const struct sockaddr *v6;
887 char listen_address[INET_ADDRSTRLEN];
888 uint16_t listening_port; /* in big endian */
890 fc->start_task = GNUNET_SCHEDULER_NO_TASK;
892 (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) )
894 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
895 _("Unable to forward to service `%s': shutdown\n"),
896 fc->listen_info->serviceName);
897 closeClientAndServiceSockets (fc, REASON_ERROR);
900 rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
901 if (rem.rel_value == 0)
903 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
904 _("Unable to forward to service `%s': timeout before connect\n"),
905 fc->listen_info->serviceName);
906 closeClientAndServiceSockets (fc, REASON_ERROR);
909 switch (fc->listen_info->service_addr->sa_family)
912 inet_ntop (fc->listen_info->service_addr->sa_family,
913 (const void *) &((struct sockaddr_in *) fc->listen_info->service_addr)->sin_addr,
916 is_zero = (strncmp (listen_address, "0.0.0.0:", 8) == 0) || (strncmp (listen_address, "0.0.0.0", 7) == 0);
917 is_ipv4 = GNUNET_YES;
919 listening_port = ((struct sockaddr_in *)fc->listen_info->service_addr)->sin_port;
922 inet_ntop (fc->listen_info->service_addr->sa_family,
923 (const void *) &((struct sockaddr_in6 *) fc->listen_info->service_addr)->sin6_addr,
926 is_zero = (strncmp (listen_address, "[::]:", 5) == 0) || (strncmp (listen_address, "::", 2) == 0);
928 is_ipv6 = GNUNET_YES;
929 listening_port = ((struct sockaddr_in6 *)fc->listen_info->service_addr)->sin6_port;
933 closeClientAndServiceSockets (fc, REASON_ERROR);
937 fc->service_connect_ipv4 = NULL;
938 fc->service_connect_ipv6 = NULL;
943 /* connect to [::1] and 127.0.0.1 instead of [::] and 0.0.0.0 */
944 memset (&target_ipv4, 0, sizeof (target_ipv4));
945 inet_pton (AF_INET, "127.0.0.1", &target_ipv4.sin_addr);
946 target_ipv4.sin_family = AF_INET;
947 target_ipv4.sin_port = listening_port;
948 v4 = (const struct sockaddr *) &target_ipv4;
949 is_ipv4 = GNUNET_YES;
951 memset (&target_ipv6, 0, sizeof (target_ipv6));
952 inet_pton (AF_INET6, "::1", &target_ipv6.sin6_addr);
953 target_ipv6.sin6_family = AF_INET6;
954 target_ipv6.sin6_port = listening_port;
955 is_ipv6 = GNUNET_YES;
956 v6 = (const struct sockaddr *) &target_ipv6;
961 v4 = (const struct sockaddr*) fc->listen_info->service_addr;
963 v6 = (const struct sockaddr*) fc->listen_info->service_addr;
967 failures += service_try_to_connect (v4,
968 sizeof (struct sockaddr_in),
971 failures += service_try_to_connect (v6,
972 sizeof (struct sockaddr_in6),
974 if (is_ipv4 + is_ipv6 == failures)
976 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
977 _ ("Unable to start service `%s': %s\n"),
978 fc->listen_info->serviceName,
980 closeClientAndServiceSockets (fc, REASON_ERROR);
990 stop_listening (const char *serviceName)
992 struct ServiceListeningInfo *pos;
993 struct ServiceListeningInfo *next;
997 next = serviceListeningInfoList_head;
998 while (NULL != (pos = next))
1001 if ( (serviceName != NULL) &&
1002 (strcmp (pos->serviceName, serviceName) != 0) )
1004 if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK)
1005 GNUNET_SCHEDULER_cancel (pos->acceptTask);
1006 GNUNET_break (GNUNET_OK ==
1007 GNUNET_NETWORK_socket_close (pos->listeningSocket));
1008 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
1009 serviceListeningInfoList_tail,
1011 GNUNET_free (pos->serviceName);
1012 GNUNET_free (pos->service_addr);
1020 * First connection has come to the listening socket associated with the service,
1021 * create the service in order to relay the incoming connection to it
1023 * @param cls callback data, struct ServiceListeningInfo describing a listen socket
1027 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1031 accept_and_forward (struct ServiceListeningInfo *serviceListeningInfo)
1033 struct ForwardedConnection *fc;
1035 fc = GNUNET_malloc (sizeof (struct ForwardedConnection));
1036 fc->listen_info = serviceListeningInfo;
1037 fc->service_to_client_bufferPos = fc->service_to_client_buffer;
1038 fc->client_to_service_bufferPos = fc->client_to_service_buffer;
1039 fc->client_addr_len = sizeof (fc->client_addr);
1040 fc->armClientSocket = GNUNET_NETWORK_socket_accept (serviceListeningInfo->listeningSocket,
1041 (struct sockaddr*) fc->client_addr,
1042 &fc->client_addr_len);
1043 if (NULL == fc->armClientSocket)
1045 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1046 _("Unable to accept connection for service `%s': %s\n"),
1047 serviceListeningInfo->serviceName,
1050 GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
1051 serviceListeningInfoList_tail,
1052 serviceListeningInfo);
1053 serviceListeningInfo->acceptTask =
1054 GNUNET_SCHEDULER_add_read_net (
1055 GNUNET_TIME_UNIT_FOREVER_REL,
1056 serviceListeningInfo->listeningSocket,
1058 serviceListeningInfo);
1061 GNUNET_break (GNUNET_OK ==
1062 GNUNET_NETWORK_socket_close (serviceListeningInfo->listeningSocket));
1063 start_service (NULL, serviceListeningInfo->serviceName, NULL);
1064 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1065 _("Service `%s' started\n"),
1066 fc->listen_info->serviceName);
1067 fc->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_SERVICE_TIMEOUT);
1068 fc->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
1069 fc->client_to_service_task =
1070 GNUNET_SCHEDULER_add_read_net (
1071 GNUNET_TIME_UNIT_FOREVER_REL,
1072 fc->armClientSocket,
1073 &receiveFromClient, fc);
1074 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
1076 = GNUNET_SCHEDULER_add_now (&start_forwarding,
1082 * First connection has come to the listening socket associated with the service,
1083 * create the service in order to relay the incoming connection to it
1085 * @param cls callback data, struct ServiceListeningInfo describing a listen socket
1089 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1091 struct ServiceListeningInfo *sli = cls;
1092 struct ServiceListeningInfo *pos;
1093 struct ServiceListeningInfo *next;
1098 sli->acceptTask = GNUNET_SCHEDULER_NO_TASK;
1099 if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
1101 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
1102 serviceListeningInfoList_tail,
1105 use_lsocks = GNUNET_NO;
1106 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (cfg,
1108 "DISABLE_SOCKET_FORWARDING"))
1109 use_lsocks = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1111 "DISABLE_SOCKET_FORWARDING");
1113 use_lsocks = GNUNET_YES;
1115 if (GNUNET_NO != use_lsocks)
1117 accept_and_forward (sli);
1122 next = serviceListeningInfoList_head;
1123 while (NULL != (pos = next))
1126 if (0 == strcmp (pos->serviceName,
1129 GNUNET_array_append (lsocks, ls,
1130 GNUNET_NETWORK_get_fd (pos->listeningSocket));
1131 GNUNET_free (pos->listeningSocket); /* deliberately no closing! */
1132 GNUNET_free (pos->service_addr);
1133 GNUNET_free (pos->serviceName);
1134 GNUNET_SCHEDULER_cancel ( pos->acceptTask);
1135 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
1136 serviceListeningInfoList_tail,
1141 GNUNET_array_append (lsocks, ls,
1142 GNUNET_NETWORK_get_fd (sli->listeningSocket));
1143 GNUNET_free (sli->listeningSocket); /* deliberately no closing! */
1144 GNUNET_free (sli->service_addr);
1145 GNUNET_array_append (lsocks, ls, -1);
1146 start_service (NULL,
1150 while (lsocks[ls] != -1)
1151 GNUNET_break (0 == close (lsocks[ls++]));
1152 GNUNET_array_grow (lsocks, ls, 0);
1153 GNUNET_free (sli->serviceName);
1159 * Creating a listening socket for each of the service's addresses and
1160 * wait for the first incoming connection to it
1162 * @param sa address associated with the service
1163 * @param addr_len length of sa
1164 * @param serviceName the name of the service in question
1167 createListeningSocket (struct sockaddr *sa,
1169 const char *serviceName)
1171 const static int on = 1;
1172 struct GNUNET_NETWORK_Handle *sock;
1173 struct ServiceListeningInfo *serviceListeningInfo;
1175 switch (sa->sa_family)
1178 sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
1181 sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
1184 sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
1189 errno = EAFNOSUPPORT;
1194 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1195 _("Unable to create socket for service `%s': %s\n"),
1201 if (GNUNET_NETWORK_socket_setsockopt
1202 (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
1203 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1206 if ( (sa->sa_family == AF_INET6) &&
1207 (GNUNET_NETWORK_socket_setsockopt
1208 (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
1209 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1213 if (GNUNET_NETWORK_socket_bind
1214 (sock, (const struct sockaddr *) sa, addr_len) != GNUNET_OK)
1216 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1217 _("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
1219 GNUNET_a2s (sa, addr_len),
1221 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1225 if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
1227 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1229 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1233 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1234 _("ARM now monitors connections to service `%s' at `%s'\n"),
1236 GNUNET_a2s (sa, addr_len));
1237 serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
1238 serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
1239 serviceListeningInfo->service_addr = sa;
1240 serviceListeningInfo->service_addr_len = addr_len;
1241 serviceListeningInfo->listeningSocket = sock;
1242 serviceListeningInfo->acceptTask =
1243 GNUNET_SCHEDULER_add_read_net (
1244 GNUNET_TIME_UNIT_FOREVER_REL, sock,
1246 serviceListeningInfo);
1247 GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
1248 serviceListeningInfoList_tail,
1249 serviceListeningInfo);
1254 * Callback function, checks whether the current tokens are representing a service,
1255 * gets its addresses and create listening socket for it.
1257 * @param cls callback data, not used
1258 * @param section configuration section
1259 * @param option configuration option
1260 * @param value the option's value
1263 checkPortNumberCB (void *cls,
1264 const char *section,
1268 struct sockaddr **addrs;
1269 socklen_t *addr_lens;
1273 if ( (strcasecmp (section, "arm") == 0) ||
1274 (strcasecmp (option, "AUTOSTART") != 0) ||
1275 (strcasecmp (value, "YES") != 0) ||
1276 (isInDefaultList (section) == GNUNET_YES) )
1278 if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs,
1281 /* this will free (or capture) addrs[i] */
1282 for (i = 0; i < ret; i++)
1283 createListeningSocket (addrs[i], addr_lens[i], section);
1284 GNUNET_free (addrs);
1285 GNUNET_free (addr_lens);
1290 * Entry point to the Service Manager
1292 * @param configurationHandle configuration to use to get services
1295 prepareServices (const struct GNUNET_CONFIGURATION_Handle
1296 *configurationHandle)
1298 char *defaultServicesString;
1300 cfg = configurationHandle;
1301 /* Split the default services into a list */
1303 GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
1304 &defaultServicesString))
1306 addDefaultServicesToList (defaultServicesString);
1307 GNUNET_free (defaultServicesString);
1309 /* Spot the services from the configuration and create a listening
1311 GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
1314 /* end of gnunet-service-arm_interceptor.c */