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;
394 if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->write_ready,
395 fc->armClientSocket))
397 fc->service_to_client_task =
398 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
400 &forwardToClient, fc);
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,
419 "Forwarded %d bytes to client\n", numberOfBytesSent);
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,
428 &forwardToClient, fc);
431 fc->service_to_client_task =
432 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
433 fc->armServiceSocket,
434 &receiveFromService, fc);
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);
457 if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->read_ready,
458 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);
506 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_min
507 (fc->back_off, rem), &start_forwarding,
512 #if DEBUG_SERVICE_MANAGER
513 if (fc->service_to_client_bufferDataLength != 0)
514 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
515 "Error receiving from service: %s\n", STRERROR (errno));
517 closeClientAndServiceSockets (fc, REASON_SERVICE_TO_CLIENT);
521 fc->first_write_done = GNUNET_YES;
522 #if DEBUG_SERVICE_MANAGER
523 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
524 "Received %d bytes for client\n",
525 fc->service_to_client_bufferDataLength);
527 fc->service_to_client_task =
528 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
530 &forwardToClient, fc);
535 * Forward client message to service
537 * @param cls callback data, struct ForwardedConnection for the communication between client and service
538 * @param tc scheduler context
541 forwardToService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
543 struct ForwardedConnection *fc = cls;
544 ssize_t numberOfBytesSent;
545 struct GNUNET_TIME_Relative rem;
547 fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
548 if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
549 (fc->first_write_done != GNUNET_YES))
551 closeClientAndServiceSockets (fc, REASON_ERROR);
554 if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->write_ready,
555 fc->armServiceSocket))
557 fc->client_to_service_task =
558 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
559 fc->armServiceSocket,
560 &forwardToService, fc);
564 GNUNET_NETWORK_socket_send (fc->armServiceSocket,
565 fc->client_to_service_bufferPos,
566 fc->client_to_service_bufferDataLength);
567 if (numberOfBytesSent <= 0)
569 if (GNUNET_YES != fc->first_write_done)
571 GNUNET_break (GNUNET_OK ==
572 GNUNET_NETWORK_socket_close (fc->armServiceSocket));
573 fc->armServiceSocket = NULL;
574 if ((fc->service_to_client_bufferDataLength == 0) &&
575 (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK))
577 GNUNET_SCHEDULER_cancel (fc->service_to_client_task);
578 fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
580 fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
581 #if DEBUG_SERVICE_MANAGER
582 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
583 "Failed to connect to service `%s' at `%s', will try again in %llu ms\n",
584 fc->listen_info->serviceName,
585 GNUNET_a2s (fc->listen_info->service_addr,
586 fc->listen_info->service_addr_len),
587 (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
590 rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
591 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
594 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_min
595 (fc->back_off, rem), &start_forwarding,
600 if ((errno != EPIPE) && (errno != ECONNRESET))
601 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
602 "Failed to forward data to service: %s\n",
604 closeClientAndServiceSockets (fc, REASON_CLIENT_TO_SERVICE);
608 #if DEBUG_SERVICE_MANAGER
609 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
610 "Forwarded %d bytes to service\n", numberOfBytesSent);
612 fc->first_write_done = GNUNET_YES;
613 if (numberOfBytesSent < fc->client_to_service_bufferDataLength)
615 fc->client_to_service_bufferPos += numberOfBytesSent;
616 fc->client_to_service_bufferDataLength -= numberOfBytesSent;
617 fc->client_to_service_task =
618 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
619 fc->armServiceSocket,
620 &forwardToService, fc);
623 fc->client_to_service_task =
624 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
626 &receiveFromClient, fc);
631 * Read data from the client and then forward it to the service.
633 * @param cls callback data, struct ForwardedConnection for the communication between client and service
637 receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
639 struct ForwardedConnection *fc = cls;
641 fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
642 if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->read_ready,
643 fc->armClientSocket))
645 fc->client_to_service_task =
646 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
648 &receiveFromClient, fc);
651 fc->client_to_service_bufferPos = fc->client_to_service_buffer;
652 fc->client_to_service_bufferDataLength =
653 GNUNET_NETWORK_socket_recv (fc->armClientSocket,
654 fc->client_to_service_buffer, BUFFER_SIZE);
655 if (fc->client_to_service_bufferDataLength <= 0)
657 if (fc->client_to_service_bufferDataLength == 0)
659 #if DEBUG_SERVICE_MANAGER
660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
661 "Client closed connection with service `%s'\n",
662 fc->listen_info->serviceName);
667 #if DEBUG_SERVICE_MANAGER
668 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
669 "Error receiving from client: %s\n", STRERROR (errno));
672 closeClientAndServiceSockets (fc, REASON_CLIENT_TO_SERVICE);
675 #if DEBUG_SERVICE_MANAGER
676 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
677 "Received %d bytes for service\n",
678 fc->client_to_service_bufferDataLength);
680 if (fc->armServiceSocket != NULL)
681 fc->client_to_service_task =
682 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
683 fc->armServiceSocket,
684 &forwardToService, fc);
686 /* We have not added any task with fc as a closure, so we're
687 * dropping our reference to fc
689 fc->reference_count -= 1;
694 fc_acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
696 struct ServiceListeningInfo *sli = cls;
697 struct ForwardedConnection *fc = sli->fc;
699 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))
701 GNUNET_assert (GNUNET_OK ==
702 GNUNET_NETWORK_socket_close (sli->listeningSocket));
703 closeClientAndServiceSockets (fc, REASON_ERROR);
707 #if DEBUG_SERVICE_MANAGER
708 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
709 "Connected to service, now starting forwarding\n");
711 fc->armServiceSocket = sli->listeningSocket;
712 GNUNET_free (fc->listen_info->service_addr);
713 fc->listen_info->service_addr = sli->service_addr;
714 fc->listen_info->service_addr_len = sli->service_addr_len;
715 /* Drop fc reference count prematurely, it'll be incremented
716 * once or twice in the following conditional branches.
717 * This is, apparently, the place where reference count increases
720 fc->reference_count -= 1;
721 if (fc->client_to_service_task == GNUNET_SCHEDULER_NO_TASK)
723 if (fc->client_to_service_bufferDataLength == 0)
724 fc->client_to_service_task =
725 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
727 &receiveFromClient, fc);
729 fc->client_to_service_task =
730 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
731 fc->armServiceSocket,
732 &forwardToService, fc);
733 fc->reference_count += 1;
735 if (fc->service_to_client_task == GNUNET_SCHEDULER_NO_TASK)
737 if (fc->service_to_client_bufferDataLength == 0)
738 fc->service_to_client_task =
739 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
740 fc->armServiceSocket,
741 &receiveFromService, fc);
743 fc->service_to_client_task =
744 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
746 &forwardToClient, fc);
747 fc->reference_count += 1;
753 static struct ServiceListeningInfo *
754 service_try_to_connect (const struct sockaddr *addr,
756 socklen_t addrlen, struct ForwardedConnection *fc)
758 struct GNUNET_NETWORK_Handle *sock;
759 struct ServiceListeningInfo *serviceListeningInfo;
761 sock = GNUNET_NETWORK_socket_create (pf, SOCK_STREAM, 0);
764 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket");
767 if ((GNUNET_SYSERR == GNUNET_NETWORK_socket_connect (sock, addr, addrlen)) &&
768 (errno != EINPROGRESS))
770 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
771 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
774 serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
775 serviceListeningInfo->serviceName = NULL;
776 serviceListeningInfo->service_addr = GNUNET_malloc (addrlen);
777 memcpy (serviceListeningInfo->service_addr, addr, addrlen);
778 serviceListeningInfo->service_addr_len = addrlen;
779 serviceListeningInfo->listeningSocket = sock;
780 serviceListeningInfo->fc = fc;
781 serviceListeningInfo->acceptTask =
782 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
783 serviceListeningInfo->listeningSocket,
784 &fc_acceptConnection,
785 serviceListeningInfo);
786 return serviceListeningInfo;
794 start_forwarding (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
796 struct ForwardedConnection *fc = cls;
797 struct ServiceListeningInfo *sc;
798 struct sockaddr_in target_ipv4;
799 struct sockaddr_in6 target_ipv6;
800 const struct sockaddr_in *v4;
801 const struct sockaddr_in6 *v6;
802 char listen_address[INET6_ADDRSTRLEN];
804 fc->start_task = GNUNET_SCHEDULER_NO_TASK;
805 if ((NULL != tc) && (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)))
807 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
808 _("Unable to forward to service `%s': shutdown\n"),
809 fc->listen_info->serviceName);
810 closeClientAndServiceSockets (fc, REASON_ERROR);
813 if (0 == GNUNET_TIME_absolute_get_remaining (fc->timeout).rel_value)
815 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
817 ("Unable to forward to service `%s': timeout before connect\n"),
818 fc->listen_info->serviceName);
819 closeClientAndServiceSockets (fc, REASON_ERROR);
822 switch (fc->listen_info->service_addr->sa_family)
826 closeClientAndServiceSockets (fc, REASON_ERROR);
829 v4 = (const struct sockaddr_in *) fc->listen_info->service_addr;
830 inet_ntop (fc->listen_info->service_addr->sa_family,
831 (const void *) &v4->sin_addr, listen_address, INET_ADDRSTRLEN);
832 if (0 == strncmp (listen_address, "0.0.0.0", 7))
834 /* connect to [::1] and 127.0.0.1 instead of [::] and 0.0.0.0 */
835 memset (&target_ipv4, 0, sizeof (target_ipv4));
837 inet_pton (AF_INET, "127.0.0.1", &target_ipv4.sin_addr));
838 target_ipv4.sin_family = AF_INET;
839 target_ipv4.sin_port = v4->sin_port;
842 sc = service_try_to_connect ((const struct sockaddr *) v4,
843 PF_INET, sizeof (struct sockaddr_in), fc);
846 v6 = (struct sockaddr_in6 *) fc->listen_info->service_addr;
847 inet_ntop (fc->listen_info->service_addr->sa_family,
848 (const void *) &v6->sin6_addr, listen_address, INET6_ADDRSTRLEN);
849 if ((strncmp (listen_address, "[::]:", 5) == 0) ||
850 (strncmp (listen_address, "::", 2) == 0))
852 memset (&target_ipv6, 0, sizeof (target_ipv6));
853 target_ipv6.sin6_addr = in6addr_loopback;
854 target_ipv6.sin6_family = AF_INET6;
855 target_ipv6.sin6_port = v6->sin6_port;
858 sc = service_try_to_connect ((const struct sockaddr *) v6,
859 PF_INET6, sizeof (struct sockaddr_in6), fc);
862 sc = service_try_to_connect (fc->listen_info->service_addr,
864 fc->listen_info->service_addr_len, fc);
868 closeClientAndServiceSockets (fc, REASON_ERROR);
873 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
874 _("Unable to start service `%s': %s\n"),
875 fc->listen_info->serviceName, STRERROR (errno));
876 closeClientAndServiceSockets (fc, REASON_ERROR);
886 stop_listening (const char *serviceName)
888 struct ServiceListeningInfo *pos;
889 struct ServiceListeningInfo *next;
893 next = serviceListeningInfoList_head;
894 while (NULL != (pos = next))
897 if ((serviceName != NULL) && (strcmp (pos->serviceName, serviceName) != 0))
899 if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK)
900 GNUNET_SCHEDULER_cancel (pos->acceptTask);
901 GNUNET_break (GNUNET_OK ==
902 GNUNET_NETWORK_socket_close (pos->listeningSocket));
903 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
904 serviceListeningInfoList_tail, pos);
905 GNUNET_free (pos->serviceName);
906 GNUNET_free (pos->service_addr);
914 * First connection has come to the listening socket associated with the service,
915 * create the service in order to relay the incoming connection to it
917 * @param cls callback data, struct ServiceListeningInfo describing a listen socket
921 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
925 accept_and_forward (struct ServiceListeningInfo *serviceListeningInfo)
927 struct ForwardedConnection *fc;
929 fc = GNUNET_malloc (sizeof (struct ForwardedConnection));
930 fc->reference_count = 1;
931 fc->listen_info = serviceListeningInfo;
932 fc->service_to_client_bufferPos = fc->service_to_client_buffer;
933 fc->client_to_service_bufferPos = fc->client_to_service_buffer;
934 fc->client_addr_len = sizeof (fc->client_addr);
935 fc->armClientSocket =
936 GNUNET_NETWORK_socket_accept (serviceListeningInfo->listeningSocket,
937 (struct sockaddr *) fc->client_addr,
938 &fc->client_addr_len);
939 if (NULL == fc->armClientSocket)
941 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
942 _("Unable to accept connection for service `%s': %s\n"),
943 serviceListeningInfo->serviceName, STRERROR (errno));
945 GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
946 serviceListeningInfoList_tail,
947 serviceListeningInfo);
948 serviceListeningInfo->acceptTask =
949 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
950 serviceListeningInfo->listeningSocket,
951 &acceptConnection, serviceListeningInfo);
954 GNUNET_break (GNUNET_OK ==
955 GNUNET_NETWORK_socket_close
956 (serviceListeningInfo->listeningSocket));
957 start_service (NULL, serviceListeningInfo->serviceName, NULL);
958 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
959 _("Service `%s' started\n"), fc->listen_info->serviceName);
961 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_SERVICE_TIMEOUT);
962 fc->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
963 fc->client_to_service_task =
964 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
966 &receiveFromClient, fc);
967 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
968 /* We're creating another chain of tasks for this fc that
969 * will have its own reference to it.
971 fc->reference_count += 1;
972 fc->start_task = GNUNET_SCHEDULER_add_now (&start_forwarding, fc);
977 * First connection has come to the listening socket associated with the service,
978 * create the service in order to relay the incoming connection to it
980 * @param cls callback data, struct ServiceListeningInfo describing a listen socket
984 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
986 struct ServiceListeningInfo *sli = cls;
987 struct ServiceListeningInfo *pos;
988 struct ServiceListeningInfo *next;
993 sli->acceptTask = GNUNET_SCHEDULER_NO_TASK;
994 if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
996 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
997 serviceListeningInfoList_tail, sli);
999 use_lsocks = GNUNET_NO;
1000 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (cfg,
1002 "DISABLE_SOCKET_FORWARDING"))
1003 use_lsocks = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1005 "DISABLE_SOCKET_FORWARDING");
1007 use_lsocks = GNUNET_YES;
1009 if (GNUNET_NO != use_lsocks)
1011 accept_and_forward (sli);
1016 next = serviceListeningInfoList_head;
1017 while (NULL != (pos = next))
1020 if (0 == strcmp (pos->serviceName, sli->serviceName))
1022 GNUNET_array_append (lsocks, ls,
1023 GNUNET_NETWORK_get_fd (pos->listeningSocket));
1024 GNUNET_free (pos->listeningSocket); /* deliberately no closing! */
1025 GNUNET_free (pos->service_addr);
1026 GNUNET_free (pos->serviceName);
1027 GNUNET_SCHEDULER_cancel (pos->acceptTask);
1028 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
1029 serviceListeningInfoList_tail, pos);
1033 GNUNET_array_append (lsocks, ls,
1034 GNUNET_NETWORK_get_fd (sli->listeningSocket));
1035 GNUNET_free (sli->listeningSocket); /* deliberately no closing! */
1036 GNUNET_free (sli->service_addr);
1037 GNUNET_array_append (lsocks, ls, -1);
1038 start_service (NULL, sli->serviceName, lsocks);
1040 while (lsocks[ls] != -1)
1041 GNUNET_break (0 == close (lsocks[ls++]));
1042 GNUNET_array_grow (lsocks, ls, 0);
1043 GNUNET_free (sli->serviceName);
1049 * Creating a listening socket for each of the service's addresses and
1050 * wait for the first incoming connection to it
1052 * @param sa address associated with the service
1053 * @param addr_len length of sa
1054 * @param serviceName the name of the service in question
1057 createListeningSocket (struct sockaddr *sa, socklen_t addr_len,
1058 const char *serviceName)
1060 const static int on = 1;
1061 struct GNUNET_NETWORK_Handle *sock;
1062 struct ServiceListeningInfo *serviceListeningInfo;
1064 switch (sa->sa_family)
1067 sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
1070 sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
1073 if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0) /* Do not bind to blank UNIX path! */
1075 sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
1080 errno = EAFNOSUPPORT;
1085 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1086 _("Unable to create socket for service `%s': %s\n"),
1087 serviceName, STRERROR (errno));
1091 if (GNUNET_NETWORK_socket_setsockopt
1092 (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
1093 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1096 if ((sa->sa_family == AF_INET6) &&
1097 (GNUNET_NETWORK_socket_setsockopt
1098 (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
1099 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1103 if (GNUNET_NETWORK_socket_bind
1104 (sock, (const struct sockaddr *) sa, addr_len) != GNUNET_OK)
1106 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1108 ("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
1109 serviceName, GNUNET_a2s (sa, addr_len), STRERROR (errno));
1110 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1114 if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
1116 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
1117 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1121 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1122 _("ARM now monitors connections to service `%s' at `%s'\n"),
1123 serviceName, GNUNET_a2s (sa, addr_len));
1124 serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
1125 serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
1126 serviceListeningInfo->service_addr = sa;
1127 serviceListeningInfo->service_addr_len = addr_len;
1128 serviceListeningInfo->listeningSocket = sock;
1129 serviceListeningInfo->acceptTask =
1130 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock,
1131 &acceptConnection, serviceListeningInfo);
1132 GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
1133 serviceListeningInfoList_tail,
1134 serviceListeningInfo);
1139 * Callback function, checks whether the current tokens are representing a service,
1140 * gets its addresses and create listening socket for it.
1142 * @param cls callback data, not used
1143 * @param section configuration section
1144 * @param option configuration option
1145 * @param value the option's value
1148 checkPortNumberCB (void *cls,
1149 const char *section, const char *option, const char *value)
1151 struct sockaddr **addrs;
1152 socklen_t *addr_lens;
1156 if ((strcasecmp (section, "arm") == 0) ||
1157 (strcasecmp (option, "AUTOSTART") != 0) ||
1158 (strcasecmp (value, "YES") != 0) ||
1159 (isInDefaultList (section) == GNUNET_YES))
1161 if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs,
1164 /* this will free (or capture) addrs[i] */
1165 for (i = 0; i < ret; i++)
1166 createListeningSocket (addrs[i], addr_lens[i], section);
1167 GNUNET_free (addrs);
1168 GNUNET_free (addr_lens);
1173 * Entry point to the Service Manager
1175 * @param configurationHandle configuration to use to get services
1178 prepareServices (const struct GNUNET_CONFIGURATION_Handle *configurationHandle)
1180 char *defaultServicesString;
1182 cfg = configurationHandle;
1183 /* Split the default services into a list */
1185 GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
1186 &defaultServicesString))
1188 addDefaultServicesToList (defaultServicesString);
1189 GNUNET_free (defaultServicesString);
1191 /* Spot the services from the configuration and create a listening
1192 * socket for each */
1193 GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
1196 /* end of gnunet-service-arm_interceptor.c */