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;
194 * Array with the names of the services started by default.
196 static char **defaultServicesList;
199 * Size of the defaultServicesList array.
201 static unsigned int numDefaultServices;
206 static const struct GNUNET_CONFIGURATION_Handle *cfg;
211 static struct GNUNET_SCHEDULER_Handle *scheduler;
216 static struct ServiceListeningInfo *serviceListeningInfoList_head;
221 static struct ServiceListeningInfo *serviceListeningInfoList_tail;
225 * Put the default services represented by a space separated string into an array of strings
227 * @param services space separated string of default services
230 addDefaultServicesToList (const char *services)
236 if (strlen (services) == 0)
238 s = GNUNET_strdup (services);
239 token = strtok (s, " ");
240 while (NULL != token)
242 numDefaultServices++;
243 token = strtok (NULL, " ");
247 defaultServicesList = GNUNET_malloc (numDefaultServices * sizeof (char *));
249 s = GNUNET_strdup (services);
250 token = strtok (s, " ");
251 while (NULL != token)
253 defaultServicesList[i++] = GNUNET_strdup (token);
254 token = strtok (NULL, " ");
257 GNUNET_assert (i == numDefaultServices);
261 * Checks whether the serviceName is in the list of default services
263 * @param serviceName string to check its existance in the list
264 * @return GNUNET_YES if the service is started by default
267 isInDefaultList (const char *serviceName)
270 for (i = 0; i < numDefaultServices; i++)
271 if (strcmp (serviceName, defaultServicesList[i]) == 0)
278 * Close forwarded connection (partial or full).
280 * @param fc connection to close
281 * @param reason which direction to close
284 closeClientAndServiceSockets (struct ForwardedConnection *fc,
287 if (0 != (REASON_SERVICE_TO_CLIENT & reason))
289 #if DEBUG_SERVICE_MANAGER
290 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
291 "Stopping forwarding from service to client\n",
292 fc->listen_info->serviceName);
294 if (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK)
296 GNUNET_SCHEDULER_cancel (scheduler, fc->service_to_client_task);
297 fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
299 if (fc->armClientSocket != NULL)
300 GNUNET_NETWORK_socket_shutdown (fc->armClientSocket,
302 if (fc->armServiceSocket != NULL)
303 GNUNET_NETWORK_socket_shutdown (fc->armServiceSocket,
306 if (0 != (REASON_CLIENT_TO_SERVICE & reason))
308 #if DEBUG_SERVICE_MANAGER
309 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
310 "Stopping forwarding from client to service\n",
311 fc->listen_info->serviceName);
313 if (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK)
315 GNUNET_SCHEDULER_cancel (scheduler,
316 fc->client_to_service_task);
317 fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
319 if (fc->armClientSocket != NULL)
320 GNUNET_NETWORK_socket_shutdown (fc->armClientSocket,
322 if (fc->armServiceSocket != NULL)
323 GNUNET_NETWORK_socket_shutdown (fc->armServiceSocket,
326 if ( (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK) ||
327 (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK) )
329 #if DEBUG_SERVICE_MANAGER
330 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
331 "Closing forwarding connection (done with both directions)\n");
333 if (fc->start_task != GNUNET_SCHEDULER_NO_TASK)
334 GNUNET_SCHEDULER_cancel (scheduler,
336 if ( (NULL != fc->armClientSocket) &&
338 GNUNET_NETWORK_socket_close (fc->armClientSocket)) )
339 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
340 if ( (NULL != fc->armServiceSocket) &&
342 GNUNET_NETWORK_socket_close (fc->armServiceSocket)) )
343 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
344 GNUNET_free (fc->listen_info->serviceName);
345 GNUNET_free (fc->listen_info->service_addr);
346 GNUNET_free (fc->listen_info);
355 receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
362 receiveFromService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
369 start_forwarding (void *cls,
370 const struct GNUNET_SCHEDULER_TaskContext *tc);
375 * Forward messages sent from service to client
377 * @param cls callback data, struct ForwardedConnection for the communication between client and service
381 forwardToClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
383 struct ForwardedConnection *fc = cls;
384 ssize_t numberOfBytesSent;
386 fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
387 if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->write_ready,
388 fc->armClientSocket))
390 fc->service_to_client_task =
391 GNUNET_SCHEDULER_add_write_net (scheduler,
392 GNUNET_TIME_UNIT_FOREVER_REL,
394 &forwardToClient, fc);
397 /* Forwarding service response to client */
399 GNUNET_NETWORK_socket_send (fc->armClientSocket,
400 fc->service_to_client_bufferPos,
401 fc->service_to_client_bufferDataLength);
402 if (numberOfBytesSent <= 0)
404 if ( (errno != EPIPE) &&
405 (errno != ECONNRESET) )
406 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
407 "Failed to forward %u bytes of data to client: %s\n",
408 fc->service_to_client_bufferDataLength,
410 closeClientAndServiceSockets (fc,
411 REASON_SERVICE_TO_CLIENT);
414 #if DEBUG_SERVICE_MANAGER
415 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
416 "Forwarded %d bytes to client\n",
419 if (numberOfBytesSent < fc->service_to_client_bufferDataLength)
421 fc->service_to_client_bufferPos += numberOfBytesSent;
422 fc->service_to_client_bufferDataLength -= numberOfBytesSent;
423 fc->service_to_client_task =
424 GNUNET_SCHEDULER_add_write_net (scheduler,
425 GNUNET_TIME_UNIT_FOREVER_REL,
431 fc->service_to_client_task =
432 GNUNET_SCHEDULER_add_read_net (scheduler,
433 GNUNET_TIME_UNIT_FOREVER_REL,
434 fc->armServiceSocket,
441 * Receive service messages sent by the service and forward it to client
443 * @param cls callback data, struct ForwardedConnection for the communication between client and service
444 * @param tc scheduler context
447 receiveFromService (void *cls,
448 const struct GNUNET_SCHEDULER_TaskContext *tc)
450 struct ForwardedConnection *fc = cls;
451 struct GNUNET_TIME_Relative rem;
453 fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
454 if ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
455 (fc->first_write_done != GNUNET_YES) )
457 closeClientAndServiceSockets (fc, REASON_ERROR);
460 if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->read_ready,
461 fc->armServiceSocket))
463 fc->service_to_client_task =
464 GNUNET_SCHEDULER_add_read_net (scheduler,
465 GNUNET_TIME_UNIT_FOREVER_REL,
466 fc->armServiceSocket,
467 &receiveFromService, fc);
470 fc->service_to_client_bufferPos = fc->service_to_client_buffer;
471 fc->service_to_client_bufferDataLength =
472 GNUNET_NETWORK_socket_recv (fc->armServiceSocket,
473 fc->service_to_client_buffer,
475 if (fc->service_to_client_bufferDataLength <= 0)
477 #if DEBUG_SERVICE_MANAGER
478 if (fc->service_to_client_bufferDataLength == 0)
480 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
481 "Service `%s' stopped sending data.\n",
482 fc->listen_info->serviceName);
485 if (fc->first_write_done != GNUNET_YES)
487 fc->service_to_client_bufferDataLength = 0;
488 GNUNET_break (GNUNET_OK ==
489 GNUNET_NETWORK_socket_close (fc->armServiceSocket));
490 fc->armServiceSocket = NULL;
491 if ( (fc->client_to_service_bufferDataLength > 0) &&
492 (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK) )
494 GNUNET_SCHEDULER_cancel (scheduler,
495 fc->client_to_service_task);
496 fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
498 fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
499 #if DEBUG_SERVICE_MANAGER
500 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
501 "Failed to connected to service `%s' at `%s', will try again in %llu ms\n",
502 fc->listen_info->serviceName,
503 GNUNET_a2s (fc->listen_info->service_addr,
504 fc->listen_info->service_addr_len),
505 (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
508 rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
509 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
511 = GNUNET_SCHEDULER_add_delayed (scheduler,
512 GNUNET_TIME_relative_min (fc->back_off,
519 #if DEBUG_SERVICE_MANAGER
520 if (fc->service_to_client_bufferDataLength != 0)
521 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
522 "Error receiving from service: %s\n",
525 closeClientAndServiceSockets (fc, REASON_SERVICE_TO_CLIENT);
529 fc->first_write_done = GNUNET_YES;
530 #if DEBUG_SERVICE_MANAGER
531 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
532 "Received %d bytes for client\n",
533 fc->service_to_client_bufferDataLength);
535 fc->service_to_client_task =
536 GNUNET_SCHEDULER_add_write_net (scheduler,
537 GNUNET_TIME_UNIT_FOREVER_REL,
539 &forwardToClient, fc);
544 * Forward client message to service
546 * @param cls callback data, struct ForwardedConnection for the communication between client and service
547 * @param tc scheduler context
550 forwardToService (void *cls,
551 const struct GNUNET_SCHEDULER_TaskContext *tc)
553 struct ForwardedConnection *fc = cls;
554 ssize_t numberOfBytesSent;
555 struct GNUNET_TIME_Relative rem;
557 fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
558 if ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
559 (fc->first_write_done != GNUNET_YES) )
561 closeClientAndServiceSockets (fc, REASON_ERROR);
564 if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->write_ready,
565 fc->armServiceSocket))
567 fc->client_to_service_task =
568 GNUNET_SCHEDULER_add_write_net (scheduler,
569 GNUNET_TIME_UNIT_FOREVER_REL,
570 fc->armServiceSocket,
571 &forwardToService, fc);
575 GNUNET_NETWORK_socket_send (fc->armServiceSocket,
576 fc->client_to_service_bufferPos,
577 fc->client_to_service_bufferDataLength);
578 if (numberOfBytesSent <= 0)
580 if (GNUNET_YES != fc->first_write_done)
582 GNUNET_break (GNUNET_OK ==
583 GNUNET_NETWORK_socket_close (fc->armServiceSocket));
584 fc->armServiceSocket = NULL;
585 if ( (fc->service_to_client_bufferDataLength == 0) &&
586 (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK) )
588 GNUNET_SCHEDULER_cancel (scheduler,
589 fc->service_to_client_task);
590 fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
592 fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
593 #if DEBUG_SERVICE_MANAGER
594 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
595 "Failed to connect to service `%s' at `%s', will try again in %llu ms\n",
596 fc->listen_info->serviceName,
597 GNUNET_a2s (fc->listen_info->service_addr,
598 fc->listen_info->service_addr_len),
599 (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
602 rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
603 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
605 = GNUNET_SCHEDULER_add_delayed (scheduler,
606 GNUNET_TIME_relative_min (fc->back_off,
613 if ( (errno != EPIPE) &&
614 (errno != ECONNRESET) )
615 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
616 "Failed to forward data to service: %s\n",
618 closeClientAndServiceSockets (fc,
619 REASON_CLIENT_TO_SERVICE);
623 #if DEBUG_SERVICE_MANAGER
624 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
625 "Forwarded %d bytes to service\n",
628 fc->first_write_done = GNUNET_YES;
629 if (numberOfBytesSent < fc->client_to_service_bufferDataLength)
631 fc->client_to_service_bufferPos += numberOfBytesSent;
632 fc->client_to_service_bufferDataLength -= numberOfBytesSent;
633 fc->client_to_service_task =
634 GNUNET_SCHEDULER_add_write_net (scheduler,
635 GNUNET_TIME_UNIT_FOREVER_REL,
636 fc->armServiceSocket,
637 &forwardToService, fc);
640 fc->client_to_service_task =
641 GNUNET_SCHEDULER_add_read_net (scheduler,
642 GNUNET_TIME_UNIT_FOREVER_REL,
644 &receiveFromClient, fc);
649 * Read data from the client and then forward it to the service.
651 * @param cls callback data, struct ForwardedConnection for the communication between client and service
655 receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
657 struct ForwardedConnection *fc = cls;
659 fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
660 if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->read_ready,
661 fc->armClientSocket))
663 fc->client_to_service_task =
664 GNUNET_SCHEDULER_add_read_net (scheduler,
665 GNUNET_TIME_UNIT_FOREVER_REL,
667 &receiveFromClient, fc);
670 fc->client_to_service_bufferPos = fc->client_to_service_buffer;
671 fc->client_to_service_bufferDataLength =
672 GNUNET_NETWORK_socket_recv (fc->armClientSocket,
673 fc->client_to_service_buffer,
675 if (fc->client_to_service_bufferDataLength <= 0)
677 if (fc->client_to_service_bufferDataLength == 0)
679 #if DEBUG_SERVICE_MANAGER
680 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
681 "Client closed connection with service `%s'\n",
682 fc->listen_info->serviceName);
687 #if DEBUG_SERVICE_MANAGER
688 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
689 "Error receiving from client: %s\n",
693 closeClientAndServiceSockets (fc, REASON_CLIENT_TO_SERVICE);
696 #if DEBUG_SERVICE_MANAGER
697 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
698 "Received %d bytes for service\n",
699 fc->client_to_service_bufferDataLength);
701 if (fc->armServiceSocket != NULL)
702 fc->client_to_service_task =
703 GNUNET_SCHEDULER_add_write_net (scheduler,
704 GNUNET_TIME_UNIT_FOREVER_REL,
705 fc->armServiceSocket,
706 &forwardToService, fc);
714 start_forwarding (void *cls,
715 const struct GNUNET_SCHEDULER_TaskContext *tc)
717 struct ForwardedConnection *fc = cls;
718 struct GNUNET_TIME_Relative rem;
720 fc->start_task = GNUNET_SCHEDULER_NO_TASK;
722 (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) )
724 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
725 _("Unable to forward to service `%s': shutdown\n"),
726 fc->listen_info->serviceName);
727 closeClientAndServiceSockets (fc, REASON_ERROR);
730 rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
733 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
734 _("Unable to forward to service `%s': timeout before connect\n"),
735 fc->listen_info->serviceName);
736 closeClientAndServiceSockets (fc, REASON_ERROR);
739 fc->armServiceSocket =
740 GNUNET_NETWORK_socket_create (fc->listen_info->service_addr->sa_family,
742 if (NULL == fc->armServiceSocket)
744 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
745 _ ("Unable to start service `%s': %s\n"),
746 fc->listen_info->serviceName,
748 closeClientAndServiceSockets (fc, REASON_ERROR);
751 if ( (GNUNET_SYSERR ==
752 GNUNET_NETWORK_socket_connect (fc->armServiceSocket,
753 fc->listen_info->service_addr,
754 fc->listen_info->service_addr_len)) &&
755 (errno != EINPROGRESS) )
757 GNUNET_break (GNUNET_OK ==
758 GNUNET_NETWORK_socket_close (fc->armServiceSocket));
759 fc->armServiceSocket = NULL;
760 fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
761 #if DEBUG_SERVICE_MANAGER
762 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
763 "Failed to connected to service `%s' at `%s', will try again in %llu ms\n",
764 fc->listen_info->serviceName,
765 GNUNET_a2s (fc->listen_info->service_addr,
766 fc->listen_info->service_addr_len),
767 (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
770 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
772 = GNUNET_SCHEDULER_add_delayed (scheduler,
773 GNUNET_TIME_relative_min (fc->back_off,
779 #if DEBUG_SERVICE_MANAGER
780 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
781 "Connected to service, now starting forwarding\n");
783 if (fc->client_to_service_task == GNUNET_SCHEDULER_NO_TASK)
785 if (fc->client_to_service_bufferDataLength == 0)
786 fc->client_to_service_task =
787 GNUNET_SCHEDULER_add_read_net (scheduler,
788 GNUNET_TIME_UNIT_FOREVER_REL,
790 &receiveFromClient, fc);
792 fc->client_to_service_task =
793 GNUNET_SCHEDULER_add_write_net (scheduler,
794 GNUNET_TIME_UNIT_FOREVER_REL,
795 fc->armServiceSocket,
796 &forwardToService, fc);
798 if (fc->service_to_client_task == GNUNET_SCHEDULER_NO_TASK)
800 if (fc->service_to_client_bufferDataLength == 0)
801 fc->service_to_client_task =
802 GNUNET_SCHEDULER_add_read_net (scheduler,
803 GNUNET_TIME_UNIT_FOREVER_REL,
804 fc->armServiceSocket,
805 &receiveFromService, fc);
807 fc->service_to_client_task =
808 GNUNET_SCHEDULER_add_write_net (scheduler,
809 GNUNET_TIME_UNIT_FOREVER_REL,
811 &forwardToClient, fc);
821 stop_listening (const char *serviceName)
823 struct ServiceListeningInfo *pos;
824 struct ServiceListeningInfo *next;
828 next = serviceListeningInfoList_head;
829 while (NULL != (pos = next))
832 if ( (serviceName != NULL) &&
833 (strcmp (pos->serviceName, serviceName) != 0) )
835 if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK)
836 GNUNET_SCHEDULER_cancel (scheduler, pos->acceptTask);
837 GNUNET_break (GNUNET_OK ==
838 GNUNET_NETWORK_socket_close (pos->listeningSocket));
839 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
840 serviceListeningInfoList_tail,
842 GNUNET_free (pos->serviceName);
843 GNUNET_free (pos->service_addr);
851 * First connection has come to the listening socket associated with the service,
852 * create the service in order to relay the incoming connection to it
854 * @param cls callback data, struct ServiceListeningInfo describing a listen socket
858 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
862 accept_and_forward (struct ServiceListeningInfo *serviceListeningInfo)
864 struct ForwardedConnection *fc;
866 fc = GNUNET_malloc (sizeof (struct ForwardedConnection));
867 fc->listen_info = serviceListeningInfo;
868 fc->service_to_client_bufferPos = fc->service_to_client_buffer;
869 fc->client_to_service_bufferPos = fc->client_to_service_buffer;
870 fc->client_addr_len = sizeof (fc->client_addr);
871 fc->armClientSocket = GNUNET_NETWORK_socket_accept (serviceListeningInfo->listeningSocket,
872 (struct sockaddr*) fc->client_addr,
873 &fc->client_addr_len);
874 if (NULL == fc->armClientSocket)
876 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
877 _("Unable to accept connection for service `%s': %s\n"),
878 serviceListeningInfo->serviceName,
881 GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
882 serviceListeningInfoList_tail,
883 serviceListeningInfo);
884 serviceListeningInfo->acceptTask =
885 GNUNET_SCHEDULER_add_read_net (scheduler,
886 GNUNET_TIME_UNIT_FOREVER_REL,
887 serviceListeningInfo->listeningSocket,
889 serviceListeningInfo);
892 GNUNET_break (GNUNET_OK ==
893 GNUNET_NETWORK_socket_close (serviceListeningInfo->listeningSocket));
894 start_service (NULL, serviceListeningInfo->serviceName, NULL);
895 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
896 _("Service `%s' started\n"),
897 fc->listen_info->serviceName);
898 fc->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_SERVICE_TIMEOUT);
899 fc->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
900 fc->client_to_service_task =
901 GNUNET_SCHEDULER_add_read_net (scheduler,
902 GNUNET_TIME_UNIT_FOREVER_REL,
904 &receiveFromClient, fc);
905 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
907 = GNUNET_SCHEDULER_add_now (scheduler,
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)
923 struct ServiceListeningInfo *sli = cls;
924 struct ServiceListeningInfo *pos;
925 struct ServiceListeningInfo *next;
930 sli->acceptTask = GNUNET_SCHEDULER_NO_TASK;
931 if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
933 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
934 serviceListeningInfoList_tail,
937 use_lsocks = GNUNET_YES;
938 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (cfg,
940 "DISABLE_SOCKET_FORWARDING"))
941 use_lsocks = GNUNET_CONFIGURATION_get_value_yesno (cfg,
943 "DISABLE_SOCKET_FORWARDING");
945 use_lsocks = GNUNET_NO;
947 if (GNUNET_YES != use_lsocks)
949 accept_and_forward (sli);
954 next = serviceListeningInfoList_head;
955 while (NULL != (pos = next))
958 if (0 == strcmp (pos->serviceName,
961 GNUNET_array_append (lsocks, ls,
962 GNUNET_NETWORK_get_fd (pos->listeningSocket));
963 GNUNET_free (pos->listeningSocket); /* deliberately no closing! */
964 GNUNET_free (pos->service_addr);
965 GNUNET_free (pos->serviceName);
966 GNUNET_SCHEDULER_cancel (scheduler,
968 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
969 serviceListeningInfoList_tail,
974 GNUNET_array_append (lsocks, ls,
975 GNUNET_NETWORK_get_fd (sli->listeningSocket));
976 GNUNET_free (sli->listeningSocket); /* deliberately no closing! */
977 GNUNET_free (sli->service_addr);
978 GNUNET_array_append (lsocks, ls, -1);
983 while (lsocks[ls] != -1)
984 GNUNET_break (0 == close (lsocks[ls++]));
985 GNUNET_array_grow (lsocks, ls, 0);
986 GNUNET_free (sli->serviceName);
992 * Creating a listening socket for each of the service's addresses and
993 * wait for the first incoming connection to it
995 * @param sa address associated with the service
996 * @param addr_len length of sa
997 * @param serviceName the name of the service in question
1000 createListeningSocket (struct sockaddr *sa,
1002 const char *serviceName)
1004 const static int on = 1;
1005 struct GNUNET_NETWORK_Handle *sock;
1006 struct ServiceListeningInfo *serviceListeningInfo;
1008 switch (sa->sa_family)
1011 sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
1014 sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
1017 sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
1022 errno = EAFNOSUPPORT;
1027 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1028 _("Unable to create socket for service `%s': %s\n"),
1034 if (GNUNET_NETWORK_socket_setsockopt
1035 (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
1036 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1039 if ( (sa->sa_family == AF_INET6) &&
1040 (GNUNET_NETWORK_socket_setsockopt
1041 (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
1042 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1046 if (GNUNET_NETWORK_socket_bind
1047 (sock, (const struct sockaddr *) sa, addr_len) != GNUNET_OK)
1049 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1050 _("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
1052 GNUNET_a2s (sa, addr_len),
1054 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1058 if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
1060 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1062 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1066 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1067 _("ARM now monitors connections to service `%s' at `%s'\n"),
1069 GNUNET_a2s (sa, addr_len));
1070 serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
1071 serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
1072 serviceListeningInfo->service_addr = sa;
1073 serviceListeningInfo->service_addr_len = addr_len;
1074 serviceListeningInfo->listeningSocket = sock;
1075 serviceListeningInfo->acceptTask =
1076 GNUNET_SCHEDULER_add_read_net (scheduler,
1077 GNUNET_TIME_UNIT_FOREVER_REL, sock,
1079 serviceListeningInfo);
1080 GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
1081 serviceListeningInfoList_tail,
1082 serviceListeningInfo);
1087 * Callback function, checks whether the current tokens are representing a service,
1088 * gets its addresses and create listening socket for it.
1090 * @param cls callback data, not used
1091 * @param section configuration section
1092 * @param option configuration option
1093 * @param value the option's value
1096 checkPortNumberCB (void *cls,
1097 const char *section,
1101 struct sockaddr **addrs;
1102 socklen_t *addr_lens;
1106 if ( (strcasecmp (section, "arm") == 0) ||
1107 (strcasecmp (option, "AUTOSTART") != 0) ||
1108 (strcasecmp (value, "YES") != 0) ||
1109 (isInDefaultList (section) == GNUNET_YES) )
1111 if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs,
1114 /* this will free (or capture) addrs[i] */
1115 for (i = 0; i < ret; i++)
1116 createListeningSocket (addrs[i], addr_lens[i], section);
1117 GNUNET_free (addrs);
1118 GNUNET_free (addr_lens);
1123 * Entry point to the Service Manager
1125 * @param configurationHandle configuration to use to get services
1126 * @param sched scheduler to handle clients and services communications
1129 prepareServices (const struct GNUNET_CONFIGURATION_Handle
1130 *configurationHandle, struct GNUNET_SCHEDULER_Handle *sched)
1132 char *defaultServicesString;
1135 cfg = configurationHandle;
1136 /* Split the default services into a list */
1138 GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
1139 &defaultServicesString))
1141 addDefaultServicesToList (defaultServicesString);
1142 GNUNET_free (defaultServicesString);
1144 /* Spot the services from the configuration and create a listening
1146 GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
1149 /* end of gnunet-service-arm_interceptor.c */