no debug
[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
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 DEBUG_SERVICE_MANAGER
478       if (fc->service_to_client_bufferDataLength == 0)
479         {
480           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
481                       "Service `%s' stopped sending data.\n",
482                       fc->listen_info->serviceName);
483         }
484 #endif
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  * First connection has come to the listening socket associated with the service,
852  * create the service in order to relay the incoming connection to it
853  * 
854  * @param cls callback data, struct ServiceListeningInfo describing a listen socket
855  * @param tc context 
856  */
857 static void
858 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
859
860
861 static void
862 accept_and_forward (struct ServiceListeningInfo *serviceListeningInfo)
863 {
864   struct ForwardedConnection *fc;
865
866   fc = GNUNET_malloc (sizeof (struct ForwardedConnection));
867   fc->listen_info = serviceListeningInfo;
868   fc->service_to_client_bufferPos = fc->service_to_client_buffer;
869   fc->client_to_service_bufferPos = fc->client_to_service_buffer;
870   fc->client_addr_len = sizeof (fc->client_addr);
871   fc->armClientSocket = GNUNET_NETWORK_socket_accept (serviceListeningInfo->listeningSocket,
872                                                       (struct sockaddr*) fc->client_addr,
873                                                       &fc->client_addr_len);
874   if (NULL == fc->armClientSocket)
875     {
876       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
877                   _("Unable to accept connection for service `%s': %s\n"),
878                   serviceListeningInfo->serviceName,
879                   STRERROR (errno));
880       GNUNET_free (fc);
881       GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
882                                    serviceListeningInfoList_tail, 
883                                    serviceListeningInfo); 
884       serviceListeningInfo->acceptTask =
885         GNUNET_SCHEDULER_add_read_net (scheduler,
886                                        GNUNET_TIME_UNIT_FOREVER_REL, 
887                                        serviceListeningInfo->listeningSocket,
888                                        &acceptConnection,
889                                        serviceListeningInfo);
890       return;
891     }
892   GNUNET_break (GNUNET_OK ==
893                 GNUNET_NETWORK_socket_close (serviceListeningInfo->listeningSocket));
894   start_service (NULL, serviceListeningInfo->serviceName, NULL);
895   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
896               _("Service `%s' started\n"),
897               fc->listen_info->serviceName);
898   fc->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_SERVICE_TIMEOUT);
899   fc->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
900   fc->client_to_service_task =
901     GNUNET_SCHEDULER_add_read_net (scheduler,
902                                    GNUNET_TIME_UNIT_FOREVER_REL,
903                                    fc->armClientSocket,
904                                    &receiveFromClient, fc);
905   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
906   fc->start_task 
907     = GNUNET_SCHEDULER_add_now (scheduler,
908                                 &start_forwarding,
909                                 fc);
910 }
911
912
913 /**
914  * First connection has come to the listening socket associated with the service,
915  * create the service in order to relay the incoming connection to it
916  * 
917  * @param cls callback data, struct ServiceListeningInfo describing a listen socket
918  * @param tc context 
919  */
920 static void
921 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
922 {
923   struct ServiceListeningInfo *sli = cls;
924   struct ServiceListeningInfo *pos;
925   struct ServiceListeningInfo *next;
926   int *lsocks;
927   unsigned int ls;
928   int use_lsocks;
929
930   sli->acceptTask = GNUNET_SCHEDULER_NO_TASK;
931   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
932     return;
933   GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
934                                serviceListeningInfoList_tail, 
935                                sli);  
936 #ifndef MINGW
937   use_lsocks = GNUNET_YES;
938   if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (cfg,
939                                                      sli->serviceName,
940                                                      "DISABLE_SOCKET_FORWARDING"))
941     use_lsocks = GNUNET_CONFIGURATION_get_value_yesno (cfg,
942                                                        sli->serviceName,
943                                                        "DISABLE_SOCKET_FORWARDING");
944 #else
945   use_lsocks = GNUNET_NO;
946 #endif
947   if (GNUNET_YES != use_lsocks)
948     {
949       accept_and_forward (sli);
950       return;
951     }
952   lsocks = NULL;
953   ls = 0;
954   next = serviceListeningInfoList_head;
955   while (NULL != (pos = next))
956     {
957       next = pos->next;
958       if (0 == strcmp (pos->serviceName,
959                        sli->serviceName))
960         {
961           GNUNET_array_append (lsocks, ls, 
962                                GNUNET_NETWORK_get_fd (pos->listeningSocket));     
963           GNUNET_free (pos->listeningSocket); /* deliberately no closing! */
964           GNUNET_free (pos->service_addr);
965           GNUNET_free (pos->serviceName);
966           GNUNET_SCHEDULER_cancel (scheduler,
967                                    pos->acceptTask);
968           GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
969                                        serviceListeningInfoList_tail, 
970                                        pos);
971           GNUNET_free (pos);
972         }
973     }
974   GNUNET_array_append (lsocks, ls, 
975                        GNUNET_NETWORK_get_fd (sli->listeningSocket));
976   GNUNET_free (sli->listeningSocket); /* deliberately no closing! */
977   GNUNET_free (sli->service_addr);
978   GNUNET_array_append (lsocks, ls, -1);
979   start_service (NULL, 
980                  sli->serviceName,
981                  lsocks);
982   ls = 0;
983   while (lsocks[ls] != -1)
984     GNUNET_break (0 == close (lsocks[ls++]));      
985   GNUNET_array_grow (lsocks, ls, 0);
986   GNUNET_free (sli->serviceName);
987   GNUNET_free (sli); 
988 }
989
990
991 /**
992  * Creating a listening socket for each of the service's addresses and
993  * wait for the first incoming connection to it
994  * 
995  * @param sa address associated with the service
996  * @param addr_len length of sa
997  * @param serviceName the name of the service in question
998  */
999 static void
1000 createListeningSocket (struct sockaddr *sa, 
1001                        socklen_t addr_len,
1002                        const char *serviceName)
1003 {
1004   const static int on = 1;
1005   struct GNUNET_NETWORK_Handle *sock;
1006   struct ServiceListeningInfo *serviceListeningInfo;
1007
1008   switch (sa->sa_family)
1009     {
1010     case AF_INET:
1011       sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
1012       break;
1013     case AF_INET6:
1014       sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
1015       break;
1016     case AF_UNIX:
1017       sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
1018       break;
1019     default:
1020       GNUNET_break (0);
1021       sock = NULL;
1022       errno = EAFNOSUPPORT;
1023       break;
1024     }
1025   if (NULL == sock)
1026     {
1027       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1028                   _("Unable to create socket for service `%s': %s\n"),
1029                   serviceName,
1030                   STRERROR (errno));
1031       GNUNET_free (sa);
1032       return;
1033     }
1034   if (GNUNET_NETWORK_socket_setsockopt
1035       (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
1036     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1037                          "setsockopt");
1038 #ifdef IPV6_V6ONLY
1039   if ( (sa->sa_family == AF_INET6) &&
1040        (GNUNET_NETWORK_socket_setsockopt
1041         (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
1042     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1043                          "setsockopt");
1044 #endif
1045
1046   if (GNUNET_NETWORK_socket_bind
1047       (sock, (const struct sockaddr *) sa, addr_len) != GNUNET_OK)
1048     {
1049       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1050                   _("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
1051                   serviceName,
1052                   GNUNET_a2s (sa, addr_len),
1053                   STRERROR (errno));
1054       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1055       GNUNET_free (sa);
1056       return;
1057     }
1058   if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
1059     {
1060       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1061                            "listen");
1062       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1063       GNUNET_free (sa);
1064       return;
1065     }
1066   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1067               _("ARM now monitors connections to service `%s' at `%s'\n"),
1068               serviceName,
1069               GNUNET_a2s (sa, addr_len));
1070   serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
1071   serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
1072   serviceListeningInfo->service_addr = sa;
1073   serviceListeningInfo->service_addr_len = addr_len;
1074   serviceListeningInfo->listeningSocket = sock;
1075   serviceListeningInfo->acceptTask =
1076     GNUNET_SCHEDULER_add_read_net (scheduler,
1077                                    GNUNET_TIME_UNIT_FOREVER_REL, sock,
1078                                    &acceptConnection,
1079                                    serviceListeningInfo);
1080   GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
1081                                serviceListeningInfoList_tail,
1082                                serviceListeningInfo);
1083 }
1084
1085
1086 /**
1087  * Callback function, checks whether the current tokens are representing a service,
1088  * gets its addresses and create listening socket for it.
1089  * 
1090  * @param cls callback data, not used
1091  * @param section configuration section
1092  * @param option configuration option
1093  * @param value the option's value
1094  */
1095 static void
1096 checkPortNumberCB (void *cls,
1097                    const char *section, 
1098                    const char *option, 
1099                    const char *value)
1100 {
1101   struct sockaddr **addrs;
1102   socklen_t *addr_lens;
1103   int ret;
1104   unsigned int i;
1105   
1106   if ( (strcasecmp (section, "arm") == 0) ||
1107        (strcasecmp (option, "AUTOSTART") != 0) ||
1108        (strcasecmp (value, "YES") != 0) ||
1109        (isInDefaultList (section) == GNUNET_YES) )
1110     return;
1111   if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs,
1112                                                        &addr_lens)))
1113     return;
1114   /* this will free (or capture) addrs[i] */
1115   for (i = 0; i < ret; i++)
1116     createListeningSocket (addrs[i], addr_lens[i], section);
1117   GNUNET_free (addrs);
1118   GNUNET_free (addr_lens);
1119 }
1120
1121
1122 /**
1123  * Entry point to the Service Manager
1124  *
1125  * @param configurationHandle configuration to use to get services
1126  * @param sched scheduler to handle clients and services communications
1127  */
1128 void
1129 prepareServices (const struct GNUNET_CONFIGURATION_Handle
1130                  *configurationHandle, struct GNUNET_SCHEDULER_Handle *sched)
1131 {
1132   char *defaultServicesString;
1133
1134   scheduler = sched;
1135   cfg = configurationHandle;
1136   /* Split the default services into a list */
1137   if (GNUNET_OK ==
1138       GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
1139                                              &defaultServicesString))
1140     {
1141       addDefaultServicesToList (defaultServicesString);
1142       GNUNET_free (defaultServicesString);    
1143     }
1144   /* Spot the services from the configuration and create a listening
1145      socket for each */
1146   GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
1147 }
1148
1149 /* end of gnunet-service-arm_interceptor.c */