renaming files to fit conventions
[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 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-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
60 /**
61  *
62  */
63 struct ServiceListeningInfo
64 {
65   /**
66    * This is a linked list.
67    */
68   struct ServiceListeningInfo *next;
69
70   /**
71    * This is a linked list.
72    */
73   struct ServiceListeningInfo *prev;
74
75   /**
76    * Name of the service being forwarded.
77    */
78   char *serviceName;
79
80   /**
81    *
82    */
83   struct sockaddr *service_addr;
84
85   /**
86    *
87    */
88   socklen_t service_addr_len;
89
90   /**
91    * Our listening socket.
92    */
93   struct GNUNET_NETWORK_Handle *listeningSocket;
94
95   /**
96    * Task doing the accepting.
97    */
98   GNUNET_SCHEDULER_TaskIdentifier acceptTask;
99 };
100
101 /**
102  * Information of the connection: client-arm-service
103  */
104 struct ForwardedConnection
105 {
106   /**
107    *
108    */
109   struct GNUNET_NETWORK_Handle *armClientSocket;
110
111   /**
112    *
113    */
114   struct GNUNET_NETWORK_Handle *armServiceSocket;
115
116   /**
117    *
118    */
119   struct ServiceListeningInfo *listen_info;
120
121   /**
122    *
123    */
124   char service_to_client_buffer[BUFFER_SIZE];
125
126   /**
127    *
128    */
129   char client_to_service_buffer[BUFFER_SIZE];
130
131   /**
132    *
133    */
134   char client_addr[32];
135
136   /**
137    *
138    */
139   const char *client_to_service_bufferPos;
140
141   /**
142    *
143    */
144   const char *service_to_client_bufferPos;
145
146   /**
147    * Timeout for forwarding.
148    */
149   struct GNUNET_TIME_Absolute timeout;
150   
151   /**
152    * Current back-off value.
153    */
154   struct GNUNET_TIME_Relative back_off;
155   
156   /**
157    * Task that tries to initiate forwarding.
158    */
159   GNUNET_SCHEDULER_TaskIdentifier start_task;
160
161   /**
162    *
163    */
164   GNUNET_SCHEDULER_TaskIdentifier client_to_service_task;
165
166   /**
167    *
168    */
169   GNUNET_SCHEDULER_TaskIdentifier service_to_client_task;
170
171   /**
172    *
173    */
174   ssize_t client_to_service_bufferDataLength;
175
176   /**
177    *
178    */
179   ssize_t service_to_client_bufferDataLength;
180
181   /**
182    *
183    */
184   socklen_t client_addr_len;
185
186   /**
187    * Have we ever successfully written data to the service?
188    */
189   int first_write_done;
190 };
191
192
193 /**
194  * Array with the names of the services started by default.
195  */
196 static char **defaultServicesList;
197
198 /**
199  * Size of the defaultServicesList array.
200  */
201 static unsigned int numDefaultServices;
202
203 /**
204  *
205  */
206 static const struct GNUNET_CONFIGURATION_Handle *cfg;
207
208 /**
209  *
210  */
211 static struct GNUNET_SCHEDULER_Handle *scheduler;
212
213 /**
214  *
215  */
216 static struct ServiceListeningInfo *serviceListeningInfoList_head;
217
218 /**
219  *
220  */
221 static struct ServiceListeningInfo *serviceListeningInfoList_tail;
222
223
224 /**
225  * Put the default services represented by a space separated string into an array of strings
226  * 
227  * @param services space separated string of default services
228  */
229 static void
230 addDefaultServicesToList (const char *services)
231 {
232   unsigned int i;
233   const char *token;
234   char *s;
235
236   if (strlen (services) == 0)
237     return;
238   s = GNUNET_strdup (services);
239   token = strtok (s, " ");
240   while (NULL != token)
241     {
242       numDefaultServices++;
243       token = strtok (NULL, " ");
244     }
245   GNUNET_free (s);
246
247   defaultServicesList = GNUNET_malloc (numDefaultServices * sizeof (char *));
248   i = 0;
249   s = GNUNET_strdup (services);
250   token = strtok (s, " ");
251   while (NULL != token)
252     {
253       defaultServicesList[i++] = GNUNET_strdup (token);
254       token = strtok (NULL, " ");
255     }
256   GNUNET_free (s);
257   GNUNET_assert (i == numDefaultServices);
258 }
259
260 /**
261  * Checks whether the serviceName is in the list of default services
262  * 
263  * @param serviceName string to check its existance in the list
264  * @return GNUNET_YES if the service is started by default
265  */
266 static int
267 isInDefaultList (const char *serviceName)
268 {
269   unsigned int i;
270   for (i = 0; i < numDefaultServices; i++)
271     if (strcmp (serviceName, defaultServicesList[i]) == 0)
272       return GNUNET_YES;    
273   return GNUNET_NO;
274 }
275
276
277 /**
278  * Close forwarded connection (partial or full).
279  *
280  * @param fc connection to close 
281  * @param reason which direction to close
282  */
283 static void
284 closeClientAndServiceSockets (struct ForwardedConnection *fc, 
285                               int reason)
286 {
287   if (0 != (REASON_SERVICE_TO_CLIENT & reason)) 
288     {      
289 #if DEBUG_SERVICE_MANAGER
290       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
291                   "Stopping forwarding from service to client\n",
292                   fc->listen_info->serviceName);
293 #endif
294       if (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK)
295         {
296           GNUNET_SCHEDULER_cancel (scheduler, fc->service_to_client_task);    
297           fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
298         }
299       if (fc->armClientSocket != NULL)
300         GNUNET_NETWORK_socket_shutdown (fc->armClientSocket,
301                                         SHUT_WR);
302       if (fc->armServiceSocket != NULL)
303         GNUNET_NETWORK_socket_shutdown (fc->armServiceSocket,
304                                         SHUT_RD);
305     }
306   if (0 != (REASON_CLIENT_TO_SERVICE & reason)) 
307     {
308 #if DEBUG_SERVICE_MANAGER
309       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
310                   "Stopping forwarding from client to service\n",
311                   fc->listen_info->serviceName);
312 #endif
313       if (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK) 
314         {
315           GNUNET_SCHEDULER_cancel (scheduler,
316                                    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 (scheduler,
335                              fc->start_task);
336   if ( (NULL != fc->armClientSocket) &&
337        (GNUNET_SYSERR ==
338         GNUNET_NETWORK_socket_close (fc->armClientSocket)) )
339     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
340   if ( (NULL != fc->armServiceSocket) &&
341        (GNUNET_SYSERR ==
342         GNUNET_NETWORK_socket_close (fc->armServiceSocket)) )
343     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
344   GNUNET_free (fc->listen_info->serviceName);              
345   GNUNET_free (fc->listen_info->service_addr);
346   GNUNET_free (fc->listen_info);        
347   GNUNET_free (fc);
348 }
349
350
351 /**
352  *
353  */
354 static void
355 receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
356
357
358 /**
359  *
360  */
361 static void
362 receiveFromService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
363
364
365 /**
366  *
367  */
368 static void
369 start_forwarding (void *cls,
370                   const struct GNUNET_SCHEDULER_TaskContext *tc);
371
372
373
374 /**
375  * Forward messages sent from service to client
376  * 
377  * @param cls callback data, struct ForwardedConnection for the communication between client and service
378  * @param tc context
379  */
380 static void
381 forwardToClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
382 {
383   struct ForwardedConnection *fc = cls;
384   ssize_t numberOfBytesSent;
385
386   fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
387   if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->write_ready,
388                                                 fc->armClientSocket))
389     {
390       fc->service_to_client_task = 
391         GNUNET_SCHEDULER_add_write_net (scheduler,
392                                         GNUNET_TIME_UNIT_FOREVER_REL,
393                                         fc->armClientSocket,
394                                         &forwardToClient, fc);
395       return;
396     }
397   /* Forwarding service response to client */
398   numberOfBytesSent =
399     GNUNET_NETWORK_socket_send (fc->armClientSocket,
400                                 fc->service_to_client_bufferPos,
401                                 fc->service_to_client_bufferDataLength);
402   if (numberOfBytesSent <= 0)
403     {
404       if ( (errno != EPIPE) &&
405            (errno != ECONNRESET) )
406         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
407                     "Failed to forward %u bytes of data to client: %s\n",
408                     fc->service_to_client_bufferDataLength,
409                     STRERROR (errno));
410       closeClientAndServiceSockets (fc,
411                                     REASON_SERVICE_TO_CLIENT);
412       return;
413     }
414 #if DEBUG_SERVICE_MANAGER
415   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
416               "Forwarded %d bytes to client\n",
417               numberOfBytesSent);
418 #endif
419   if (numberOfBytesSent < fc->service_to_client_bufferDataLength)
420     {
421       fc->service_to_client_bufferPos += numberOfBytesSent;
422       fc->service_to_client_bufferDataLength -= numberOfBytesSent;
423       fc->service_to_client_task = 
424         GNUNET_SCHEDULER_add_write_net (scheduler, 
425                                         GNUNET_TIME_UNIT_FOREVER_REL,
426                                         fc->armClientSocket,
427                                         &forwardToClient, 
428                                         fc);
429       return;
430     }
431   fc->service_to_client_task =
432     GNUNET_SCHEDULER_add_read_net (scheduler, 
433                                    GNUNET_TIME_UNIT_FOREVER_REL,
434                                    fc->armServiceSocket,
435                                    &receiveFromService, 
436                                    fc);
437 }
438
439
440 /**
441  * Receive service messages sent by the service and forward it to client
442  * 
443  * @param cls callback data, struct ForwardedConnection for the communication between client and service
444  * @param tc scheduler context
445  */
446 static void
447 receiveFromService (void *cls, 
448                     const struct GNUNET_SCHEDULER_TaskContext *tc)
449 {
450   struct ForwardedConnection *fc = cls;
451   struct GNUNET_TIME_Relative rem;
452
453   fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
454   if ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
455        (fc->first_write_done != GNUNET_YES) )
456     {
457       closeClientAndServiceSockets (fc, REASON_ERROR);
458       return;
459     }
460   if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->read_ready,
461                                                 fc->armServiceSocket))
462     {
463       fc->service_to_client_task =
464         GNUNET_SCHEDULER_add_read_net (scheduler,
465                                        GNUNET_TIME_UNIT_FOREVER_REL,
466                                        fc->armServiceSocket,
467                                        &receiveFromService, fc);
468       return;
469     }
470   fc->service_to_client_bufferPos = fc->service_to_client_buffer;
471   fc->service_to_client_bufferDataLength =
472     GNUNET_NETWORK_socket_recv (fc->armServiceSocket,
473                                 fc->service_to_client_buffer, 
474                                 BUFFER_SIZE);
475   if (fc->service_to_client_bufferDataLength <= 0)
476     {
477       if (fc->service_to_client_bufferDataLength == 0)
478         {
479 #if DEBUG_SERVICE_MANAGER
480           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
481                       "Service `%s' stopped sending data.\n",
482                       fc->listen_info->serviceName);
483 #endif
484         }
485       if (fc->first_write_done != GNUNET_YES)
486         {
487           fc->service_to_client_bufferDataLength = 0;
488           GNUNET_break (GNUNET_OK ==
489                         GNUNET_NETWORK_socket_close (fc->armServiceSocket));
490           fc->armServiceSocket = NULL;
491           if ( (fc->client_to_service_bufferDataLength > 0) &&
492                (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK) )
493             {
494               GNUNET_SCHEDULER_cancel (scheduler,
495                                        fc->client_to_service_task);
496               fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
497             }
498           fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
499 #if DEBUG_SERVICE_MANAGER
500           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
501                       "Failed to connected to service `%s' at `%s', will try again in %llu ms\n",
502                       fc->listen_info->serviceName,
503                       GNUNET_a2s (fc->listen_info->service_addr,
504                                   fc->listen_info->service_addr_len),
505                       (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
506                                                                      rem).value);
507 #endif
508           rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
509           GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
510           fc->start_task
511             = GNUNET_SCHEDULER_add_delayed (scheduler,
512                                             GNUNET_TIME_relative_min (fc->back_off,
513                                                                       rem),
514                                             &start_forwarding,
515                                             fc);
516         }
517       else
518         {
519 #if DEBUG_SERVICE_MANAGER
520           if (fc->service_to_client_bufferDataLength != 0)
521             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
522                         "Error receiving from service: %s\n", 
523                         STRERROR (errno));
524 #endif
525           closeClientAndServiceSockets (fc, REASON_SERVICE_TO_CLIENT);
526         }
527       return;
528     }
529   fc->first_write_done = GNUNET_YES;
530 #if DEBUG_SERVICE_MANAGER
531   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
532               "Received %d bytes for client\n",
533               fc->service_to_client_bufferDataLength);
534 #endif
535   fc->service_to_client_task = 
536     GNUNET_SCHEDULER_add_write_net (scheduler, 
537                                     GNUNET_TIME_UNIT_FOREVER_REL,
538                                     fc->armClientSocket,
539                                     &forwardToClient, fc);
540 }
541
542
543 /**
544  * Forward client message to service
545  * 
546  * @param cls callback data, struct ForwardedConnection for the communication between client and service
547  * @param tc scheduler context
548  */
549 static void
550 forwardToService (void *cls, 
551                   const struct GNUNET_SCHEDULER_TaskContext *tc)
552 {
553   struct ForwardedConnection *fc = cls;
554   ssize_t numberOfBytesSent;
555   struct GNUNET_TIME_Relative rem;
556
557   fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
558   if ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
559        (fc->first_write_done != GNUNET_YES) )
560     {
561       closeClientAndServiceSockets (fc, REASON_ERROR);
562       return;
563     }
564   if (GNUNET_YES != GNUNET_NETWORK_fdset_isset (tc->write_ready,
565                                                 fc->armServiceSocket))
566     {
567       fc->client_to_service_task = 
568         GNUNET_SCHEDULER_add_write_net (scheduler,
569                                         GNUNET_TIME_UNIT_FOREVER_REL,
570                                         fc->armServiceSocket,
571                                         &forwardToService, fc);
572       return;
573     }
574   numberOfBytesSent =
575     GNUNET_NETWORK_socket_send (fc->armServiceSocket,
576                                 fc->client_to_service_bufferPos,
577                                 fc->client_to_service_bufferDataLength);
578   if (numberOfBytesSent <= 0)
579     {
580       if (GNUNET_YES != fc->first_write_done)
581         {
582           GNUNET_break (GNUNET_OK ==
583                         GNUNET_NETWORK_socket_close (fc->armServiceSocket));
584           fc->armServiceSocket = NULL;
585           if ( (fc->service_to_client_bufferDataLength == 0) &&
586                (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK) )
587             {
588               GNUNET_SCHEDULER_cancel (scheduler,
589                                        fc->service_to_client_task);
590               fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
591             }
592           fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
593 #if DEBUG_SERVICE_MANAGER
594           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
595                       "Failed to connect to service `%s' at `%s', will try again in %llu ms\n",
596                       fc->listen_info->serviceName,
597                       GNUNET_a2s (fc->listen_info->service_addr,
598                                   fc->listen_info->service_addr_len),
599                       (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
600                                                                      rem).value);
601 #endif
602           rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
603           GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
604           fc->start_task 
605             = GNUNET_SCHEDULER_add_delayed (scheduler,
606                                             GNUNET_TIME_relative_min (fc->back_off,
607                                                                       rem),
608                                             &start_forwarding,
609                                             fc);
610         }
611       else
612         {
613           if ( (errno != EPIPE) &&
614                (errno != ECONNRESET) )
615             GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
616                         "Failed to forward data to service: %s\n",
617                         STRERROR (errno));
618           closeClientAndServiceSockets (fc,
619                                         REASON_CLIENT_TO_SERVICE);
620         }
621       return;
622     }
623 #if DEBUG_SERVICE_MANAGER
624   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
625               "Forwarded %d bytes to service\n",
626               numberOfBytesSent);
627 #endif
628   fc->first_write_done = GNUNET_YES;
629   if (numberOfBytesSent < fc->client_to_service_bufferDataLength)
630     {
631       fc->client_to_service_bufferPos += numberOfBytesSent;
632       fc->client_to_service_bufferDataLength -= numberOfBytesSent;
633       fc->client_to_service_task = 
634         GNUNET_SCHEDULER_add_write_net (scheduler, 
635                                         GNUNET_TIME_UNIT_FOREVER_REL,
636                                         fc->armServiceSocket,
637                                         &forwardToService, fc);
638       return;
639     }
640   fc->client_to_service_task =
641     GNUNET_SCHEDULER_add_read_net (scheduler, 
642                                    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 (scheduler,
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 (scheduler,
704                                       GNUNET_TIME_UNIT_FOREVER_REL,
705                                       fc->armServiceSocket,
706                                       &forwardToService, fc);
707 }
708
709
710 /**
711  *
712  */
713 static void
714 start_forwarding (void *cls,
715                   const struct GNUNET_SCHEDULER_TaskContext *tc)
716 {
717   struct ForwardedConnection *fc = cls;
718   struct GNUNET_TIME_Relative rem;
719
720   fc->start_task = GNUNET_SCHEDULER_NO_TASK;
721   if ( (NULL != tc) &&
722        (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) )
723     {
724       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
725                   _("Unable to forward to service `%s': shutdown\n"),
726                   fc->listen_info->serviceName);
727       closeClientAndServiceSockets (fc, REASON_ERROR);
728       return;
729     }
730   rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
731   if (rem.value == 0)
732     {
733       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
734                   _("Unable to forward to service `%s': timeout before connect\n"),
735                   fc->listen_info->serviceName);
736       closeClientAndServiceSockets (fc, REASON_ERROR);
737       return;
738     }
739   fc->armServiceSocket =
740     GNUNET_NETWORK_socket_create (fc->listen_info->service_addr->sa_family,
741                                   SOCK_STREAM, 0);
742   if (NULL == fc->armServiceSocket)
743     {
744       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
745                   _ ("Unable to start service `%s': %s\n"),
746                   fc->listen_info->serviceName,
747                   STRERROR (errno));
748       closeClientAndServiceSockets (fc, REASON_ERROR);
749       return;
750     }
751   if ( (GNUNET_SYSERR ==
752         GNUNET_NETWORK_socket_connect (fc->armServiceSocket,
753                                        fc->listen_info->service_addr,
754                                        fc->listen_info->service_addr_len)) &&
755        (errno != EINPROGRESS) )
756     {
757       GNUNET_break (GNUNET_OK ==
758                     GNUNET_NETWORK_socket_close (fc->armServiceSocket));
759       fc->armServiceSocket = NULL;
760       fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
761   #if DEBUG_SERVICE_MANAGER
762       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
763                   "Failed to connected to service `%s' at `%s', will try again in %llu ms\n",
764                   fc->listen_info->serviceName,
765                   GNUNET_a2s (fc->listen_info->service_addr,
766                               fc->listen_info->service_addr_len),
767                   (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
768                                                                  rem).value);
769 #endif
770       GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
771       fc->start_task
772         = GNUNET_SCHEDULER_add_delayed (scheduler,
773                                         GNUNET_TIME_relative_min (fc->back_off,
774                                                                   rem),
775                                         &start_forwarding,
776                                         fc);
777       return;
778     }
779 #if DEBUG_SERVICE_MANAGER
780   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
781               "Connected to service, now starting forwarding\n");
782 #endif
783   if (fc->client_to_service_task == GNUNET_SCHEDULER_NO_TASK)
784     {
785       if (fc->client_to_service_bufferDataLength == 0) 
786         fc->client_to_service_task =
787           GNUNET_SCHEDULER_add_read_net (scheduler,
788                                          GNUNET_TIME_UNIT_FOREVER_REL,
789                                          fc->armClientSocket,
790                                          &receiveFromClient, fc);
791       else
792         fc->client_to_service_task = 
793           GNUNET_SCHEDULER_add_write_net (scheduler, 
794                                           GNUNET_TIME_UNIT_FOREVER_REL,
795                                           fc->armServiceSocket,
796                                           &forwardToService, fc);
797     }
798   if (fc->service_to_client_task == GNUNET_SCHEDULER_NO_TASK)
799     {
800       if (fc->service_to_client_bufferDataLength == 0) 
801         fc->service_to_client_task =
802           GNUNET_SCHEDULER_add_read_net (scheduler,
803                                          GNUNET_TIME_UNIT_FOREVER_REL,
804                                          fc->armServiceSocket,
805                                          &receiveFromService, fc);
806       else
807         fc->service_to_client_task = 
808           GNUNET_SCHEDULER_add_write_net (scheduler, 
809                                           GNUNET_TIME_UNIT_FOREVER_REL,
810                                           fc->armClientSocket,
811                                           &forwardToClient, fc);
812     }
813 }
814
815
816
817 /**
818  *
819  */
820 int
821 stop_listening (const char *serviceName)
822 {
823   struct ServiceListeningInfo *pos;
824   struct ServiceListeningInfo *next;
825   int ret;
826   
827   ret = GNUNET_NO;
828   next = serviceListeningInfoList_head;
829   while (NULL != (pos = next))
830     {
831       next = pos->next;
832       if ( (serviceName != NULL) &&
833            (strcmp (pos->serviceName, serviceName) != 0) )
834         continue;
835       if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK)
836         GNUNET_SCHEDULER_cancel (scheduler, pos->acceptTask);
837       GNUNET_break (GNUNET_OK ==
838                     GNUNET_NETWORK_socket_close (pos->listeningSocket));
839       GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
840                                    serviceListeningInfoList_tail, 
841                                    pos);
842       GNUNET_free (pos->serviceName);              
843       GNUNET_free (pos->service_addr);
844       GNUNET_free (pos); 
845       ret = GNUNET_OK;
846     }
847   return ret;
848 }
849
850
851 /**
852  * First connection has come to the listening socket associated with the service,
853  * create the service in order to relay the incoming connection to it
854  * 
855  * @param cls callback data, struct ServiceListeningInfo describing a listen socket
856  * @param tc context 
857  */
858 static void
859 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
860 {
861   struct ServiceListeningInfo *serviceListeningInfo = cls;
862   struct ForwardedConnection *fc;
863
864   serviceListeningInfo->acceptTask = GNUNET_SCHEDULER_NO_TASK;
865   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
866     return;
867   fc = GNUNET_malloc (sizeof (struct ForwardedConnection));
868   fc->listen_info = serviceListeningInfo;
869   fc->service_to_client_bufferPos = fc->service_to_client_buffer;
870   fc->client_to_service_bufferPos = fc->client_to_service_buffer;
871   fc->client_addr_len = sizeof (fc->client_addr);
872   fc->armClientSocket = GNUNET_NETWORK_socket_accept (serviceListeningInfo->listeningSocket,
873                                                       (struct sockaddr*) fc->client_addr,
874                                                       &fc->client_addr_len);
875   if (NULL == fc->armClientSocket)
876     {
877       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
878                   _("Unable to accept connection for service `%s': %s\n"),
879                   serviceListeningInfo->serviceName,
880                   STRERROR (errno));
881       GNUNET_free (fc);
882       serviceListeningInfo->acceptTask =
883         GNUNET_SCHEDULER_add_read_net (scheduler,
884                                        GNUNET_TIME_UNIT_FOREVER_REL, 
885                                        serviceListeningInfo->listeningSocket,
886                                        &acceptConnection,
887                                        serviceListeningInfo);
888       return;
889     }
890   GNUNET_break (GNUNET_OK ==
891                 GNUNET_NETWORK_socket_close (serviceListeningInfo->listeningSocket));
892   GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
893                                serviceListeningInfoList_tail, 
894                                serviceListeningInfo);
895   start_service (NULL, serviceListeningInfo->serviceName);
896   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
897               _("Service `%s' started\n"),
898               fc->listen_info->serviceName);
899   fc->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_SERVICE_TIMEOUT);
900   fc->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
901   fc->client_to_service_task =
902     GNUNET_SCHEDULER_add_read_net (scheduler,
903                                    GNUNET_TIME_UNIT_FOREVER_REL,
904                                    fc->armClientSocket,
905                                    &receiveFromClient, fc);
906   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
907   fc->start_task 
908     = GNUNET_SCHEDULER_add_now (scheduler,
909                                 &start_forwarding,
910                                 fc);
911 }
912
913
914 /**
915  * Creating a listening socket for each of the service's addresses and
916  * wait for the first incoming connection to it
917  * 
918  * @param sa address associated with the service
919  * @param addr_len length of sa
920  * @param serviceName the name of the service in question
921  */
922 static void
923 createListeningSocket (struct sockaddr *sa, 
924                        socklen_t addr_len,
925                        const char *serviceName)
926 {
927   const static int on = 1;
928   struct GNUNET_NETWORK_Handle *sock;
929   struct ServiceListeningInfo *serviceListeningInfo;
930
931   switch (sa->sa_family)
932     {
933     case AF_INET:
934       sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
935       break;
936     case AF_INET6:
937       sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
938       break;
939     default:
940       sock = NULL;
941       break;
942     }
943   if (NULL == sock)
944     {
945       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
946                   _("Unable to create socket for service `%s'"),
947                   serviceName);
948       GNUNET_free (sa);
949       return;
950     }
951   if (GNUNET_NETWORK_socket_setsockopt
952       (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
953     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
954                          "setsockopt");
955 #ifdef IPV6_V6ONLY
956   if ( (sa->sa_family == AF_INET6) &&
957        (GNUNET_NETWORK_socket_setsockopt
958         (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
959     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
960                          "setsockopt");
961 #endif
962
963   if (GNUNET_NETWORK_socket_bind
964       (sock, (const struct sockaddr *) sa, addr_len) != GNUNET_OK)
965     {
966       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
967                   _("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
968                   serviceName,
969                   GNUNET_a2s (sa, addr_len),
970                   STRERROR (errno));
971       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
972       GNUNET_free (sa);
973       return;
974     }
975   if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
976     {
977       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
978                            "listen");
979       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
980       GNUNET_free (sa);
981       return;
982     }
983   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
984               _("ARM now monitors connections to service `%s' at `%s'\n"),
985               serviceName,
986               GNUNET_a2s (sa, addr_len));
987   serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
988   serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
989   serviceListeningInfo->service_addr = sa;
990   serviceListeningInfo->service_addr_len = addr_len;
991   serviceListeningInfo->listeningSocket = sock;
992   serviceListeningInfo->acceptTask =
993     GNUNET_SCHEDULER_add_read_net (scheduler,
994                                    GNUNET_TIME_UNIT_FOREVER_REL, sock,
995                                    &acceptConnection,
996                                    serviceListeningInfo);
997   GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
998                                serviceListeningInfoList_tail,
999                                serviceListeningInfo);
1000 }
1001
1002
1003 /**
1004  * Callback function, checks whether the current tokens are representing a service,
1005  * gets its addresses and create listening socket for it.
1006  * 
1007  * @param cls callback data, not used
1008  * @param section configuration section
1009  * @param option configuration option
1010  * @param value the option's value
1011  */
1012 static void
1013 checkPortNumberCB (void *cls,
1014                    const char *section, 
1015                    const char *option, 
1016                    const char *value)
1017 {
1018   struct sockaddr **addrs;
1019   socklen_t *addr_lens;
1020   int ret;
1021   unsigned int i;
1022   
1023   if ( (strcasecmp (section, "arm") == 0) ||
1024        (strcasecmp (option, "AUTOSTART") != 0) ||
1025        (strcasecmp (value, "YES") != 0) ||
1026        (isInDefaultList (section) == GNUNET_YES) )
1027     return;
1028   if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs,
1029                                                        &addr_lens)))
1030     return;
1031   /* this will free (or capture) addrs[i] */
1032   for (i = 0; i < ret; i++)
1033     createListeningSocket (addrs[i], addr_lens[i], section);
1034   GNUNET_free (addrs);
1035   GNUNET_free (addr_lens);
1036 }
1037
1038
1039 /**
1040  * Entry point to the Service Manager
1041  *
1042  * @param configurationHandle configuration to use to get services
1043  * @param sched scheduler to handle clients and services communications
1044  */
1045 void
1046 prepareServices (const struct GNUNET_CONFIGURATION_Handle
1047                  *configurationHandle, struct GNUNET_SCHEDULER_Handle *sched)
1048 {
1049   char *defaultServicesString;
1050
1051   scheduler = sched;
1052   cfg = configurationHandle;
1053   /* Split the default services into a list */
1054   if (GNUNET_OK ==
1055       GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
1056                                              &defaultServicesString))
1057     {
1058       addDefaultServicesToList (defaultServicesString);
1059       GNUNET_free (defaultServicesString);    
1060     }
1061   /* Spot the services from the configuration and create a listening
1062      socket for each */
1063   GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
1064 }
1065
1066 /* end of gnunet-service-arm_interceptor.c */