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
59 struct ForwardedConnection;
64 struct ServiceListeningInfo
67 * This is a linked list.
69 struct ServiceListeningInfo *next;
72 * This is a linked list.
74 struct ServiceListeningInfo *prev;
77 * Name of the service being forwarded.
84 struct sockaddr *service_addr;
89 socklen_t service_addr_len;
92 * Our listening socket.
94 struct GNUNET_NETWORK_Handle *listeningSocket;
99 struct ForwardedConnection *fc;
102 * Task doing the accepting.
104 GNUNET_SCHEDULER_TaskIdentifier acceptTask;
108 * Information of the connection: client-arm-service
110 struct ForwardedConnection
115 struct GNUNET_NETWORK_Handle *armClientSocket;
120 struct GNUNET_NETWORK_Handle *armServiceSocket;
125 struct ServiceListeningInfo *listen_info;
130 char service_to_client_buffer[BUFFER_SIZE];
135 char client_to_service_buffer[BUFFER_SIZE];
140 char client_addr[32];
145 const char *client_to_service_bufferPos;
150 const char *service_to_client_bufferPos;
153 * Timeout for forwarding.
155 struct GNUNET_TIME_Absolute timeout;
158 * Current back-off value.
160 struct GNUNET_TIME_Relative back_off;
163 * Task that tries to initiate forwarding.
165 GNUNET_SCHEDULER_TaskIdentifier start_task;
170 GNUNET_SCHEDULER_TaskIdentifier client_to_service_task;
175 GNUNET_SCHEDULER_TaskIdentifier service_to_client_task;
180 ssize_t client_to_service_bufferDataLength;
185 ssize_t service_to_client_bufferDataLength;
190 socklen_t client_addr_len;
193 * Have we ever successfully written data to the service?
195 int first_write_done;
198 * Reference count (the structure is freed when it reaches zero)
204 * Array with the names of the services started by default.
206 static char **defaultServicesList;
209 * Size of the defaultServicesList array.
211 static unsigned int numDefaultServices;
216 static const struct GNUNET_CONFIGURATION_Handle *cfg;
221 static struct ServiceListeningInfo *serviceListeningInfoList_head;
226 static struct ServiceListeningInfo *serviceListeningInfoList_tail;
230 * Put the default services represented by a space separated string into an array of strings
232 * @param services space separated string of default services
235 addDefaultServicesToList (const char *services)
241 if (strlen (services) == 0)
243 s = GNUNET_strdup (services);
244 token = strtok (s, " ");
245 while (NULL != token)
247 numDefaultServices++;
248 token = strtok (NULL, " ");
252 defaultServicesList = GNUNET_malloc (numDefaultServices * sizeof (char *));
254 s = GNUNET_strdup (services);
255 token = strtok (s, " ");
256 while (NULL != token)
258 defaultServicesList[i++] = GNUNET_strdup (token);
259 token = strtok (NULL, " ");
262 GNUNET_assert (i == numDefaultServices);
266 * Checks whether the serviceName is in the list of default services
268 * @param serviceName string to check its existance in the list
269 * @return GNUNET_YES if the service is started by default
272 isInDefaultList (const char *serviceName)
276 for (i = 0; i < numDefaultServices; i++)
277 if (strcmp (serviceName, defaultServicesList[i]) == 0)
284 * Close forwarded connection (partial or full).
286 * @param fc connection to close
287 * @param reason which direction to close
290 closeClientAndServiceSockets (struct ForwardedConnection *fc, int reason)
292 if (0 != (REASON_SERVICE_TO_CLIENT & reason))
294 #if DEBUG_SERVICE_MANAGER
295 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
296 "Stopping forwarding from service to client\n");
298 if (fc->armClientSocket != NULL)
299 GNUNET_NETWORK_socket_shutdown (fc->armClientSocket, SHUT_WR);
300 if (fc->armServiceSocket != NULL)
301 GNUNET_NETWORK_socket_shutdown (fc->armServiceSocket, SHUT_RD);
303 if (0 != (REASON_CLIENT_TO_SERVICE & reason))
305 #if DEBUG_SERVICE_MANAGER
306 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
307 "Stopping forwarding from client to service\n");
309 if (fc->armClientSocket != NULL)
310 GNUNET_NETWORK_socket_shutdown (fc->armClientSocket, SHUT_RD);
311 if (fc->armServiceSocket != NULL)
312 GNUNET_NETWORK_socket_shutdown (fc->armServiceSocket, SHUT_WR);
314 #if DEBUG_SERVICE_MANAGER
315 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
316 "Closing forwarding connection (done with both directions)\n");
318 fc->reference_count -= 1;
319 if (fc->reference_count <= 0)
321 if ((NULL != fc->armClientSocket) &&
322 (GNUNET_SYSERR == GNUNET_NETWORK_socket_close (fc->armClientSocket)))
324 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
325 fc->armClientSocket = NULL;
327 if ((NULL != fc->armServiceSocket) &&
328 (GNUNET_SYSERR == GNUNET_NETWORK_socket_close (fc->armServiceSocket)))
330 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
331 fc->armServiceSocket = NULL;
333 if (fc->listen_info != NULL)
335 if (fc->listen_info->serviceName != NULL)
337 GNUNET_free (fc->listen_info->serviceName);
338 fc->listen_info->serviceName = NULL;
340 if (fc->listen_info->service_addr != NULL)
342 GNUNET_free (fc->listen_info->service_addr);
343 fc->listen_info->service_addr = NULL;
345 GNUNET_free (fc->listen_info);
346 fc->listen_info = NULL;
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, const struct GNUNET_SCHEDULER_TaskContext *tc);
382 * Forward messages sent from service to client
384 * @param cls callback data, struct ForwardedConnection for the communication between client and service
388 forwardToClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
390 struct ForwardedConnection *fc = cls;
391 ssize_t numberOfBytesSent;
393 fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
395 GNUNET_NETWORK_fdset_isset (tc->write_ready, fc->armClientSocket))
397 fc->service_to_client_task =
398 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
399 fc->armClientSocket, &forwardToClient,
403 /* Forwarding service response to client */
405 GNUNET_NETWORK_socket_send (fc->armClientSocket,
406 fc->service_to_client_bufferPos,
407 fc->service_to_client_bufferDataLength);
408 if (numberOfBytesSent <= 0)
410 if ((errno != EPIPE) && (errno != ECONNRESET))
411 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
412 "Failed to forward %u bytes of data to client: %s\n",
413 fc->service_to_client_bufferDataLength, STRERROR (errno));
414 closeClientAndServiceSockets (fc, REASON_SERVICE_TO_CLIENT);
417 #if DEBUG_SERVICE_MANAGER
418 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Forwarded %d bytes to client\n",
421 if (numberOfBytesSent < fc->service_to_client_bufferDataLength)
423 fc->service_to_client_bufferPos += numberOfBytesSent;
424 fc->service_to_client_bufferDataLength -= numberOfBytesSent;
425 fc->service_to_client_task =
426 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
427 fc->armClientSocket, &forwardToClient,
431 fc->service_to_client_task =
432 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
433 fc->armServiceSocket, &receiveFromService,
439 * Receive service messages sent by the service and forward it to client
441 * @param cls callback data, struct ForwardedConnection for the communication between client and service
442 * @param tc scheduler context
445 receiveFromService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
447 struct ForwardedConnection *fc = cls;
448 struct GNUNET_TIME_Relative rem;
450 fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
451 if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
452 (fc->first_write_done != GNUNET_YES))
454 closeClientAndServiceSockets (fc, REASON_ERROR);
458 GNUNET_NETWORK_fdset_isset (tc->read_ready, fc->armServiceSocket))
460 fc->service_to_client_task =
461 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
462 fc->armServiceSocket,
463 &receiveFromService, fc);
466 fc->service_to_client_bufferPos = fc->service_to_client_buffer;
467 fc->service_to_client_bufferDataLength =
468 GNUNET_NETWORK_socket_recv (fc->armServiceSocket,
469 fc->service_to_client_buffer, BUFFER_SIZE);
470 if (fc->service_to_client_bufferDataLength <= 0)
472 #if DEBUG_SERVICE_MANAGER
473 if (fc->service_to_client_bufferDataLength == 0)
475 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
476 "Service `%s' stopped sending data.\n",
477 fc->listen_info->serviceName);
480 if (fc->first_write_done != GNUNET_YES)
482 fc->service_to_client_bufferDataLength = 0;
483 GNUNET_break (GNUNET_OK ==
484 GNUNET_NETWORK_socket_close (fc->armServiceSocket));
485 fc->armServiceSocket = NULL;
486 if ((fc->client_to_service_bufferDataLength > 0) &&
487 (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK))
489 GNUNET_SCHEDULER_cancel (fc->client_to_service_task);
490 fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
492 fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
493 #if DEBUG_SERVICE_MANAGER
494 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
495 "Failed to connected to service `%s' at `%s', will try again in %llu ms\n",
496 fc->listen_info->serviceName,
497 GNUNET_a2s (fc->listen_info->service_addr,
498 fc->listen_info->service_addr_len),
499 (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
502 rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
503 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
505 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_min
506 (fc->back_off, rem), &start_forwarding,
511 #if DEBUG_SERVICE_MANAGER
512 if (fc->service_to_client_bufferDataLength != 0)
513 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
514 "Error receiving from service: %s\n", STRERROR (errno));
516 closeClientAndServiceSockets (fc, REASON_SERVICE_TO_CLIENT);
520 fc->first_write_done = GNUNET_YES;
521 #if DEBUG_SERVICE_MANAGER
522 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received %d bytes for client\n",
523 fc->service_to_client_bufferDataLength);
525 fc->service_to_client_task =
526 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
527 fc->armClientSocket, &forwardToClient,
533 * Forward client message to service
535 * @param cls callback data, struct ForwardedConnection for the communication between client and service
536 * @param tc scheduler context
539 forwardToService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
541 struct ForwardedConnection *fc = cls;
542 ssize_t numberOfBytesSent;
543 struct GNUNET_TIME_Relative rem;
545 fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
546 if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
547 (fc->first_write_done != GNUNET_YES))
549 closeClientAndServiceSockets (fc, REASON_ERROR);
553 GNUNET_NETWORK_fdset_isset (tc->write_ready, fc->armServiceSocket))
555 fc->client_to_service_task =
556 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
557 fc->armServiceSocket, &forwardToService,
562 GNUNET_NETWORK_socket_send (fc->armServiceSocket,
563 fc->client_to_service_bufferPos,
564 fc->client_to_service_bufferDataLength);
565 if (numberOfBytesSent <= 0)
567 if (GNUNET_YES != fc->first_write_done)
569 GNUNET_break (GNUNET_OK ==
570 GNUNET_NETWORK_socket_close (fc->armServiceSocket));
571 fc->armServiceSocket = NULL;
572 if ((fc->service_to_client_bufferDataLength == 0) &&
573 (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK))
575 GNUNET_SCHEDULER_cancel (fc->service_to_client_task);
576 fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
578 fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
579 #if DEBUG_SERVICE_MANAGER
580 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
581 "Failed to connect to service `%s' at `%s', will try again in %llu ms\n",
582 fc->listen_info->serviceName,
583 GNUNET_a2s (fc->listen_info->service_addr,
584 fc->listen_info->service_addr_len),
585 (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
588 rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
589 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
591 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_min
592 (fc->back_off, rem), &start_forwarding,
597 if ((errno != EPIPE) && (errno != ECONNRESET))
598 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
599 "Failed to forward data to service: %s\n",
601 closeClientAndServiceSockets (fc, REASON_CLIENT_TO_SERVICE);
605 #if DEBUG_SERVICE_MANAGER
606 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Forwarded %d bytes to service\n",
609 fc->first_write_done = GNUNET_YES;
610 if (numberOfBytesSent < fc->client_to_service_bufferDataLength)
612 fc->client_to_service_bufferPos += numberOfBytesSent;
613 fc->client_to_service_bufferDataLength -= numberOfBytesSent;
614 fc->client_to_service_task =
615 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
616 fc->armServiceSocket, &forwardToService,
620 fc->client_to_service_task =
621 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
622 fc->armClientSocket, &receiveFromClient,
628 * Read data from the client and then forward it to the service.
630 * @param cls callback data, struct ForwardedConnection for the communication between client and service
634 receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
636 struct ForwardedConnection *fc = cls;
638 fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
640 GNUNET_NETWORK_fdset_isset (tc->read_ready, fc->armClientSocket))
642 fc->client_to_service_task =
643 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
644 fc->armClientSocket, &receiveFromClient,
648 fc->client_to_service_bufferPos = fc->client_to_service_buffer;
649 fc->client_to_service_bufferDataLength =
650 GNUNET_NETWORK_socket_recv (fc->armClientSocket,
651 fc->client_to_service_buffer, BUFFER_SIZE);
652 if (fc->client_to_service_bufferDataLength <= 0)
654 if (fc->client_to_service_bufferDataLength == 0)
656 #if DEBUG_SERVICE_MANAGER
657 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
658 "Client closed connection with service `%s'\n",
659 fc->listen_info->serviceName);
664 #if DEBUG_SERVICE_MANAGER
665 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error receiving from client: %s\n",
669 closeClientAndServiceSockets (fc, REASON_CLIENT_TO_SERVICE);
672 #if DEBUG_SERVICE_MANAGER
673 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received %d bytes for service\n",
674 fc->client_to_service_bufferDataLength);
676 if (fc->armServiceSocket != NULL)
677 fc->client_to_service_task =
678 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
679 fc->armServiceSocket, &forwardToService,
682 /* We have not added any task with fc as a closure, so we're
683 * dropping our reference to fc
685 fc->reference_count -= 1;
690 fc_acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
692 struct ServiceListeningInfo *sli = cls;
693 struct ForwardedConnection *fc = sli->fc;
695 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))
697 GNUNET_assert (GNUNET_OK ==
698 GNUNET_NETWORK_socket_close (sli->listeningSocket));
699 closeClientAndServiceSockets (fc, REASON_ERROR);
703 #if DEBUG_SERVICE_MANAGER
704 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
705 "Connected to service, now starting forwarding\n");
707 fc->armServiceSocket = sli->listeningSocket;
708 GNUNET_free (fc->listen_info->service_addr);
709 fc->listen_info->service_addr = sli->service_addr;
710 fc->listen_info->service_addr_len = sli->service_addr_len;
711 /* Drop fc reference count prematurely, it'll be incremented
712 * once or twice in the following conditional branches.
713 * This is, apparently, the place where reference count increases
716 fc->reference_count -= 1;
717 if (fc->client_to_service_task == GNUNET_SCHEDULER_NO_TASK)
719 if (fc->client_to_service_bufferDataLength == 0)
720 fc->client_to_service_task =
721 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
723 &receiveFromClient, fc);
725 fc->client_to_service_task =
726 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
727 fc->armServiceSocket,
728 &forwardToService, fc);
729 fc->reference_count += 1;
731 if (fc->service_to_client_task == GNUNET_SCHEDULER_NO_TASK)
733 if (fc->service_to_client_bufferDataLength == 0)
734 fc->service_to_client_task =
735 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
736 fc->armServiceSocket,
737 &receiveFromService, fc);
739 fc->service_to_client_task =
740 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
741 fc->armClientSocket, &forwardToClient,
743 fc->reference_count += 1;
749 static struct ServiceListeningInfo *
750 service_try_to_connect (const struct sockaddr *addr, int pf, socklen_t addrlen,
751 struct ForwardedConnection *fc)
753 struct GNUNET_NETWORK_Handle *sock;
754 struct ServiceListeningInfo *serviceListeningInfo;
756 sock = GNUNET_NETWORK_socket_create (pf, SOCK_STREAM, 0);
759 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket");
762 if ((GNUNET_SYSERR == GNUNET_NETWORK_socket_connect (sock, addr, addrlen)) &&
763 (errno != EINPROGRESS))
765 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
766 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
769 serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
770 serviceListeningInfo->serviceName = NULL;
771 serviceListeningInfo->service_addr = GNUNET_malloc (addrlen);
772 memcpy (serviceListeningInfo->service_addr, addr, addrlen);
773 serviceListeningInfo->service_addr_len = addrlen;
774 serviceListeningInfo->listeningSocket = sock;
775 serviceListeningInfo->fc = fc;
776 serviceListeningInfo->acceptTask =
777 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
778 serviceListeningInfo->listeningSocket,
779 &fc_acceptConnection,
780 serviceListeningInfo);
781 return serviceListeningInfo;
789 start_forwarding (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
791 struct ForwardedConnection *fc = cls;
792 struct ServiceListeningInfo *sc;
793 struct sockaddr_in target_ipv4;
794 struct sockaddr_in6 target_ipv6;
795 const struct sockaddr_in *v4;
796 const struct sockaddr_in6 *v6;
797 char listen_address[INET6_ADDRSTRLEN];
799 fc->start_task = GNUNET_SCHEDULER_NO_TASK;
800 if ((NULL != tc) && (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)))
802 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
803 _("Unable to forward to service `%s': shutdown\n"),
804 fc->listen_info->serviceName);
805 closeClientAndServiceSockets (fc, REASON_ERROR);
808 if (0 == GNUNET_TIME_absolute_get_remaining (fc->timeout).rel_value)
810 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
812 ("Unable to forward to service `%s': timeout before connect\n"),
813 fc->listen_info->serviceName);
814 closeClientAndServiceSockets (fc, REASON_ERROR);
817 switch (fc->listen_info->service_addr->sa_family)
821 closeClientAndServiceSockets (fc, REASON_ERROR);
824 v4 = (const struct sockaddr_in *) fc->listen_info->service_addr;
825 inet_ntop (fc->listen_info->service_addr->sa_family,
826 (const void *) &v4->sin_addr, listen_address, INET_ADDRSTRLEN);
827 if (0 == strncmp (listen_address, "0.0.0.0", 7))
829 /* connect to [::1] and 127.0.0.1 instead of [::] and 0.0.0.0 */
830 memset (&target_ipv4, 0, sizeof (target_ipv4));
832 inet_pton (AF_INET, "127.0.0.1", &target_ipv4.sin_addr));
833 target_ipv4.sin_family = AF_INET;
834 target_ipv4.sin_port = v4->sin_port;
837 sc = service_try_to_connect ((const struct sockaddr *) v4, PF_INET,
838 sizeof (struct sockaddr_in), fc);
841 v6 = (struct sockaddr_in6 *) fc->listen_info->service_addr;
842 inet_ntop (fc->listen_info->service_addr->sa_family,
843 (const void *) &v6->sin6_addr, listen_address, INET6_ADDRSTRLEN);
844 if ((strncmp (listen_address, "[::]:", 5) == 0) ||
845 (strncmp (listen_address, "::", 2) == 0))
847 memset (&target_ipv6, 0, sizeof (target_ipv6));
848 target_ipv6.sin6_addr = in6addr_loopback;
849 target_ipv6.sin6_family = AF_INET6;
850 target_ipv6.sin6_port = v6->sin6_port;
853 sc = service_try_to_connect ((const struct sockaddr *) v6, PF_INET6,
854 sizeof (struct sockaddr_in6), fc);
857 sc = service_try_to_connect (fc->listen_info->service_addr, PF_UNIX,
858 fc->listen_info->service_addr_len, fc);
862 closeClientAndServiceSockets (fc, REASON_ERROR);
867 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
868 _("Unable to start service `%s': %s\n"),
869 fc->listen_info->serviceName, STRERROR (errno));
870 closeClientAndServiceSockets (fc, REASON_ERROR);
880 stop_listening (const char *serviceName)
882 struct ServiceListeningInfo *pos;
883 struct ServiceListeningInfo *next;
887 next = serviceListeningInfoList_head;
888 while (NULL != (pos = next))
891 if ((serviceName != NULL) && (strcmp (pos->serviceName, serviceName) != 0))
893 if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK)
894 GNUNET_SCHEDULER_cancel (pos->acceptTask);
895 GNUNET_break (GNUNET_OK ==
896 GNUNET_NETWORK_socket_close (pos->listeningSocket));
897 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
898 serviceListeningInfoList_tail, pos);
899 GNUNET_free (pos->serviceName);
900 GNUNET_free (pos->service_addr);
908 * First connection has come to the listening socket associated with the service,
909 * create the service in order to relay the incoming connection to it
911 * @param cls callback data, struct ServiceListeningInfo describing a listen socket
915 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
919 accept_and_forward (struct ServiceListeningInfo *serviceListeningInfo)
921 struct ForwardedConnection *fc;
923 fc = GNUNET_malloc (sizeof (struct ForwardedConnection));
924 fc->reference_count = 1;
925 fc->listen_info = serviceListeningInfo;
926 fc->service_to_client_bufferPos = fc->service_to_client_buffer;
927 fc->client_to_service_bufferPos = fc->client_to_service_buffer;
928 fc->client_addr_len = sizeof (fc->client_addr);
929 fc->armClientSocket =
930 GNUNET_NETWORK_socket_accept (serviceListeningInfo->listeningSocket,
931 (struct sockaddr *) fc->client_addr,
932 &fc->client_addr_len);
933 if (NULL == fc->armClientSocket)
935 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
936 _("Unable to accept connection for service `%s': %s\n"),
937 serviceListeningInfo->serviceName, STRERROR (errno));
939 GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
940 serviceListeningInfoList_tail,
941 serviceListeningInfo);
942 serviceListeningInfo->acceptTask =
943 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
944 serviceListeningInfo->listeningSocket,
945 &acceptConnection, serviceListeningInfo);
948 GNUNET_break (GNUNET_OK ==
949 GNUNET_NETWORK_socket_close
950 (serviceListeningInfo->listeningSocket));
951 start_service (NULL, serviceListeningInfo->serviceName, NULL);
952 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Service `%s' started\n"),
953 fc->listen_info->serviceName);
955 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_SERVICE_TIMEOUT);
956 fc->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
957 fc->client_to_service_task =
958 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
959 fc->armClientSocket, &receiveFromClient,
961 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
962 /* We're creating another chain of tasks for this fc that
963 * will have its own reference to it.
965 fc->reference_count += 1;
966 fc->start_task = GNUNET_SCHEDULER_add_now (&start_forwarding, fc);
971 * First connection has come to the listening socket associated with the service,
972 * create the service in order to relay the incoming connection to it
974 * @param cls callback data, struct ServiceListeningInfo describing a listen socket
978 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
980 struct ServiceListeningInfo *sli = cls;
981 struct ServiceListeningInfo *pos;
982 struct ServiceListeningInfo *next;
987 sli->acceptTask = GNUNET_SCHEDULER_NO_TASK;
988 if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
990 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
991 serviceListeningInfoList_tail, sli);
993 use_lsocks = GNUNET_NO;
995 GNUNET_CONFIGURATION_have_value (cfg, sli->serviceName,
996 "DISABLE_SOCKET_FORWARDING"))
998 GNUNET_CONFIGURATION_get_value_yesno (cfg, sli->serviceName,
999 "DISABLE_SOCKET_FORWARDING");
1001 use_lsocks = GNUNET_YES;
1003 if (GNUNET_NO != use_lsocks)
1005 accept_and_forward (sli);
1010 next = serviceListeningInfoList_head;
1011 while (NULL != (pos = next))
1014 if (0 == strcmp (pos->serviceName, sli->serviceName))
1016 GNUNET_array_append (lsocks, ls,
1017 GNUNET_NETWORK_get_fd (pos->listeningSocket));
1018 GNUNET_free (pos->listeningSocket); /* deliberately no closing! */
1019 GNUNET_free (pos->service_addr);
1020 GNUNET_free (pos->serviceName);
1021 GNUNET_SCHEDULER_cancel (pos->acceptTask);
1022 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
1023 serviceListeningInfoList_tail, pos);
1027 GNUNET_array_append (lsocks, ls,
1028 GNUNET_NETWORK_get_fd (sli->listeningSocket));
1029 GNUNET_free (sli->listeningSocket); /* deliberately no closing! */
1030 GNUNET_free (sli->service_addr);
1031 GNUNET_array_append (lsocks, ls, -1);
1032 start_service (NULL, sli->serviceName, lsocks);
1034 while (lsocks[ls] != -1)
1035 GNUNET_break (0 == close (lsocks[ls++]));
1036 GNUNET_array_grow (lsocks, ls, 0);
1037 GNUNET_free (sli->serviceName);
1043 * Creating a listening socket for each of the service's addresses and
1044 * wait for the first incoming connection to it
1046 * @param sa address associated with the service
1047 * @param addr_len length of sa
1048 * @param serviceName the name of the service in question
1051 createListeningSocket (struct sockaddr *sa, socklen_t addr_len,
1052 const char *serviceName)
1054 const static int on = 1;
1055 struct GNUNET_NETWORK_Handle *sock;
1056 struct ServiceListeningInfo *serviceListeningInfo;
1058 switch (sa->sa_family)
1061 sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
1064 sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
1067 if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0) /* Do not bind to blank UNIX path! */
1069 sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
1074 errno = EAFNOSUPPORT;
1079 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1080 _("Unable to create socket for service `%s': %s\n"),
1081 serviceName, STRERROR (errno));
1085 if (GNUNET_NETWORK_socket_setsockopt
1086 (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
1087 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1090 if ((sa->sa_family == AF_INET6) &&
1091 (GNUNET_NETWORK_socket_setsockopt
1092 (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
1093 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1097 if (GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) sa, addr_len)
1100 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1102 ("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
1103 serviceName, GNUNET_a2s (sa, addr_len), STRERROR (errno));
1104 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1108 if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
1110 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
1111 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1115 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1116 _("ARM now monitors connections to service `%s' at `%s'\n"),
1117 serviceName, GNUNET_a2s (sa, addr_len));
1118 serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
1119 serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
1120 serviceListeningInfo->service_addr = sa;
1121 serviceListeningInfo->service_addr_len = addr_len;
1122 serviceListeningInfo->listeningSocket = sock;
1123 serviceListeningInfo->acceptTask =
1124 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock,
1125 &acceptConnection, serviceListeningInfo);
1126 GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
1127 serviceListeningInfoList_tail,
1128 serviceListeningInfo);
1133 * Callback function, checks whether the current tokens are representing a service,
1134 * gets its addresses and create listening socket for it.
1136 * @param cls callback data, not used
1137 * @param section configuration section
1138 * @param option configuration option
1139 * @param value the option's value
1142 checkPortNumberCB (void *cls, const char *section, const char *option,
1145 struct sockaddr **addrs;
1146 socklen_t *addr_lens;
1150 if ((strcasecmp (section, "arm") == 0) ||
1151 (strcasecmp (option, "AUTOSTART") != 0) ||
1152 (strcasecmp (value, "YES") != 0) ||
1153 (isInDefaultList (section) == GNUNET_YES))
1157 GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs, &addr_lens)))
1159 /* this will free (or capture) addrs[i] */
1160 for (i = 0; i < ret; i++)
1161 createListeningSocket (addrs[i], addr_lens[i], section);
1162 GNUNET_free (addrs);
1163 GNUNET_free (addr_lens);
1168 * Entry point to the Service Manager
1170 * @param configurationHandle configuration to use to get services
1173 prepareServices (const struct GNUNET_CONFIGURATION_Handle *configurationHandle)
1175 char *defaultServicesString;
1177 cfg = configurationHandle;
1178 /* Split the default services into a list */
1180 GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
1181 &defaultServicesString))
1183 addDefaultServicesToList (defaultServicesString);
1184 GNUNET_free (defaultServicesString);
1186 /* Spot the services from the configuration and create a listening
1187 * socket for each */
1188 GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
1191 /* end of gnunet-service-arm_interceptor.c */