cleaning up ARM shutdown
[oweals/gnunet.git] / src / arm / gnunet-service-arm_interceptor.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 3, 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-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.
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 BUFFER_SIZE (64 * 1024)
43
44 /**
45  * Problem forwarding from client to service.
46  */
47 #define REASON_CLIENT_TO_SERVICE 1
48
49 /**
50  * Problem forwarding from service to client.
51  */
52 #define REASON_SERVICE_TO_CLIENT 2
53
54 /**
55  * Problem in both directions.
56  */
57 #define REASON_ERROR 3
58
59 struct ForwardedConnection;
60
61 /**
62  *
63  */
64 struct ServiceListeningInfo
65 {
66   /**
67    * This is a linked list.
68    */
69   struct ServiceListeningInfo *next;
70
71   /**
72    * This is a linked list.
73    */
74   struct ServiceListeningInfo *prev;
75
76   /**
77    * Name of the service being forwarded.
78    */
79   char *serviceName;
80
81   /**
82    *
83    */
84   struct sockaddr *service_addr;
85
86   /**
87    *
88    */
89   socklen_t service_addr_len;
90
91   /**
92    * Our listening socket.
93    */
94   struct GNUNET_NETWORK_Handle *listeningSocket;
95
96   /**
97    *
98    */
99   struct ForwardedConnection *fc;
100
101   /**
102    * Task doing the accepting.
103    */
104   GNUNET_SCHEDULER_TaskIdentifier acceptTask;
105 };
106
107 /**
108  * Information of the connection: client-arm-service
109  */
110 struct ForwardedConnection
111 {
112   /**
113    *
114    */
115   struct GNUNET_NETWORK_Handle *armClientSocket;
116
117   /**
118    *
119    */
120   struct GNUNET_NETWORK_Handle *armServiceSocket;
121
122   /**
123    *
124    */
125   struct ServiceListeningInfo *listen_info;
126
127   /**
128    *
129    */
130   char service_to_client_buffer[BUFFER_SIZE];
131
132   /**
133    *
134    */
135   char client_to_service_buffer[BUFFER_SIZE];
136
137   /**
138    *
139    */
140   char client_addr[32];
141
142   /**
143    *
144    */
145   const char *client_to_service_bufferPos;
146
147   /**
148    *
149    */
150   const char *service_to_client_bufferPos;
151
152   /**
153    * Timeout for forwarding.
154    */
155   struct GNUNET_TIME_Absolute timeout;
156   
157   /**
158    * Current back-off value.
159    */
160   struct GNUNET_TIME_Relative back_off;
161   
162   /**
163    * Task that tries to initiate forwarding.
164    */
165   GNUNET_SCHEDULER_TaskIdentifier start_task;
166
167   /**
168    *
169    */
170   GNUNET_SCHEDULER_TaskIdentifier client_to_service_task;
171
172   /**
173    *
174    */
175   GNUNET_SCHEDULER_TaskIdentifier service_to_client_task;
176
177   /**
178    *
179    */
180   ssize_t client_to_service_bufferDataLength;
181
182   /**
183    *
184    */
185   ssize_t service_to_client_bufferDataLength;
186
187   /**
188    *
189    */
190   socklen_t client_addr_len;
191
192   /**
193    * Have we ever successfully written data to the service?
194    */
195   int first_write_done;
196
197 };
198
199 /**
200  * Array with the names of the services started by default.
201  */
202 static char **defaultServicesList;
203
204 /**
205  * Size of the defaultServicesList array.
206  */
207 static unsigned int numDefaultServices;
208
209 /**
210  *
211  */
212 static const struct GNUNET_CONFIGURATION_Handle *cfg;
213
214 /**
215  *
216  */
217 static struct ServiceListeningInfo *serviceListeningInfoList_head;
218
219 /**
220  *
221  */
222 static struct ServiceListeningInfo *serviceListeningInfoList_tail;
223
224
225 /**
226  * Put the default services represented by a space separated string into an array of strings
227  * 
228  * @param services space separated string of default services
229  */
230 static void
231 addDefaultServicesToList (const char *services)
232 {
233   unsigned int i;
234   const char *token;
235   char *s;
236
237   if (strlen (services) == 0)
238     return;
239   s = GNUNET_strdup (services);
240   token = strtok (s, " ");
241   while (NULL != token)
242     {
243       numDefaultServices++;
244       token = strtok (NULL, " ");
245     }
246   GNUNET_free (s);
247
248   defaultServicesList = GNUNET_malloc (numDefaultServices * sizeof (char *));
249   i = 0;
250   s = GNUNET_strdup (services);
251   token = strtok (s, " ");
252   while (NULL != token)
253     {
254       defaultServicesList[i++] = GNUNET_strdup (token);
255       token = strtok (NULL, " ");
256     }
257   GNUNET_free (s);
258   GNUNET_assert (i == numDefaultServices);
259 }
260
261 /**
262  * Checks whether the serviceName is in the list of default services
263  * 
264  * @param serviceName string to check its existance in the list
265  * @return GNUNET_YES if the service is started by default
266  */
267 static int
268 isInDefaultList (const char *serviceName)
269 {
270   unsigned int i;
271   for (i = 0; i < numDefaultServices; i++)
272     if (strcmp (serviceName, defaultServicesList[i]) == 0)
273       return GNUNET_YES;    
274   return GNUNET_NO;
275 }
276
277
278 /**
279  * Close forwarded connection (partial or full).
280  *
281  * @param fc connection to close 
282  * @param reason which direction to close
283  */
284 static void
285 closeClientAndServiceSockets (struct ForwardedConnection *fc, 
286                               int reason)
287 {
288   if (0 != (REASON_SERVICE_TO_CLIENT & reason)) 
289     {      
290 #if DEBUG_SERVICE_MANAGER
291       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292                   "Stopping forwarding from service to client\n",
293                   fc->listen_info->serviceName);
294 #endif
295       if (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK)
296         {
297           GNUNET_SCHEDULER_cancel (fc->service_to_client_task);
298           fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
299         }
300       if (fc->armClientSocket != NULL)
301         GNUNET_NETWORK_socket_shutdown (fc->armClientSocket,
302                                         SHUT_WR);
303       if (fc->armServiceSocket != NULL)
304         GNUNET_NETWORK_socket_shutdown (fc->armServiceSocket,
305                                         SHUT_RD);
306     }
307   if (0 != (REASON_CLIENT_TO_SERVICE & reason)) 
308     {
309 #if DEBUG_SERVICE_MANAGER
310       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
311                   "Stopping forwarding from client to service\n",
312                   fc->listen_info->serviceName);
313 #endif
314       if (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK) 
315         {
316           GNUNET_SCHEDULER_cancel (fc->client_to_service_task);
317           fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
318         }
319       if (fc->armClientSocket != NULL)
320         GNUNET_NETWORK_socket_shutdown (fc->armClientSocket,
321                                         SHUT_RD);
322       if (fc->armServiceSocket != NULL)
323         GNUNET_NETWORK_socket_shutdown (fc->armServiceSocket,
324                                         SHUT_WR);
325     }
326   if ( (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK) ||
327        (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK) )
328     return;
329 #if DEBUG_SERVICE_MANAGER
330   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
331               "Closing forwarding connection (done with both directions)\n");
332 #endif
333   if (fc->start_task != GNUNET_SCHEDULER_NO_TASK)
334     GNUNET_SCHEDULER_cancel (fc->start_task);
335   if ( (NULL != fc->armClientSocket) &&
336        (GNUNET_SYSERR ==
337         GNUNET_NETWORK_socket_close (fc->armClientSocket)) )
338     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
339   if ( (NULL != fc->armServiceSocket) &&
340        (GNUNET_SYSERR ==
341         GNUNET_NETWORK_socket_close (fc->armServiceSocket)) )
342     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
343   GNUNET_free (fc->listen_info->serviceName);              
344   GNUNET_free (fc->listen_info->service_addr);
345   GNUNET_free (fc->listen_info);        
346   GNUNET_free (fc);
347 }
348
349
350 /**
351  * Read data from the client and then forward it to the service.
352  * 
353  * @param cls callback data,   struct ForwardedConnection for the communication between client and service
354  * @param tc context 
355  */
356 static void
357 receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
358
359
360 /**
361  * Receive service messages sent by the service and forward it to client
362  * 
363  * @param cls callback data, struct ForwardedConnection for the communication between client and service
364  * @param tc scheduler context
365  */
366 static void
367 receiveFromService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
368
369
370 /**
371  *
372  */
373 static void
374 start_forwarding (void *cls,
375                   const struct GNUNET_SCHEDULER_TaskContext *tc);
376
377
378
379 /**
380  * Forward messages sent from service to client
381  * 
382  * @param cls callback data, struct ForwardedConnection for the communication between client and service
383  * @param tc context
384  */
385 static void
386 forwardToClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
387 {
388   struct ForwardedConnection *fc = cls;
389   ssize_t numberOfBytesSent;
390
391   fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
392   if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->write_ready,
393                                                 fc->armClientSocket))
394     {
395       fc->service_to_client_task = 
396         GNUNET_SCHEDULER_add_write_net (
397                                         GNUNET_TIME_UNIT_FOREVER_REL,
398                                         fc->armClientSocket,
399                                         &forwardToClient, fc);
400       return;
401     }
402   /* Forwarding service response to client */
403   numberOfBytesSent =
404     GNUNET_NETWORK_socket_send (fc->armClientSocket,
405                                 fc->service_to_client_bufferPos,
406                                 fc->service_to_client_bufferDataLength);
407   if (numberOfBytesSent <= 0)
408     {
409       if ( (errno != EPIPE) &&
410            (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,
414                     STRERROR (errno));
415       closeClientAndServiceSockets (fc,
416                                     REASON_SERVICE_TO_CLIENT);
417       return;
418     }
419 #if DEBUG_SERVICE_MANAGER
420   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
421               "Forwarded %d bytes to client\n",
422               numberOfBytesSent);
423 #endif
424   if (numberOfBytesSent < fc->service_to_client_bufferDataLength)
425     {
426       fc->service_to_client_bufferPos += numberOfBytesSent;
427       fc->service_to_client_bufferDataLength -= numberOfBytesSent;
428       fc->service_to_client_task = 
429         GNUNET_SCHEDULER_add_write_net (
430                                         GNUNET_TIME_UNIT_FOREVER_REL,
431                                         fc->armClientSocket,
432                                         &forwardToClient, 
433                                         fc);
434       return;
435     }
436   fc->service_to_client_task =
437     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
438                                    fc->armServiceSocket,
439                                    &receiveFromService, 
440                                    fc);
441 }
442
443
444 /**
445  * Receive service messages sent by the service and forward it to client
446  * 
447  * @param cls callback data, struct ForwardedConnection for the communication between client and service
448  * @param tc scheduler context
449  */
450 static void
451 receiveFromService (void *cls, 
452                     const struct GNUNET_SCHEDULER_TaskContext *tc)
453 {
454   struct ForwardedConnection *fc = cls;
455   struct GNUNET_TIME_Relative rem;
456
457   fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
458   if ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
459        (fc->first_write_done != GNUNET_YES) )
460     {
461       closeClientAndServiceSockets (fc, REASON_ERROR);
462       return;
463     }
464   if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->read_ready,
465                                                 fc->armServiceSocket))
466     {
467       fc->service_to_client_task =
468         GNUNET_SCHEDULER_add_read_net (
469                                        GNUNET_TIME_UNIT_FOREVER_REL,
470                                        fc->armServiceSocket,
471                                        &receiveFromService, fc);
472       return;
473     }
474   fc->service_to_client_bufferPos = fc->service_to_client_buffer;
475   fc->service_to_client_bufferDataLength =
476     GNUNET_NETWORK_socket_recv (fc->armServiceSocket,
477                                 fc->service_to_client_buffer, 
478                                 BUFFER_SIZE);
479   if (fc->service_to_client_bufferDataLength <= 0)
480     {
481 #if DEBUG_SERVICE_MANAGER
482       if (fc->service_to_client_bufferDataLength == 0)
483         {
484           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
485                       "Service `%s' stopped sending data.\n",
486                       fc->listen_info->serviceName);
487         }
488 #endif
489       if (fc->first_write_done != GNUNET_YES)
490         {
491           fc->service_to_client_bufferDataLength = 0;
492           GNUNET_break (GNUNET_OK ==
493                         GNUNET_NETWORK_socket_close (fc->armServiceSocket));
494           fc->armServiceSocket = NULL;
495           if ( (fc->client_to_service_bufferDataLength > 0) &&
496                (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK) )
497             {
498               GNUNET_SCHEDULER_cancel (fc->client_to_service_task);
499               fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
500             }
501           fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
502 #if DEBUG_SERVICE_MANAGER
503           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
504                       "Failed to connected to service `%s' at `%s', will try again in %llu ms\n",
505                       fc->listen_info->serviceName,
506                       GNUNET_a2s (fc->listen_info->service_addr,
507                                   fc->listen_info->service_addr_len),
508                       (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
509                                                                      rem).rel_value);
510 #endif
511           rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
512           GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
513           fc->start_task
514             = GNUNET_SCHEDULER_add_delayed (
515                                             GNUNET_TIME_relative_min (fc->back_off,
516                                                                       rem),
517                                             &start_forwarding,
518                                             fc);
519         }
520       else
521         {
522 #if DEBUG_SERVICE_MANAGER
523           if (fc->service_to_client_bufferDataLength != 0)
524             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
525                         "Error receiving from service: %s\n", 
526                         STRERROR (errno));
527 #endif
528           closeClientAndServiceSockets (fc, REASON_SERVICE_TO_CLIENT);
529         }
530       return;
531     }
532   fc->first_write_done = GNUNET_YES;
533 #if DEBUG_SERVICE_MANAGER
534   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
535               "Received %d bytes for client\n",
536               fc->service_to_client_bufferDataLength);
537 #endif
538   fc->service_to_client_task = 
539     GNUNET_SCHEDULER_add_write_net (
540                                     GNUNET_TIME_UNIT_FOREVER_REL,
541                                     fc->armClientSocket,
542                                     &forwardToClient, fc);
543 }
544
545
546 /**
547  * Forward client message to service
548  * 
549  * @param cls callback data, struct ForwardedConnection for the communication between client and service
550  * @param tc scheduler context
551  */
552 static void
553 forwardToService (void *cls, 
554                   const struct GNUNET_SCHEDULER_TaskContext *tc)
555 {
556   struct ForwardedConnection *fc = cls;
557   ssize_t numberOfBytesSent;
558   struct GNUNET_TIME_Relative rem;
559
560   fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
561   if ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
562        (fc->first_write_done != GNUNET_YES) )
563     {
564       closeClientAndServiceSockets (fc, REASON_ERROR);
565       return;
566     }
567   if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->write_ready,
568                                                 fc->armServiceSocket))
569     {
570       fc->client_to_service_task = 
571         GNUNET_SCHEDULER_add_write_net (
572                                         GNUNET_TIME_UNIT_FOREVER_REL,
573                                         fc->armServiceSocket,
574                                         &forwardToService, fc);
575       return;
576     }
577   numberOfBytesSent =
578     GNUNET_NETWORK_socket_send (fc->armServiceSocket,
579                                 fc->client_to_service_bufferPos,
580                                 fc->client_to_service_bufferDataLength);
581   if (numberOfBytesSent <= 0)
582     {
583       if (GNUNET_YES != fc->first_write_done)
584         {
585           GNUNET_break (GNUNET_OK ==
586                         GNUNET_NETWORK_socket_close (fc->armServiceSocket));
587           fc->armServiceSocket = NULL;
588           if ( (fc->service_to_client_bufferDataLength == 0) &&
589                (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK) )
590             {
591               GNUNET_SCHEDULER_cancel (fc->service_to_client_task);
592               fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
593             }
594           fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
595 #if DEBUG_SERVICE_MANAGER
596           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
597                       "Failed to connect to service `%s' at `%s', will try again in %llu ms\n",
598                       fc->listen_info->serviceName,
599                       GNUNET_a2s (fc->listen_info->service_addr,
600                                   fc->listen_info->service_addr_len),
601                       (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
602                                                                      rem).rel_value);
603 #endif
604           rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
605           GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
606           fc->start_task 
607             = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_min (fc->back_off,
608                                                                       rem),
609                                             &start_forwarding,
610                                             fc);
611         }
612       else
613         {
614           if ( (errno != EPIPE) &&
615                (errno != ECONNRESET) )
616             GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
617                         "Failed to forward data to service: %s\n",
618                         STRERROR (errno));
619           closeClientAndServiceSockets (fc,
620                                         REASON_CLIENT_TO_SERVICE);
621         }
622       return;
623     }
624 #if DEBUG_SERVICE_MANAGER
625   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
626               "Forwarded %d bytes to service\n",
627               numberOfBytesSent);
628 #endif
629   fc->first_write_done = GNUNET_YES;
630   if (numberOfBytesSent < fc->client_to_service_bufferDataLength)
631     {
632       fc->client_to_service_bufferPos += numberOfBytesSent;
633       fc->client_to_service_bufferDataLength -= numberOfBytesSent;
634       fc->client_to_service_task = 
635         GNUNET_SCHEDULER_add_write_net (
636                                         GNUNET_TIME_UNIT_FOREVER_REL,
637                                         fc->armServiceSocket,
638                                         &forwardToService, fc);
639       return;
640     }
641   fc->client_to_service_task =
642     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
643                                    fc->armClientSocket,
644                                    &receiveFromClient, fc);
645 }
646
647
648 /**
649  * Read data from the client and then forward it to the service.
650  * 
651  * @param cls callback data,   struct ForwardedConnection for the communication between client and service
652  * @param tc context 
653  */
654 static void
655 receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
656 {
657   struct ForwardedConnection *fc = cls;
658
659   fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
660   if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->read_ready,
661                                                 fc->armClientSocket))
662     {
663       fc->client_to_service_task =
664         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
665                                        fc->armClientSocket,
666                                        &receiveFromClient, fc);
667       return;
668     }
669   fc->client_to_service_bufferPos = fc->client_to_service_buffer;
670   fc->client_to_service_bufferDataLength =
671     GNUNET_NETWORK_socket_recv (fc->armClientSocket,
672                                 fc->client_to_service_buffer, 
673                                 BUFFER_SIZE);
674   if (fc->client_to_service_bufferDataLength <= 0)
675     {
676       if (fc->client_to_service_bufferDataLength == 0)
677         {
678 #if DEBUG_SERVICE_MANAGER
679           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
680                       "Client closed connection with service `%s'\n",
681                       fc->listen_info->serviceName);
682 #endif
683         }
684       else
685         {
686 #if DEBUG_SERVICE_MANAGER
687           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
688                       "Error receiving from client: %s\n",
689                       STRERROR (errno));
690 #endif
691         }
692       closeClientAndServiceSockets (fc, REASON_CLIENT_TO_SERVICE);
693       return;
694     }
695 #if DEBUG_SERVICE_MANAGER
696   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
697               "Received %d bytes for service\n",
698               fc->client_to_service_bufferDataLength);
699 #endif
700   if (fc->armServiceSocket != NULL)        
701     fc->client_to_service_task = 
702       GNUNET_SCHEDULER_add_write_net (
703                                       GNUNET_TIME_UNIT_FOREVER_REL,
704                                       fc->armServiceSocket,
705                                       &forwardToService, fc);
706 }
707
708
709 static void
710 fc_acceptConnection (void *cls, 
711                      const struct GNUNET_SCHEDULER_TaskContext *tc)
712 {
713   struct ServiceListeningInfo *sli = cls;
714   struct ForwardedConnection *fc = sli->fc;
715
716   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))
717     {
718       GNUNET_assert (GNUNET_OK == GNUNET_NETWORK_socket_close (sli->listeningSocket));
719       closeClientAndServiceSockets (fc, REASON_ERROR);
720       GNUNET_free (sli);
721       return;
722     }
723 #if DEBUG_SERVICE_MANAGER
724   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
725               "Connected to service, now starting forwarding\n");
726 #endif
727   fc->armServiceSocket = sli->listeningSocket;
728   GNUNET_free (fc->listen_info->service_addr);
729   fc->listen_info->service_addr = sli->service_addr;
730   fc->listen_info->service_addr_len = sli->service_addr_len;
731   if (fc->client_to_service_task == GNUNET_SCHEDULER_NO_TASK)
732     {
733       if (fc->client_to_service_bufferDataLength == 0) 
734         fc->client_to_service_task =
735           GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
736                                          fc->armClientSocket,
737                                          &receiveFromClient, fc);
738       else
739         fc->client_to_service_task = 
740           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
741                                           fc->armServiceSocket,
742                                           &forwardToService, fc);
743     }
744   if (fc->service_to_client_task == GNUNET_SCHEDULER_NO_TASK)
745     {
746       if (fc->service_to_client_bufferDataLength == 0) 
747         fc->service_to_client_task =
748           GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
749                                          fc->armServiceSocket,
750                                          &receiveFromService, fc);
751       else
752         fc->service_to_client_task = 
753           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
754                                           fc->armClientSocket,
755                                           &forwardToClient, fc);
756     }
757   GNUNET_free (sli);
758 }
759
760
761 static struct ServiceListeningInfo *
762 service_try_to_connect (const struct sockaddr *addr, 
763                         int pf,
764                         socklen_t addrlen, 
765                         struct ForwardedConnection *fc)
766 {
767   struct GNUNET_NETWORK_Handle *sock;
768   struct ServiceListeningInfo *serviceListeningInfo;
769
770   sock = GNUNET_NETWORK_socket_create (pf, SOCK_STREAM, 0);
771   if (sock == NULL)
772     {
773       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket");
774       return NULL;
775     }  
776   if ( (GNUNET_SYSERR == GNUNET_NETWORK_socket_connect (sock, addr, addrlen)) &&
777        (errno != EINPROGRESS) )
778     {
779       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
780       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
781       return NULL;
782     }  
783   serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
784   serviceListeningInfo->serviceName = NULL;
785   serviceListeningInfo->service_addr = GNUNET_malloc (addrlen);
786   memcpy (serviceListeningInfo->service_addr,
787           addr,
788           addrlen);
789   serviceListeningInfo->service_addr_len = addrlen;
790   serviceListeningInfo->listeningSocket = sock;
791   serviceListeningInfo->fc = fc;
792   serviceListeningInfo->acceptTask =
793     GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
794                                     serviceListeningInfo->listeningSocket,
795                                     &fc_acceptConnection, serviceListeningInfo);
796   return serviceListeningInfo;
797 }
798
799
800 /**
801  *
802  */
803 static void
804 start_forwarding (void *cls,
805                   const struct GNUNET_SCHEDULER_TaskContext *tc)
806 {
807   struct ForwardedConnection *fc = cls;
808   struct ServiceListeningInfo *sc;
809   struct sockaddr_in target_ipv4;
810   struct sockaddr_in6 target_ipv6;
811   const struct sockaddr_in *v4;
812   const struct sockaddr_in6 *v6;
813   char listen_address[INET6_ADDRSTRLEN];
814
815   fc->start_task = GNUNET_SCHEDULER_NO_TASK;
816   if ( (NULL != tc) &&
817        (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) )
818     {
819       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
820                   _("Unable to forward to service `%s': shutdown\n"),
821                   fc->listen_info->serviceName);
822       closeClientAndServiceSockets (fc, REASON_ERROR);
823       return;
824     }
825   if (0 == GNUNET_TIME_absolute_get_remaining (fc->timeout).rel_value)
826     {
827       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
828                   _("Unable to forward to service `%s': timeout before connect\n"),
829                   fc->listen_info->serviceName);
830       closeClientAndServiceSockets (fc, REASON_ERROR);
831       return;
832     }
833   switch (fc->listen_info->service_addr->sa_family)
834     {
835     case AF_UNSPEC:
836       GNUNET_break (0);
837       closeClientAndServiceSockets (fc, REASON_ERROR);
838       return;      
839     case AF_INET:
840       v4 = (const struct sockaddr_in *) fc->listen_info->service_addr;
841       inet_ntop (fc->listen_info->service_addr->sa_family, 
842                  (const void *) &v4->sin_addr, 
843                  listen_address,
844                  INET_ADDRSTRLEN);
845       if (0 == strncmp (listen_address, "0.0.0.0", 7))
846         {
847           /* connect to [::1] and 127.0.0.1 instead of [::] and 0.0.0.0 */
848           memset (&target_ipv4, 0, sizeof (target_ipv4));
849           GNUNET_assert (1 == 
850                          inet_pton (AF_INET, 
851                                     "127.0.0.1",
852                                     &target_ipv4.sin_addr));
853           target_ipv4.sin_family = AF_INET;
854           target_ipv4.sin_port = v4->sin_port;
855           v4 = &target_ipv4;
856         }
857       sc = service_try_to_connect ((const struct sockaddr*) v4,
858                                    PF_INET,
859                                    sizeof (struct sockaddr_in), 
860                                    fc);
861       break;
862     case AF_INET6:
863       v6 = (struct sockaddr_in6 *)fc->listen_info->service_addr;
864       inet_ntop (fc->listen_info->service_addr->sa_family, 
865                  (const void *) &v6->sin6_addr, 
866                  listen_address, 
867                  INET6_ADDRSTRLEN);
868       if ( (strncmp (listen_address, "[::]:", 5) == 0) || (strncmp (listen_address, "::", 2) == 0) )
869         {
870           memset (&target_ipv6, 0, sizeof (target_ipv6));
871           target_ipv6.sin6_addr = in6addr_loopback;
872           target_ipv6.sin6_family = AF_INET6;
873           target_ipv6.sin6_port = v6->sin6_port;
874           v6 = &target_ipv6;
875         }
876       sc = service_try_to_connect ((const struct sockaddr*) v6,
877                                    PF_INET6,
878                                    sizeof (struct sockaddr_in6), 
879                                    fc);
880       break;
881     case AF_UNIX:
882       sc = service_try_to_connect (fc->listen_info->service_addr,
883                                    PF_UNIX,
884                                    fc->listen_info->service_addr_len,
885                                    fc);
886       break;
887     default:
888       GNUNET_break (0);
889       closeClientAndServiceSockets (fc, REASON_ERROR);
890       return;
891     }  
892   if (NULL == sc)
893     {
894       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
895                   _ ("Unable to start service `%s': %s\n"),
896                   fc->listen_info->serviceName,
897                   STRERROR (errno));
898       closeClientAndServiceSockets (fc, REASON_ERROR);
899       return;
900     }
901 }
902
903
904 /**
905  *
906  */
907 int
908 stop_listening (const char *serviceName)
909 {
910   struct ServiceListeningInfo *pos;
911   struct ServiceListeningInfo *next;
912   int ret;
913   
914   ret = GNUNET_NO;
915   next = serviceListeningInfoList_head;
916   while (NULL != (pos = next))
917     {
918       next = pos->next;
919       if ( (serviceName != NULL) &&
920            (strcmp (pos->serviceName, serviceName) != 0) )
921         continue;
922       if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK)
923         GNUNET_SCHEDULER_cancel (pos->acceptTask);
924       GNUNET_break (GNUNET_OK ==
925                     GNUNET_NETWORK_socket_close (pos->listeningSocket));
926       GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
927                                    serviceListeningInfoList_tail, 
928                                    pos);
929       GNUNET_free (pos->serviceName);              
930       GNUNET_free (pos->service_addr);
931       GNUNET_free (pos); 
932       ret = GNUNET_OK;
933     }
934   return ret;
935 }
936
937 /**
938  * First connection has come to the listening socket associated with the service,
939  * create the service in order to relay the incoming connection to it
940  * 
941  * @param cls callback data, struct ServiceListeningInfo describing a listen socket
942  * @param tc context 
943  */
944 static void
945 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
946
947
948 static void
949 accept_and_forward (struct ServiceListeningInfo *serviceListeningInfo)
950 {
951   struct ForwardedConnection *fc;
952
953   fc = GNUNET_malloc (sizeof (struct ForwardedConnection));
954   fc->listen_info = serviceListeningInfo;
955   fc->service_to_client_bufferPos = fc->service_to_client_buffer;
956   fc->client_to_service_bufferPos = fc->client_to_service_buffer;
957   fc->client_addr_len = sizeof (fc->client_addr);
958   fc->armClientSocket = GNUNET_NETWORK_socket_accept (serviceListeningInfo->listeningSocket,
959                                                       (struct sockaddr*) fc->client_addr,
960                                                       &fc->client_addr_len);
961   if (NULL == fc->armClientSocket)
962     {
963       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
964                   _("Unable to accept connection for service `%s': %s\n"),
965                   serviceListeningInfo->serviceName,
966                   STRERROR (errno));
967       GNUNET_free (fc);
968       GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
969                                    serviceListeningInfoList_tail, 
970                                    serviceListeningInfo); 
971       serviceListeningInfo->acceptTask =
972         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 
973                                        serviceListeningInfo->listeningSocket,
974                                        &acceptConnection,
975                                        serviceListeningInfo);
976       return;
977     }
978   GNUNET_break (GNUNET_OK ==
979                 GNUNET_NETWORK_socket_close (serviceListeningInfo->listeningSocket));
980   start_service (NULL, serviceListeningInfo->serviceName, NULL);
981   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
982               _("Service `%s' started\n"),
983               fc->listen_info->serviceName);
984   fc->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_SERVICE_TIMEOUT);
985   fc->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
986   fc->client_to_service_task =
987     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
988                                    fc->armClientSocket,
989                                    &receiveFromClient, fc);
990   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
991   fc->start_task 
992     = GNUNET_SCHEDULER_add_now (&start_forwarding,
993                                 fc);
994 }
995
996
997 /**
998  * First connection has come to the listening socket associated with the service,
999  * create the service in order to relay the incoming connection to it
1000  * 
1001  * @param cls callback data, struct ServiceListeningInfo describing a listen socket
1002  * @param tc context 
1003  */
1004 static void
1005 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1006 {
1007   struct ServiceListeningInfo *sli = cls;
1008   struct ServiceListeningInfo *pos;
1009   struct ServiceListeningInfo *next;
1010   int *lsocks;
1011   unsigned int ls;
1012   int use_lsocks;
1013
1014   sli->acceptTask = GNUNET_SCHEDULER_NO_TASK;
1015   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
1016     return;
1017   GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
1018                                serviceListeningInfoList_tail, 
1019                                sli);  
1020 #ifndef MINGW
1021   use_lsocks = GNUNET_NO;
1022   if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (cfg,
1023                                                      sli->serviceName,
1024                                                      "DISABLE_SOCKET_FORWARDING"))
1025     use_lsocks = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1026                                                        sli->serviceName,
1027                                                        "DISABLE_SOCKET_FORWARDING");
1028 #else
1029   use_lsocks = GNUNET_YES;
1030 #endif
1031   if (GNUNET_NO != use_lsocks)
1032     {
1033       accept_and_forward (sli);
1034       return;
1035     }
1036   lsocks = NULL;
1037   ls = 0;
1038   next = serviceListeningInfoList_head;
1039   while (NULL != (pos = next))
1040     {
1041       next = pos->next;
1042       if (0 == strcmp (pos->serviceName,
1043                        sli->serviceName))
1044         {
1045           GNUNET_array_append (lsocks, ls, 
1046                                GNUNET_NETWORK_get_fd (pos->listeningSocket));     
1047           GNUNET_free (pos->listeningSocket); /* deliberately no closing! */
1048           GNUNET_free (pos->service_addr);
1049           GNUNET_free (pos->serviceName);
1050           GNUNET_SCHEDULER_cancel (pos->acceptTask);
1051           GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
1052                                        serviceListeningInfoList_tail, 
1053                                        pos);
1054           GNUNET_free (pos);
1055         }
1056     }
1057   GNUNET_array_append (lsocks, ls, 
1058                        GNUNET_NETWORK_get_fd (sli->listeningSocket));
1059   GNUNET_free (sli->listeningSocket); /* deliberately no closing! */
1060   GNUNET_free (sli->service_addr);
1061   GNUNET_array_append (lsocks, ls, -1);
1062   start_service (NULL, 
1063                  sli->serviceName,
1064                  lsocks);
1065   ls = 0;
1066   while (lsocks[ls] != -1)
1067     GNUNET_break (0 == close (lsocks[ls++]));      
1068   GNUNET_array_grow (lsocks, ls, 0);
1069   GNUNET_free (sli->serviceName);
1070   GNUNET_free (sli); 
1071 }
1072
1073
1074 /**
1075  * Creating a listening socket for each of the service's addresses and
1076  * wait for the first incoming connection to it
1077  * 
1078  * @param sa address associated with the service
1079  * @param addr_len length of sa
1080  * @param serviceName the name of the service in question
1081  */
1082 static void
1083 createListeningSocket(struct sockaddr *sa, socklen_t addr_len,
1084                       const char *serviceName)
1085 {
1086   const static int on = 1;
1087   struct GNUNET_NETWORK_Handle *sock;
1088   struct ServiceListeningInfo *serviceListeningInfo;
1089
1090   switch (sa->sa_family)
1091     {
1092     case AF_INET:
1093       sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
1094       break;
1095     case AF_INET6:
1096       sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
1097       break;
1098     case AF_UNIX:
1099       if (strcmp(GNUNET_a2s (sa, addr_len), "@") == 0) /* Do not bind to blank UNIX path! */
1100         return;
1101       sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
1102       break;
1103     default:
1104       GNUNET_break (0);
1105       sock = NULL;
1106       errno = EAFNOSUPPORT;
1107       break;
1108     }
1109   if (NULL == sock)
1110     {
1111       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1112                   _("Unable to create socket for service `%s': %s\n"),
1113                   serviceName,
1114                   STRERROR (errno));
1115       GNUNET_free (sa);
1116       return;
1117     }
1118   if (GNUNET_NETWORK_socket_setsockopt
1119       (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
1120     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1121                          "setsockopt");
1122 #ifdef IPV6_V6ONLY
1123   if ( (sa->sa_family == AF_INET6) &&
1124        (GNUNET_NETWORK_socket_setsockopt
1125         (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
1126     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1127                          "setsockopt");
1128 #endif
1129
1130   if (GNUNET_NETWORK_socket_bind
1131       (sock, (const struct sockaddr *) sa, addr_len) != GNUNET_OK)
1132     {
1133       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1134                   _("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
1135                   serviceName,
1136                   GNUNET_a2s (sa, addr_len),
1137                   STRERROR (errno));
1138       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1139       GNUNET_free (sa);
1140       return;
1141     }
1142   if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
1143     {
1144       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1145                            "listen");
1146       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1147       GNUNET_free (sa);
1148       return;
1149     }
1150   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1151               _("ARM now monitors connections to service `%s' at `%s'\n"),
1152               serviceName,
1153               GNUNET_a2s (sa, addr_len));
1154   serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
1155   serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
1156   serviceListeningInfo->service_addr = sa;
1157   serviceListeningInfo->service_addr_len = addr_len;
1158   serviceListeningInfo->listeningSocket = sock;
1159   serviceListeningInfo->acceptTask =
1160     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock,
1161                                    &acceptConnection,
1162                                    serviceListeningInfo);
1163   GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
1164                                serviceListeningInfoList_tail,
1165                                serviceListeningInfo);
1166 }
1167
1168
1169 /**
1170  * Callback function, checks whether the current tokens are representing a service,
1171  * gets its addresses and create listening socket for it.
1172  * 
1173  * @param cls callback data, not used
1174  * @param section configuration section
1175  * @param option configuration option
1176  * @param value the option's value
1177  */
1178 static void
1179 checkPortNumberCB (void *cls,
1180                    const char *section, 
1181                    const char *option, 
1182                    const char *value)
1183 {
1184   struct sockaddr **addrs;
1185   socklen_t *addr_lens;
1186   int ret;
1187   unsigned int i;
1188   
1189   if ( (strcasecmp (section, "arm") == 0) ||
1190        (strcasecmp (option, "AUTOSTART") != 0) ||
1191        (strcasecmp (value, "YES") != 0) ||
1192        (isInDefaultList (section) == GNUNET_YES) )
1193     return;
1194   if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs,
1195                                                        &addr_lens)))
1196     return;
1197   /* this will free (or capture) addrs[i] */
1198   for (i = 0; i < ret; i++)
1199     createListeningSocket (addrs[i], addr_lens[i], section);
1200   GNUNET_free (addrs);
1201   GNUNET_free (addr_lens);
1202 }
1203
1204
1205 /**
1206  * Entry point to the Service Manager
1207  *
1208  * @param configurationHandle configuration to use to get services
1209  */
1210 void
1211 prepareServices (const struct GNUNET_CONFIGURATION_Handle
1212                  *configurationHandle)
1213 {
1214   char *defaultServicesString;
1215
1216   cfg = configurationHandle;
1217   /* Split the default services into a list */
1218   if (GNUNET_OK ==
1219       GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
1220                                              &defaultServicesString))
1221     {
1222       addDefaultServicesToList (defaultServicesString);
1223       GNUNET_free (defaultServicesString);    
1224     }
1225   /* Spot the services from the configuration and create a listening
1226      socket for each */
1227   GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
1228 }
1229
1230 /* end of gnunet-service-arm_interceptor.c */