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