codesonar fixes
[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 (
665                                        GNUNET_TIME_UNIT_FOREVER_REL,
666                                        fc->armClientSocket,
667                                        &receiveFromClient, fc);
668       return;
669     }
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, 
674                                 BUFFER_SIZE);
675   if (fc->client_to_service_bufferDataLength <= 0)
676     {
677       if (fc->client_to_service_bufferDataLength == 0)
678         {
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);
683 #endif
684         }
685       else
686         {
687 #if DEBUG_SERVICE_MANAGER
688           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
689                       "Error receiving from client: %s\n",
690                       STRERROR (errno));
691 #endif
692         }
693       closeClientAndServiceSockets (fc, REASON_CLIENT_TO_SERVICE);
694       return;
695     }
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);
700 #endif
701   if (fc->armServiceSocket != NULL)        
702     fc->client_to_service_task = 
703       GNUNET_SCHEDULER_add_write_net (
704                                       GNUNET_TIME_UNIT_FOREVER_REL,
705                                       fc->armServiceSocket,
706                                       &forwardToService, fc);
707 }
708
709
710 static void
711 fc_acceptConnection (void *cls, 
712                      const struct GNUNET_SCHEDULER_TaskContext *tc)
713 {
714   struct ServiceListeningInfo *sli = cls;
715   struct ForwardedConnection *fc = sli->fc;
716
717   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))
718     {
719       GNUNET_NETWORK_socket_close (sli->listeningSocket);
720       closeClientAndServiceSockets (fc, REASON_ERROR);
721       GNUNET_free (sli);
722       return;
723     }
724 #if DEBUG_SERVICE_MANAGER
725   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
726               "Connected to service, now starting forwarding\n");
727 #endif
728   fc->armServiceSocket = sli->listeningSocket;
729   GNUNET_free (fc->listen_info->service_addr);
730   fc->listen_info->service_addr = sli->service_addr;
731   fc->listen_info->service_addr_len = sli->service_addr_len;
732   if (fc->client_to_service_task == GNUNET_SCHEDULER_NO_TASK)
733     {
734       if (fc->client_to_service_bufferDataLength == 0) 
735         fc->client_to_service_task =
736           GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
737                                          fc->armClientSocket,
738                                          &receiveFromClient, fc);
739       else
740         fc->client_to_service_task = 
741           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
742                                           fc->armServiceSocket,
743                                           &forwardToService, fc);
744     }
745   if (fc->service_to_client_task == GNUNET_SCHEDULER_NO_TASK)
746     {
747       if (fc->service_to_client_bufferDataLength == 0) 
748         fc->service_to_client_task =
749           GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
750                                          fc->armServiceSocket,
751                                          &receiveFromService, fc);
752       else
753         fc->service_to_client_task = 
754           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
755                                           fc->armClientSocket,
756                                           &forwardToClient, fc);
757     }
758   GNUNET_free (sli);
759 }
760
761
762 static struct ServiceListeningInfo *
763 service_try_to_connect (const struct sockaddr *addr, 
764                         int pf,
765                         socklen_t addrlen, 
766                         struct ForwardedConnection *fc)
767 {
768   struct GNUNET_NETWORK_Handle *sock;
769   struct ServiceListeningInfo *serviceListeningInfo;
770
771   sock = GNUNET_NETWORK_socket_create (pf, SOCK_STREAM, 0);
772   if (sock == NULL)
773     {
774       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket");
775       return NULL;
776     }  
777   if ( (GNUNET_SYSERR == GNUNET_NETWORK_socket_connect (sock, addr, addrlen)) &&
778        (errno != EINPROGRESS) )
779     {
780       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
781       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
782       return NULL;
783     }  
784   serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
785   serviceListeningInfo->serviceName = NULL;
786   serviceListeningInfo->service_addr = GNUNET_malloc (addrlen);
787   memcpy (serviceListeningInfo->service_addr,
788           addr,
789           addrlen);
790   serviceListeningInfo->service_addr_len = addrlen;
791   serviceListeningInfo->listeningSocket = sock;
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           inet_pton (AF_INET, "127.0.0.1", &target_ipv4.sin_addr);
850           target_ipv4.sin_family = AF_INET;
851           target_ipv4.sin_port = v4->sin_port;
852           v4 = &target_ipv4;
853         }
854       sc = service_try_to_connect ((const struct sockaddr*) v4,
855                                    PF_INET,
856                                    sizeof (struct sockaddr_in), 
857                                    fc);
858       break;
859     case AF_INET6:
860       v6 = (struct sockaddr_in6 *)fc->listen_info->service_addr;
861       inet_ntop (fc->listen_info->service_addr->sa_family, 
862                  (const void *) &v6->sin6_addr, 
863                  listen_address, 
864                  INET6_ADDRSTRLEN);
865       if ( (strncmp (listen_address, "[::]:", 5) == 0) || (strncmp (listen_address, "::", 2) == 0) )
866         {
867           memset (&target_ipv6, 0, sizeof (target_ipv6));
868           inet_pton (AF_INET6, "::1", &target_ipv6.sin6_addr);
869           target_ipv6.sin6_family = AF_INET6;
870           target_ipv6.sin6_port = v6->sin6_port;
871           v6 = &target_ipv6;
872         }
873       sc = service_try_to_connect ((const struct sockaddr*) v6,
874                                    PF_INET6,
875                                    sizeof (struct sockaddr_in6), 
876                                    fc);
877       break;
878     case AF_UNIX:
879       sc = service_try_to_connect (fc->listen_info->service_addr,
880                                    PF_UNIX,
881                                    fc->listen_info->service_addr_len,
882                                    fc);
883       break;
884     default:
885       GNUNET_break (0);
886       closeClientAndServiceSockets (fc, REASON_ERROR);
887       return;
888     }  
889   if (NULL == sc)
890     {
891       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
892                   _ ("Unable to start service `%s': %s\n"),
893                   fc->listen_info->serviceName,
894                   STRERROR (errno));
895       closeClientAndServiceSockets (fc, REASON_ERROR);
896       return;
897     }
898 }
899
900
901 /**
902  *
903  */
904 int
905 stop_listening (const char *serviceName)
906 {
907   struct ServiceListeningInfo *pos;
908   struct ServiceListeningInfo *next;
909   int ret;
910   
911   ret = GNUNET_NO;
912   next = serviceListeningInfoList_head;
913   while (NULL != (pos = next))
914     {
915       next = pos->next;
916       if ( (serviceName != NULL) &&
917            (strcmp (pos->serviceName, serviceName) != 0) )
918         continue;
919       if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK)
920         GNUNET_SCHEDULER_cancel (pos->acceptTask);
921       GNUNET_break (GNUNET_OK ==
922                     GNUNET_NETWORK_socket_close (pos->listeningSocket));
923       GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
924                                    serviceListeningInfoList_tail, 
925                                    pos);
926       GNUNET_free (pos->serviceName);              
927       GNUNET_free (pos->service_addr);
928       GNUNET_free (pos); 
929       ret = GNUNET_OK;
930     }
931   return ret;
932 }
933
934 /**
935  * First connection has come to the listening socket associated with the service,
936  * create the service in order to relay the incoming connection to it
937  * 
938  * @param cls callback data, struct ServiceListeningInfo describing a listen socket
939  * @param tc context 
940  */
941 static void
942 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
943
944
945 static void
946 accept_and_forward (struct ServiceListeningInfo *serviceListeningInfo)
947 {
948   struct ForwardedConnection *fc;
949
950   fc = GNUNET_malloc (sizeof (struct ForwardedConnection));
951   fc->listen_info = serviceListeningInfo;
952   fc->service_to_client_bufferPos = fc->service_to_client_buffer;
953   fc->client_to_service_bufferPos = fc->client_to_service_buffer;
954   fc->client_addr_len = sizeof (fc->client_addr);
955   fc->armClientSocket = GNUNET_NETWORK_socket_accept (serviceListeningInfo->listeningSocket,
956                                                       (struct sockaddr*) fc->client_addr,
957                                                       &fc->client_addr_len);
958   if (NULL == fc->armClientSocket)
959     {
960       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
961                   _("Unable to accept connection for service `%s': %s\n"),
962                   serviceListeningInfo->serviceName,
963                   STRERROR (errno));
964       GNUNET_free (fc);
965       GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
966                                    serviceListeningInfoList_tail, 
967                                    serviceListeningInfo); 
968       serviceListeningInfo->acceptTask =
969         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 
970                                        serviceListeningInfo->listeningSocket,
971                                        &acceptConnection,
972                                        serviceListeningInfo);
973       return;
974     }
975   GNUNET_break (GNUNET_OK ==
976                 GNUNET_NETWORK_socket_close (serviceListeningInfo->listeningSocket));
977   start_service (NULL, serviceListeningInfo->serviceName, NULL);
978   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
979               _("Service `%s' started\n"),
980               fc->listen_info->serviceName);
981   fc->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_SERVICE_TIMEOUT);
982   fc->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
983   fc->client_to_service_task =
984     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
985                                    fc->armClientSocket,
986                                    &receiveFromClient, fc);
987   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
988   fc->start_task 
989     = GNUNET_SCHEDULER_add_now (&start_forwarding,
990                                 fc);
991 }
992
993
994 /**
995  * First connection has come to the listening socket associated with the service,
996  * create the service in order to relay the incoming connection to it
997  * 
998  * @param cls callback data, struct ServiceListeningInfo describing a listen socket
999  * @param tc context 
1000  */
1001 static void
1002 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1003 {
1004   struct ServiceListeningInfo *sli = cls;
1005   struct ServiceListeningInfo *pos;
1006   struct ServiceListeningInfo *next;
1007   int *lsocks;
1008   unsigned int ls;
1009   int use_lsocks;
1010
1011   sli->acceptTask = GNUNET_SCHEDULER_NO_TASK;
1012   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
1013     return;
1014   GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
1015                                serviceListeningInfoList_tail, 
1016                                sli);  
1017 #ifndef MINGW
1018   use_lsocks = GNUNET_NO;
1019   if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (cfg,
1020                                                      sli->serviceName,
1021                                                      "DISABLE_SOCKET_FORWARDING"))
1022     use_lsocks = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1023                                                        sli->serviceName,
1024                                                        "DISABLE_SOCKET_FORWARDING");
1025 #else
1026   use_lsocks = GNUNET_YES;
1027 #endif
1028   if (GNUNET_NO != use_lsocks)
1029     {
1030       accept_and_forward (sli);
1031       return;
1032     }
1033   lsocks = NULL;
1034   ls = 0;
1035   next = serviceListeningInfoList_head;
1036   while (NULL != (pos = next))
1037     {
1038       next = pos->next;
1039       if (0 == strcmp (pos->serviceName,
1040                        sli->serviceName))
1041         {
1042           GNUNET_array_append (lsocks, ls, 
1043                                GNUNET_NETWORK_get_fd (pos->listeningSocket));     
1044           GNUNET_free (pos->listeningSocket); /* deliberately no closing! */
1045           GNUNET_free (pos->service_addr);
1046           GNUNET_free (pos->serviceName);
1047           GNUNET_SCHEDULER_cancel (                                pos->acceptTask);
1048           GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
1049                                        serviceListeningInfoList_tail, 
1050                                        pos);
1051           GNUNET_free (pos);
1052         }
1053     }
1054   GNUNET_array_append (lsocks, ls, 
1055                        GNUNET_NETWORK_get_fd (sli->listeningSocket));
1056   GNUNET_free (sli->listeningSocket); /* deliberately no closing! */
1057   GNUNET_free (sli->service_addr);
1058   GNUNET_array_append (lsocks, ls, -1);
1059   start_service (NULL, 
1060                  sli->serviceName,
1061                  lsocks);
1062   ls = 0;
1063   while (lsocks[ls] != -1)
1064     GNUNET_break (0 == close (lsocks[ls++]));      
1065   GNUNET_array_grow (lsocks, ls, 0);
1066   GNUNET_free (sli->serviceName);
1067   GNUNET_free (sli); 
1068 }
1069
1070
1071 /**
1072  * Creating a listening socket for each of the service's addresses and
1073  * wait for the first incoming connection to it
1074  * 
1075  * @param sa address associated with the service
1076  * @param addr_len length of sa
1077  * @param serviceName the name of the service in question
1078  */
1079 static void
1080 createListeningSocket (struct sockaddr *sa, 
1081                        socklen_t addr_len,
1082                        const char *serviceName)
1083 {
1084   const static int on = 1;
1085   struct GNUNET_NETWORK_Handle *sock;
1086   struct ServiceListeningInfo *serviceListeningInfo;
1087
1088   switch (sa->sa_family)
1089     {
1090     case AF_INET:
1091       sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
1092       break;
1093     case AF_INET6:
1094       sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
1095       break;
1096     case AF_UNIX:
1097       sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
1098       break;
1099     default:
1100       GNUNET_break (0);
1101       sock = NULL;
1102       errno = EAFNOSUPPORT;
1103       break;
1104     }
1105   if (NULL == sock)
1106     {
1107       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1108                   _("Unable to create socket for service `%s': %s\n"),
1109                   serviceName,
1110                   STRERROR (errno));
1111       GNUNET_free (sa);
1112       return;
1113     }
1114   if (GNUNET_NETWORK_socket_setsockopt
1115       (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
1116     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1117                          "setsockopt");
1118 #ifdef IPV6_V6ONLY
1119   if ( (sa->sa_family == AF_INET6) &&
1120        (GNUNET_NETWORK_socket_setsockopt
1121         (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
1122     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1123                          "setsockopt");
1124 #endif
1125
1126   if (GNUNET_NETWORK_socket_bind
1127       (sock, (const struct sockaddr *) sa, addr_len) != GNUNET_OK)
1128     {
1129       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1130                   _("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
1131                   serviceName,
1132                   GNUNET_a2s (sa, addr_len),
1133                   STRERROR (errno));
1134       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1135       GNUNET_free (sa);
1136       return;
1137     }
1138   if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
1139     {
1140       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1141                            "listen");
1142       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1143       GNUNET_free (sa);
1144       return;
1145     }
1146   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1147               _("ARM now monitors connections to service `%s' at `%s'\n"),
1148               serviceName,
1149               GNUNET_a2s (sa, addr_len));
1150   serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
1151   serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
1152   serviceListeningInfo->service_addr = sa;
1153   serviceListeningInfo->service_addr_len = addr_len;
1154   serviceListeningInfo->listeningSocket = sock;
1155   serviceListeningInfo->acceptTask =
1156     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock,
1157                                    &acceptConnection,
1158                                    serviceListeningInfo);
1159   GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
1160                                serviceListeningInfoList_tail,
1161                                serviceListeningInfo);
1162 }
1163
1164
1165 /**
1166  * Callback function, checks whether the current tokens are representing a service,
1167  * gets its addresses and create listening socket for it.
1168  * 
1169  * @param cls callback data, not used
1170  * @param section configuration section
1171  * @param option configuration option
1172  * @param value the option's value
1173  */
1174 static void
1175 checkPortNumberCB (void *cls,
1176                    const char *section, 
1177                    const char *option, 
1178                    const char *value)
1179 {
1180   struct sockaddr **addrs;
1181   socklen_t *addr_lens;
1182   int ret;
1183   unsigned int i;
1184   
1185   if ( (strcasecmp (section, "arm") == 0) ||
1186        (strcasecmp (option, "AUTOSTART") != 0) ||
1187        (strcasecmp (value, "YES") != 0) ||
1188        (isInDefaultList (section) == GNUNET_YES) )
1189     return;
1190   if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs,
1191                                                        &addr_lens)))
1192     return;
1193   /* this will free (or capture) addrs[i] */
1194   for (i = 0; i < ret; i++)
1195     createListeningSocket (addrs[i], addr_lens[i], section);
1196   GNUNET_free (addrs);
1197   GNUNET_free (addr_lens);
1198 }
1199
1200
1201 /**
1202  * Entry point to the Service Manager
1203  *
1204  * @param configurationHandle configuration to use to get services
1205  */
1206 void
1207 prepareServices (const struct GNUNET_CONFIGURATION_Handle
1208                  *configurationHandle)
1209 {
1210   char *defaultServicesString;
1211
1212   cfg = configurationHandle;
1213   /* Split the default services into a list */
1214   if (GNUNET_OK ==
1215       GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
1216                                              &defaultServicesString))
1217     {
1218       addDefaultServicesToList (defaultServicesString);
1219       GNUNET_free (defaultServicesString);    
1220     }
1221   /* Spot the services from the configuration and create a listening
1222      socket for each */
1223   GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
1224 }
1225
1226 /* end of gnunet-service-arm_interceptor.c */