nodbg
[oweals/gnunet.git] / src / arm / gnunet-service-manager.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 Christian Grothoff (and other contributing authors)
4
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 2, or (at your
8      option) any later version.
9
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.
14
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.
19 */
20 /**
21  * @file arm/gnunet-service-manager.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.
26  *
27  * @author Safey Abdel Halim
28  * @author Christian Grothoff
29  */
30
31 #include "platform.h"
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"
38
39
40 #define DEBUG_SERVICE_MANAGER GNUNET_NO
41
42 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
43
44 #define BUFFER_SIZE (64 * 1024)
45
46 /**
47  * Problem forwarding from client to service.
48  */
49 #define REASON_CLIENT_TO_SERVICE 1
50
51 /**
52  * Problem forwarding from service to client.
53  */
54 #define REASON_SERVICE_TO_CLIENT 2
55
56 /**
57  * Problem in both directions.
58  */
59 #define REASON_ERROR 3
60
61
62 /**
63  *
64  */
65 struct ServiceListeningInfo
66 {
67   /**
68    * This is a linked list.
69    */
70   struct ServiceListeningInfo *next;
71
72   /**
73    * This is a linked list.
74    */
75   struct ServiceListeningInfo *prev;
76
77   /**
78    * Name of the service being forwarded.
79    */
80   char *serviceName;
81
82   /**
83    *
84    */
85   struct sockaddr *service_addr;
86
87   /**
88    *
89    */
90   socklen_t service_addr_len;
91
92   /**
93    * Our listening socket.
94    */
95   struct GNUNET_NETWORK_Handle *listeningSocket;
96
97   /**
98    * Task doing the accepting.
99    */
100   GNUNET_SCHEDULER_TaskIdentifier acceptTask;
101 };
102
103 /**
104  * Information of the connection: client-arm-service
105  */
106 struct ForwardedConnection
107 {
108   /**
109    *
110    */
111   struct GNUNET_NETWORK_Handle *armClientSocket;
112
113   /**
114    *
115    */
116   struct GNUNET_NETWORK_Handle *armServiceSocket;
117
118   /**
119    *
120    */
121   struct ServiceListeningInfo *listen_info;
122
123   /**
124    *
125    */
126   char service_to_client_buffer[BUFFER_SIZE];
127
128   /**
129    *
130    */
131   char client_to_service_buffer[BUFFER_SIZE];
132
133   /**
134    *
135    */
136   char client_addr[32];
137
138   /**
139    *
140    */
141   const char *client_to_service_bufferPos;
142
143   /**
144    *
145    */
146   const char *service_to_client_bufferPos;
147
148   /**
149    * Timeout for forwarding.
150    */
151   struct GNUNET_TIME_Absolute timeout;
152   
153   /**
154    * Current back-off value.
155    */
156   struct GNUNET_TIME_Relative back_off;
157
158   /**
159    *
160    */
161   GNUNET_SCHEDULER_TaskIdentifier client_to_service_task;
162
163   /**
164    *
165    */
166   GNUNET_SCHEDULER_TaskIdentifier service_to_client_task;
167
168   /**
169    *
170    */
171   ssize_t client_to_service_bufferDataLength;
172
173   /**
174    *
175    */
176   ssize_t service_to_client_bufferDataLength;
177
178   /**
179    *
180    */
181   socklen_t client_addr_len;
182
183 };
184
185
186 /**
187  * Array with the names of the services started by default.
188  */
189 static char **defaultServicesList;
190
191 /**
192  * Size of the defaultServicesList array.
193  */
194 static unsigned int numDefaultServices;
195
196 /**
197  *
198  */
199 static const struct GNUNET_CONFIGURATION_Handle *cfg;
200
201 /**
202  *
203  */
204 static struct GNUNET_SCHEDULER_Handle *scheduler;
205
206 /**
207  *
208  */
209 static struct ServiceListeningInfo *serviceListeningInfoList_head;
210
211 /**
212  *
213  */
214 static struct ServiceListeningInfo *serviceListeningInfoList_tail;
215
216
217 /**
218  * Put the default services represented by a space separated string into an array of strings
219  * 
220  * @param services space separated string of default services
221  */
222 static void
223 addDefaultServicesToList (const char *services)
224 {
225   unsigned int i;
226   const char *token;
227   char *s;
228
229   if (strlen (services) == 0)
230     return;
231   s = GNUNET_strdup (services);
232   token = strtok (s, " ");
233   while (NULL != token)
234     {
235       numDefaultServices++;
236       token = strtok (NULL, " ");
237     }
238   GNUNET_free (s);
239
240   defaultServicesList = GNUNET_malloc (numDefaultServices * sizeof (char *));
241   i = 0;
242   s = GNUNET_strdup (services);
243   token = strtok (s, " ");
244   while (NULL != token)
245     {
246       defaultServicesList[i++] = GNUNET_strdup (token);
247       token = strtok (NULL, " ");
248     }
249   GNUNET_free (s);
250   GNUNET_assert (i == numDefaultServices);
251 }
252
253 /**
254  * Checks whether the serviceName is in the list of default services
255  * 
256  * @param serviceName string to check its existance in the list
257  * @return GNUNET_YES if the service is started by default
258  */
259 static int
260 isInDefaultList (const char *serviceName)
261 {
262   unsigned int i;
263   for (i = 0; i < numDefaultServices; i++)
264     if (strcmp (serviceName, defaultServicesList[i]) == 0)
265       return GNUNET_YES;    
266   return GNUNET_NO;
267 }
268
269
270 /**
271  * Close forwarded connection (partial or full).
272  *
273  * @param fc connection to close 
274  * @param reason which direction to close
275  */
276 static void
277 closeClientAndServiceSockets (struct ForwardedConnection *fc, 
278                               int reason)
279 {
280   if (0 != (REASON_SERVICE_TO_CLIENT & reason)) 
281     {      
282 #if DEBUG_SERVICE_MANAGER
283       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284                   "Stopping forwarding from service to client\n",
285                   fc->listen_info->serviceName);
286 #endif
287       if (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK)
288         {
289           GNUNET_SCHEDULER_cancel (scheduler, fc->service_to_client_task);    
290           fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
291         }
292       if (fc->armClientSocket != NULL)
293         GNUNET_NETWORK_socket_shutdown (fc->armClientSocket,
294                                         SHUT_WR);
295       if (fc->armServiceSocket != NULL)
296         GNUNET_NETWORK_socket_shutdown (fc->armServiceSocket,
297                                         SHUT_RD);
298     }
299   if (0 != (REASON_CLIENT_TO_SERVICE & reason)) 
300     {
301 #if DEBUG_SERVICE_MANAGER
302       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
303                   "Stopping forwarding from client to service\n",
304                   fc->listen_info->serviceName);
305 #endif
306       if (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK) 
307         {
308           GNUNET_SCHEDULER_cancel (scheduler,
309                                    fc->client_to_service_task);
310           fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
311         }
312       if (fc->armClientSocket != NULL)
313         GNUNET_NETWORK_socket_shutdown (fc->armClientSocket,
314                                         SHUT_RD);
315       if (fc->armServiceSocket != NULL)
316         GNUNET_NETWORK_socket_shutdown (fc->armServiceSocket,
317                                         SHUT_WR);
318     }
319   if ( (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK) ||
320        (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK) )
321     return;
322 #if DEBUG_SERVICE_MANAGER
323   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
324               "Closing forwarding connection (done with both directions)\n");
325 #endif
326   if ( (NULL != fc->armClientSocket) &&
327        (GNUNET_SYSERR ==
328         GNUNET_NETWORK_socket_close (fc->armClientSocket)) )
329     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
330   if ( (NULL != fc->armServiceSocket) &&
331        (GNUNET_SYSERR ==
332         GNUNET_NETWORK_socket_close (fc->armServiceSocket)) )
333     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
334   GNUNET_free (fc->listen_info->serviceName);              
335   GNUNET_free (fc->listen_info->service_addr);
336   GNUNET_free (fc->listen_info);        
337   GNUNET_free (fc);
338 }
339
340
341 /**
342  *
343  */
344 static void
345 receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
346
347
348 /**
349  *
350  */
351 static void
352 receiveFromService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
353
354
355 /**
356  * Forward messages sent from service to client
357  * 
358  * @param cls callback data, struct ForwardedConnection for the communication between client and service
359  * @param tc context
360  */
361 static void
362 forwardToClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
363 {
364   struct ForwardedConnection *fc = cls;
365   ssize_t numberOfBytesSent;
366
367   fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
368   if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->write_ready,
369                                                 fc->armClientSocket))
370     {
371       fc->service_to_client_task = 
372         GNUNET_SCHEDULER_add_write_net (scheduler,
373                                         GNUNET_TIME_UNIT_FOREVER_REL,
374                                         fc->armClientSocket,
375                                         &forwardToClient, fc);
376       return;
377     }
378   /* Forwarding service response to client */
379   numberOfBytesSent =
380     GNUNET_NETWORK_socket_send (fc->armClientSocket,
381                                 fc->service_to_client_bufferPos,
382                                 fc->service_to_client_bufferDataLength);
383   if (numberOfBytesSent <= 0)
384     {
385       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
386                   "Failed to forward data to client: %s\n",
387                   STRERROR (errno));
388       closeClientAndServiceSockets (fc,
389                                     REASON_SERVICE_TO_CLIENT);
390       return;
391     }
392 #if DEBUG_SERVICE_MANAGER
393   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
394               "Forwarded %d bytes to client\n",
395               numberOfBytesSent);
396 #endif
397   if (numberOfBytesSent < fc->service_to_client_bufferDataLength)
398     {
399       fc->service_to_client_bufferPos += numberOfBytesSent;
400       fc->service_to_client_bufferDataLength -= numberOfBytesSent;
401       fc->service_to_client_task = 
402         GNUNET_SCHEDULER_add_write_net (scheduler, 
403                                         GNUNET_TIME_UNIT_FOREVER_REL,
404                                         fc->armClientSocket,
405                                         &forwardToClient, 
406                                         fc);
407       return;
408     }
409   fc->service_to_client_task =
410     GNUNET_SCHEDULER_add_read_net (scheduler, 
411                                    GNUNET_TIME_UNIT_FOREVER_REL,
412                                    fc->armServiceSocket,
413                                    &receiveFromService, 
414                                    fc);
415 }
416
417
418 /**
419  * Receive service messages sent by the service and forward it to client
420  * 
421  * @param cls callback data, struct ForwardedConnection for the communication between client and service
422  * @param tc scheduler context
423  */
424 static void
425 receiveFromService (void *cls, 
426                     const struct GNUNET_SCHEDULER_TaskContext *tc)
427 {
428   struct ForwardedConnection *fc = cls;
429
430   fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
431   if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->read_ready,
432                                                 fc->armServiceSocket))
433     {
434       fc->service_to_client_task =
435         GNUNET_SCHEDULER_add_read_net (scheduler,
436                                        GNUNET_TIME_UNIT_FOREVER_REL,
437                                        fc->armServiceSocket,
438                                        &receiveFromService, fc);
439       return;
440     }
441   fc->service_to_client_bufferPos = fc->service_to_client_buffer;
442   fc->service_to_client_bufferDataLength =
443     GNUNET_NETWORK_socket_recv (fc->armServiceSocket,
444                                 fc->service_to_client_buffer, 
445                                 BUFFER_SIZE);
446   if (fc->service_to_client_bufferDataLength <= 0)
447     {
448       if (fc->service_to_client_bufferDataLength == 0)
449         {
450 #if DEBUG_SERVICE_MANAGER
451           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
452                       "Service `%s' stopped sending data.\n",
453                       fc->listen_info->serviceName);
454 #endif
455         }
456       else
457         {         
458 #if DEBUG_SERVICE_MANAGER
459           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
460                       "Error receiving from service: %s\n", 
461                       STRERROR (errno));
462 #endif
463         }
464       closeClientAndServiceSockets (fc, REASON_SERVICE_TO_CLIENT);
465       return;
466     }
467 #if DEBUG_SERVICE_MANAGER
468   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
469               "Received %d bytes for client\n",
470               fc->service_to_client_bufferDataLength);
471 #endif
472   fc->service_to_client_task = 
473     GNUNET_SCHEDULER_add_write_net (scheduler, 
474                                     GNUNET_TIME_UNIT_FOREVER_REL,
475                                     fc->armClientSocket,
476                                     &forwardToClient, fc);
477 }
478
479
480 /**
481  * Forward client message to service
482  * 
483  * @param cls callback data, struct ForwardedConnection for the communication between client and service
484  * @param tc scheduler context
485  */
486 static void
487 forwardToService (void *cls, 
488                   const struct GNUNET_SCHEDULER_TaskContext *tc)
489 {
490   struct ForwardedConnection *fc = cls;
491   ssize_t numberOfBytesSent;
492
493   fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
494   if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->write_ready,
495                                                 fc->armServiceSocket))
496     {
497       fc->client_to_service_task = 
498         GNUNET_SCHEDULER_add_write_net (scheduler,
499                                         GNUNET_TIME_UNIT_FOREVER_REL,
500                                         fc->armServiceSocket,
501                                         &forwardToService, fc);
502       return;
503     }
504   numberOfBytesSent =
505     GNUNET_NETWORK_socket_send (fc->armServiceSocket,
506                                 fc->client_to_service_bufferPos,
507                                 fc->client_to_service_bufferDataLength);
508   if (numberOfBytesSent <= 0)
509     {
510       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
511                   "Failed to forward data to service: %s\n",
512                   STRERROR (errno));
513       closeClientAndServiceSockets (fc,
514                                     REASON_CLIENT_TO_SERVICE);
515       return;
516     }
517 #if DEBUG_SERVICE_MANAGER
518   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
519               "Forwarded %d bytes to service\n",
520               numberOfBytesSent);
521 #endif
522   if (numberOfBytesSent < fc->client_to_service_bufferDataLength)
523     {
524       fc->client_to_service_bufferPos += numberOfBytesSent;
525       fc->client_to_service_bufferDataLength -= numberOfBytesSent;
526       fc->client_to_service_task = 
527         GNUNET_SCHEDULER_add_write_net (scheduler, 
528                                         GNUNET_TIME_UNIT_FOREVER_REL,
529                                         fc->armServiceSocket,
530                                         &forwardToService, fc);
531       return;
532     }
533   fc->client_to_service_task =
534     GNUNET_SCHEDULER_add_read_net (scheduler, 
535                                    GNUNET_TIME_UNIT_FOREVER_REL,
536                                    fc->armClientSocket,
537                                    &receiveFromClient, fc);
538 }
539
540
541 /**
542  * Read data from the client and then forward it to the service.
543  * 
544  * @param cls callback data,   struct ForwardedConnection for the communication between client and service
545  * @param tc context 
546  */
547 static void
548 receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
549 {
550   struct ForwardedConnection *fc = cls;
551
552   fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
553   if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->read_ready,
554                                                 fc->armClientSocket))
555     {
556       fc->client_to_service_task =
557         GNUNET_SCHEDULER_add_read_net (scheduler,
558                                        GNUNET_TIME_UNIT_FOREVER_REL,
559                                        fc->armClientSocket,
560                                        &receiveFromClient, fc);
561       return;
562     }
563   fc->client_to_service_bufferPos = fc->client_to_service_buffer;
564   fc->client_to_service_bufferDataLength =
565     GNUNET_NETWORK_socket_recv (fc->armClientSocket,
566                                 fc->client_to_service_buffer, 
567                                 BUFFER_SIZE);
568   if (fc->client_to_service_bufferDataLength <= 0)
569     {
570       if (fc->client_to_service_bufferDataLength == 0)
571         {
572 #if DEBUG_SERVICE_MANAGER
573           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
574                       "Client closed connection with service `%s'\n",
575                       fc->listen_info->serviceName);
576 #endif
577         }
578       else
579         {
580 #if DEBUG_SERVICE_MANAGER
581           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
582                       "Error receiving from client: %s\n",
583                       STRERROR (errno));
584 #endif
585         }
586       closeClientAndServiceSockets (fc, REASON_CLIENT_TO_SERVICE);
587       return;
588     }
589 #if DEBUG_SERVICE_MANAGER
590   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
591               "Received %d bytes for service\n",
592               fc->client_to_service_bufferDataLength);
593 #endif
594   fc->client_to_service_task = 
595     GNUNET_SCHEDULER_add_write_net (scheduler,
596                                     GNUNET_TIME_UNIT_FOREVER_REL,
597                                     fc->armServiceSocket,
598                                     &forwardToService, fc);
599 }
600
601
602 /**
603  *
604  */
605 static void
606 start_forwarding (void *cls,
607                   const struct GNUNET_SCHEDULER_TaskContext *tc)
608 {
609   struct ForwardedConnection *fc = cls;
610   struct GNUNET_TIME_Relative rem;
611
612   if ( (NULL != tc) &&
613        (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) )
614     {
615       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
616                   _("Unable to forward to service `%s': shutdown\n"),
617                   fc->listen_info->serviceName);
618       closeClientAndServiceSockets (fc, REASON_ERROR);
619       return;
620     }
621   rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
622   fc->armServiceSocket =
623     GNUNET_NETWORK_socket_create (fc->listen_info->service_addr->sa_family,
624                                   SOCK_STREAM, 0);
625   if (NULL == fc->armServiceSocket)
626     {
627       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
628                   _ ("Unable to start service `%s': %s\n"),
629                   fc->listen_info->serviceName,
630                   STRERROR (errno));
631       closeClientAndServiceSockets (fc, REASON_ERROR);
632       return;
633     }
634   if ((GNUNET_SYSERR ==
635        GNUNET_NETWORK_socket_connect (fc->armServiceSocket,
636                                       fc->listen_info->service_addr,
637                                       fc->listen_info->service_addr_len)) &&
638       (EINPROGRESS != errno) )
639     {
640       if (rem.value == 0)
641         {
642           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
643                       _("Unable to forward to service `%s': timeout before connect\n"),
644                       fc->listen_info->serviceName);
645           closeClientAndServiceSockets (fc, REASON_ERROR);
646           return;
647         }
648       fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
649       GNUNET_SCHEDULER_add_delayed (scheduler,
650                                     GNUNET_TIME_relative_min (fc->back_off,
651                                                               rem),
652                                     &start_forwarding,
653                                     fc);
654     }
655 #if DEBUG_SERVICE_MANAGER
656   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
657               "Connected to service, now starting forwarding\n");
658 #endif
659   fc->client_to_service_task =
660     GNUNET_SCHEDULER_add_read_net (scheduler,
661                                    GNUNET_TIME_UNIT_FOREVER_REL,
662                                    fc->armClientSocket,
663                                    &receiveFromClient, fc);
664   fc->service_to_client_task =
665     GNUNET_SCHEDULER_add_read_net (scheduler,
666                                    GNUNET_TIME_UNIT_FOREVER_REL,
667                                    fc->armServiceSocket,
668                                    &receiveFromService, fc);
669 }
670
671
672 /**
673  * ARM connects to the just created service, 
674  * starts the processes for relaying messages between the client and the service
675  * 
676  * @param cls callback data, struct ForwardedConnection for the communication between client and service
677  * @param tc context
678  */
679 static void
680 connectToService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
681 {
682   struct ForwardedConnection *fc = cls;
683
684   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
685     {
686       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
687                   _("Unable to start service `%s': shutdown\n"),
688                   fc->listen_info->serviceName);
689       closeClientAndServiceSockets (fc,
690                                     (REASON_CLIENT_TO_SERVICE & REASON_SERVICE_TO_CLIENT));
691       return;
692     }
693   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
694     {
695       /* Service is not up. Unable to proceed */
696       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
697                   _("Unable to start service `%s': timeout\n"),
698                   fc->listen_info->serviceName);
699       closeClientAndServiceSockets (fc,
700                                     (REASON_CLIENT_TO_SERVICE & REASON_SERVICE_TO_CLIENT));
701       return;
702     }
703   GNUNET_break (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE));
704   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
705               _("Service `%s' started\n"),
706               fc->listen_info->serviceName);
707   fc->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_SERVICE_TIMEOUT);
708   fc->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
709   start_forwarding (fc, NULL);
710 }
711
712
713 /**
714  *
715  */
716 int
717 stop_listening (const char *serviceName)
718 {
719   struct ServiceListeningInfo *pos;
720   struct ServiceListeningInfo *next;
721   int ret;
722   
723   ret = GNUNET_NO;
724   next = serviceListeningInfoList_head;
725   while (NULL != (pos = next))
726     {
727       next = pos->next;
728       if ( (serviceName != NULL) &&
729            (strcmp (pos->serviceName, serviceName) != 0) )
730         continue;
731       GNUNET_SCHEDULER_cancel (scheduler, pos->acceptTask);
732       GNUNET_break (GNUNET_OK ==
733                     GNUNET_NETWORK_socket_close (pos->listeningSocket));
734       GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
735                                    serviceListeningInfoList_tail, 
736                                    pos);
737       GNUNET_free (pos->serviceName);              
738       GNUNET_free (pos->service_addr);
739       GNUNET_free (pos); 
740       ret = GNUNET_OK;
741     }
742   return ret;
743 }
744
745
746 /**
747  * First connection has come to the listening socket associated with the service,
748  * create the service in order to relay the incoming connection to it
749  * 
750  * @param cls callback data, struct ServiceListeningInfo describing a listen socket
751  * @param tc context 
752  */
753 static void
754 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
755 {
756   struct ServiceListeningInfo *serviceListeningInfo = cls;
757   struct ForwardedConnection *fc;
758
759   serviceListeningInfo->acceptTask = GNUNET_SCHEDULER_NO_TASK;
760   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
761     return;
762   fc = GNUNET_malloc (sizeof (struct ForwardedConnection));
763   fc->listen_info = serviceListeningInfo;
764   fc->service_to_client_bufferPos = fc->service_to_client_buffer;
765   fc->client_to_service_bufferPos = fc->client_to_service_buffer;
766   fc->client_addr_len = sizeof (fc->client_addr);
767   fc->armClientSocket = GNUNET_NETWORK_socket_accept (serviceListeningInfo->listeningSocket,
768                                                       (struct sockaddr*) fc->client_addr,
769                                                       &fc->client_addr_len);
770   if (NULL == fc->armClientSocket)
771     {
772       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
773                   _("Unable to accept connection for service `%s': %s\n"),
774                   serviceListeningInfo->serviceName,
775                   STRERROR (errno));
776       GNUNET_free (fc);
777       serviceListeningInfo->acceptTask =
778         GNUNET_SCHEDULER_add_read_net (scheduler,
779                                        GNUNET_TIME_UNIT_FOREVER_REL, 
780                                        serviceListeningInfo->listeningSocket,
781                                        &acceptConnection,
782                                        serviceListeningInfo);
783       return;
784     }
785   GNUNET_break (GNUNET_OK ==
786                 GNUNET_NETWORK_socket_close (serviceListeningInfo->listeningSocket));
787   GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
788                                serviceListeningInfoList_tail, 
789                                serviceListeningInfo);
790   start_service (NULL, serviceListeningInfo->serviceName);
791   GNUNET_CLIENT_service_test (scheduler,
792                               serviceListeningInfo->serviceName, 
793                               cfg,
794                               TIMEOUT,
795                               &connectToService,
796                               fc);    
797 }
798
799
800 /**
801  * Creating a listening socket for each of the service's addresses and
802  * wait for the first incoming connection to it
803  * 
804  * @param sa address associated with the service
805  * @param addr_len length of sa
806  * @param serviceName the name of the service in question
807  */
808 static void
809 createListeningSocket (struct sockaddr *sa, 
810                        socklen_t addr_len,
811                        const char *serviceName)
812 {
813   const static int on = 1;
814   struct GNUNET_NETWORK_Handle *sock;
815   struct ServiceListeningInfo *serviceListeningInfo;
816
817   switch (sa->sa_family)
818     {
819     case AF_INET:
820       sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
821       break;
822     case AF_INET6:
823       sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
824       break;
825     default:
826       sock = NULL;
827       break;
828     }
829   if (NULL == sock)
830     {
831       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
832                   _("Unable to create socket for service `%s'"),
833                   serviceName);
834       GNUNET_free (sa);
835       return;
836     }
837   if (GNUNET_NETWORK_socket_setsockopt
838       (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
839     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
840                          "setsockopt");
841 #ifdef IPV6_V6ONLY
842   if ( (sa->sa_family == AF_INET6) &&
843        (GNUNET_NETWORK_socket_setsockopt
844         (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
845     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
846                          "setsockopt");
847 #endif
848
849   if (GNUNET_NETWORK_socket_bind
850       (sock, (const struct sockaddr *) sa, addr_len) != GNUNET_OK)
851     {
852       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
853                   _("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
854                   serviceName,
855                   GNUNET_a2s (sa, addr_len),
856                   STRERROR (errno));
857       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
858       GNUNET_free (sa);
859       return;
860     }
861   if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
862     {
863       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
864                            "listen");
865       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
866       GNUNET_free (sa);
867       return;
868     }
869   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
870               _("ARM now monitors connections to service `%s' at `%s'\n"),
871               serviceName,
872               GNUNET_a2s (sa, addr_len));
873   serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
874   serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
875   serviceListeningInfo->service_addr = sa;
876   serviceListeningInfo->service_addr_len = addr_len;
877   serviceListeningInfo->listeningSocket = sock;
878   serviceListeningInfo->acceptTask =
879     GNUNET_SCHEDULER_add_read_net (scheduler,
880                                    GNUNET_TIME_UNIT_FOREVER_REL, sock,
881                                    &acceptConnection,
882                                    serviceListeningInfo);
883   GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
884                                serviceListeningInfoList_tail,
885                                serviceListeningInfo);
886 }
887
888
889 /**
890  * Callback function, checks whether the current tokens are representing a service,
891  * gets its addresses and create listening socket for it.
892  * 
893  * @param cls callback data, not used
894  * @param section configuration section
895  * @param option configuration option
896  * @param value the option's value
897  */
898 static void
899 checkPortNumberCB (void *cls,
900                    const char *section, 
901                    const char *option, 
902                    const char *value)
903 {
904   struct sockaddr **addrs;
905   socklen_t *addr_lens;
906   int ret;
907   unsigned int i;
908   
909   if ( (strcasecmp (section, "arm") == 0) ||
910        (strcasecmp (option, "AUTOSTART") != 0) ||
911        (strcasecmp (value, "YES") != 0) ||
912        (isInDefaultList (section) == GNUNET_YES) )
913     return;
914   if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs,
915                                                        &addr_lens)))
916     return;
917   /* this will free (or capture) addrs[i] for i in 0..ret */
918   for (i = 0; i < ret; i++)
919     createListeningSocket (addrs[i], addr_lens[i], section);
920   GNUNET_free (addrs);
921   GNUNET_free (addr_lens);
922 }
923
924
925 /**
926  * Entry point to the Service Manager
927  *
928  * @param configurationHandle configuration to use to get services
929  * @param sched scheduler to handle clients and services communications
930  */
931 void
932 prepareServices (const struct GNUNET_CONFIGURATION_Handle
933                  *configurationHandle, struct GNUNET_SCHEDULER_Handle *sched)
934 {
935   char *defaultServicesString;
936
937   scheduler = sched;
938   cfg = configurationHandle;
939   /* Split the default services into a list */
940   if (GNUNET_OK ==
941       GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
942                                              &defaultServicesString))
943     {
944       addDefaultServicesToList (defaultServicesString);
945       GNUNET_free (defaultServicesString);    
946     }
947   /* Spot the services from the configuration and create a listening
948      socket for each */
949   GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
950 }
951
952 /* end of gnunet-service-manager.c */