working version
[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 !=
395       GNUNET_NETWORK_fdset_isset (tc->write_ready, fc->armClientSocket))
396   {
397     fc->service_to_client_task =
398         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
399                                         fc->armClientSocket, &forwardToClient,
400                                         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, "Forwarded %d bytes to client\n",
419               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, &forwardToClient,
428                                         fc);
429     return;
430   }
431   fc->service_to_client_task =
432       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
433                                      fc->armServiceSocket, &receiveFromService,
434                                      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 !=
458       GNUNET_NETWORK_fdset_isset (tc->read_ready, 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           GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_min
506                                         (fc->back_off, rem), &start_forwarding,
507                                         fc);
508     }
509     else
510     {
511 #if DEBUG_SERVICE_MANAGER
512       if (fc->service_to_client_bufferDataLength != 0)
513         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
514                     "Error receiving from service: %s\n", STRERROR (errno));
515 #endif
516       closeClientAndServiceSockets (fc, REASON_SERVICE_TO_CLIENT);
517     }
518     return;
519   }
520   fc->first_write_done = GNUNET_YES;
521 #if DEBUG_SERVICE_MANAGER
522   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received %d bytes for client\n",
523               fc->service_to_client_bufferDataLength);
524 #endif
525   fc->service_to_client_task =
526       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
527                                       fc->armClientSocket, &forwardToClient,
528                                       fc);
529 }
530
531
532 /**
533  * Forward client message to service
534  *
535  * @param cls callback data, struct ForwardedConnection for the communication between client and service
536  * @param tc scheduler context
537  */
538 static void
539 forwardToService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
540 {
541   struct ForwardedConnection *fc = cls;
542   ssize_t numberOfBytesSent;
543   struct GNUNET_TIME_Relative rem;
544
545   fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
546   if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
547       (fc->first_write_done != GNUNET_YES))
548   {
549     closeClientAndServiceSockets (fc, REASON_ERROR);
550     return;
551   }
552   if (GNUNET_YES !=
553       GNUNET_NETWORK_fdset_isset (tc->write_ready, fc->armServiceSocket))
554   {
555     fc->client_to_service_task =
556         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
557                                         fc->armServiceSocket, &forwardToService,
558                                         fc);
559     return;
560   }
561   numberOfBytesSent =
562       GNUNET_NETWORK_socket_send (fc->armServiceSocket,
563                                   fc->client_to_service_bufferPos,
564                                   fc->client_to_service_bufferDataLength);
565   if (numberOfBytesSent <= 0)
566   {
567     if (GNUNET_YES != fc->first_write_done)
568     {
569       GNUNET_break (GNUNET_OK ==
570                     GNUNET_NETWORK_socket_close (fc->armServiceSocket));
571       fc->armServiceSocket = NULL;
572       if ((fc->service_to_client_bufferDataLength == 0) &&
573           (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK))
574       {
575         GNUNET_SCHEDULER_cancel (fc->service_to_client_task);
576         fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
577       }
578       fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
579 #if DEBUG_SERVICE_MANAGER
580       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
581                   "Failed to connect to service `%s' at `%s', will try again in %llu ms\n",
582                   fc->listen_info->serviceName,
583                   GNUNET_a2s (fc->listen_info->service_addr,
584                               fc->listen_info->service_addr_len),
585                   (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
586                                                                  rem).rel_value);
587 #endif
588       rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
589       GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
590       fc->start_task =
591           GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_min
592                                         (fc->back_off, rem), &start_forwarding,
593                                         fc);
594     }
595     else
596     {
597       if ((errno != EPIPE) && (errno != ECONNRESET))
598         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
599                     "Failed to forward data to service: %s\n",
600                     STRERROR (errno));
601       closeClientAndServiceSockets (fc, REASON_CLIENT_TO_SERVICE);
602     }
603     return;
604   }
605 #if DEBUG_SERVICE_MANAGER
606   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Forwarded %d bytes to service\n",
607               numberOfBytesSent);
608 #endif
609   fc->first_write_done = GNUNET_YES;
610   if (numberOfBytesSent < fc->client_to_service_bufferDataLength)
611   {
612     fc->client_to_service_bufferPos += numberOfBytesSent;
613     fc->client_to_service_bufferDataLength -= numberOfBytesSent;
614     fc->client_to_service_task =
615         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
616                                         fc->armServiceSocket, &forwardToService,
617                                         fc);
618     return;
619   }
620   fc->client_to_service_task =
621       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
622                                      fc->armClientSocket, &receiveFromClient,
623                                      fc);
624 }
625
626
627 /**
628  * Read data from the client and then forward it to the service.
629  *
630  * @param cls callback data,   struct ForwardedConnection for the communication between client and service
631  * @param tc context
632  */
633 static void
634 receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
635 {
636   struct ForwardedConnection *fc = cls;
637
638   fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
639   if (GNUNET_YES !=
640       GNUNET_NETWORK_fdset_isset (tc->read_ready, fc->armClientSocket))
641   {
642     fc->client_to_service_task =
643         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
644                                        fc->armClientSocket, &receiveFromClient,
645                                        fc);
646     return;
647   }
648   fc->client_to_service_bufferPos = fc->client_to_service_buffer;
649   fc->client_to_service_bufferDataLength =
650       GNUNET_NETWORK_socket_recv (fc->armClientSocket,
651                                   fc->client_to_service_buffer, BUFFER_SIZE);
652   if (fc->client_to_service_bufferDataLength <= 0)
653   {
654     if (fc->client_to_service_bufferDataLength == 0)
655     {
656 #if DEBUG_SERVICE_MANAGER
657       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
658                   "Client closed connection with service `%s'\n",
659                   fc->listen_info->serviceName);
660 #endif
661     }
662     else
663     {
664 #if DEBUG_SERVICE_MANAGER
665       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error receiving from client: %s\n",
666                   STRERROR (errno));
667 #endif
668     }
669     closeClientAndServiceSockets (fc, REASON_CLIENT_TO_SERVICE);
670     return;
671   }
672 #if DEBUG_SERVICE_MANAGER
673   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received %d bytes for service\n",
674               fc->client_to_service_bufferDataLength);
675 #endif
676   if (fc->armServiceSocket != NULL)
677     fc->client_to_service_task =
678         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
679                                         fc->armServiceSocket, &forwardToService,
680                                         fc);
681   else
682     /* We have not added any task with fc as a closure, so we're
683      * dropping our reference to fc
684      */
685     fc->reference_count -= 1;
686 }
687
688
689 static void
690 fc_acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
691 {
692   struct ServiceListeningInfo *sli = cls;
693   struct ForwardedConnection *fc = sli->fc;
694
695   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))
696   {
697     GNUNET_assert (GNUNET_OK ==
698                    GNUNET_NETWORK_socket_close (sli->listeningSocket));
699     closeClientAndServiceSockets (fc, REASON_ERROR);
700     GNUNET_free (sli);
701     return;
702   }
703 #if DEBUG_SERVICE_MANAGER
704   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
705               "Connected to service, now starting forwarding\n");
706 #endif
707   fc->armServiceSocket = sli->listeningSocket;
708   GNUNET_free (fc->listen_info->service_addr);
709   fc->listen_info->service_addr = sli->service_addr;
710   fc->listen_info->service_addr_len = sli->service_addr_len;
711   /* Drop fc reference count prematurely, it'll be incremented
712    * once or twice in the following conditional branches.
713    * This is, apparently, the place where reference count increases
714    * past 1.
715    */
716   fc->reference_count -= 1;
717   if (fc->client_to_service_task == GNUNET_SCHEDULER_NO_TASK)
718   {
719     if (fc->client_to_service_bufferDataLength == 0)
720       fc->client_to_service_task =
721           GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
722                                          fc->armClientSocket,
723                                          &receiveFromClient, fc);
724     else
725       fc->client_to_service_task =
726           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
727                                           fc->armServiceSocket,
728                                           &forwardToService, fc);
729     fc->reference_count += 1;
730   }
731   if (fc->service_to_client_task == GNUNET_SCHEDULER_NO_TASK)
732   {
733     if (fc->service_to_client_bufferDataLength == 0)
734       fc->service_to_client_task =
735           GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
736                                          fc->armServiceSocket,
737                                          &receiveFromService, fc);
738     else
739       fc->service_to_client_task =
740           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
741                                           fc->armClientSocket, &forwardToClient,
742                                           fc);
743     fc->reference_count += 1;
744   }
745   GNUNET_free (sli);
746 }
747
748
749 static struct ServiceListeningInfo *
750 service_try_to_connect (const struct sockaddr *addr, int pf, socklen_t addrlen,
751                         struct ForwardedConnection *fc)
752 {
753   struct GNUNET_NETWORK_Handle *sock;
754   struct ServiceListeningInfo *serviceListeningInfo;
755
756   sock = GNUNET_NETWORK_socket_create (pf, SOCK_STREAM, 0);
757   if (sock == NULL)
758   {
759     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket");
760     return NULL;
761   }
762   if ((GNUNET_SYSERR == GNUNET_NETWORK_socket_connect (sock, addr, addrlen)) &&
763       (errno != EINPROGRESS))
764   {
765     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
766     GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
767     return NULL;
768   }
769   serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
770   serviceListeningInfo->serviceName = NULL;
771   serviceListeningInfo->service_addr = GNUNET_malloc (addrlen);
772   memcpy (serviceListeningInfo->service_addr, addr, addrlen);
773   serviceListeningInfo->service_addr_len = addrlen;
774   serviceListeningInfo->listeningSocket = sock;
775   serviceListeningInfo->fc = fc;
776   serviceListeningInfo->acceptTask =
777       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
778                                       serviceListeningInfo->listeningSocket,
779                                       &fc_acceptConnection,
780                                       serviceListeningInfo);
781   return serviceListeningInfo;
782 }
783
784
785 /**
786  *
787  */
788 static void
789 start_forwarding (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
790 {
791   struct ForwardedConnection *fc = cls;
792   struct ServiceListeningInfo *sc;
793   struct sockaddr_in target_ipv4;
794   struct sockaddr_in6 target_ipv6;
795   const struct sockaddr_in *v4;
796   const struct sockaddr_in6 *v6;
797   char listen_address[INET6_ADDRSTRLEN];
798
799   fc->start_task = GNUNET_SCHEDULER_NO_TASK;
800   if ((NULL != tc) && (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)))
801   {
802     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
803                 _("Unable to forward to service `%s': shutdown\n"),
804                 fc->listen_info->serviceName);
805     closeClientAndServiceSockets (fc, REASON_ERROR);
806     return;
807   }
808   if (0 == GNUNET_TIME_absolute_get_remaining (fc->timeout).rel_value)
809   {
810     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
811                 _
812                 ("Unable to forward to service `%s': timeout before connect\n"),
813                 fc->listen_info->serviceName);
814     closeClientAndServiceSockets (fc, REASON_ERROR);
815     return;
816   }
817   switch (fc->listen_info->service_addr->sa_family)
818   {
819   case AF_UNSPEC:
820     GNUNET_break (0);
821     closeClientAndServiceSockets (fc, REASON_ERROR);
822     return;
823   case AF_INET:
824     v4 = (const struct sockaddr_in *) fc->listen_info->service_addr;
825     inet_ntop (fc->listen_info->service_addr->sa_family,
826                (const void *) &v4->sin_addr, listen_address, INET_ADDRSTRLEN);
827     if (0 == strncmp (listen_address, "0.0.0.0", 7))
828     {
829       /* connect to [::1] and 127.0.0.1 instead of [::] and 0.0.0.0 */
830       memset (&target_ipv4, 0, sizeof (target_ipv4));
831       GNUNET_assert (1 ==
832                      inet_pton (AF_INET, "127.0.0.1", &target_ipv4.sin_addr));
833       target_ipv4.sin_family = AF_INET;
834       target_ipv4.sin_port = v4->sin_port;
835       v4 = &target_ipv4;
836     }
837     sc = service_try_to_connect ((const struct sockaddr *) v4, PF_INET,
838                                  sizeof (struct sockaddr_in), fc);
839     break;
840   case AF_INET6:
841     v6 = (struct sockaddr_in6 *) fc->listen_info->service_addr;
842     inet_ntop (fc->listen_info->service_addr->sa_family,
843                (const void *) &v6->sin6_addr, listen_address, INET6_ADDRSTRLEN);
844     if ((strncmp (listen_address, "[::]:", 5) == 0) ||
845         (strncmp (listen_address, "::", 2) == 0))
846     {
847       memset (&target_ipv6, 0, sizeof (target_ipv6));
848       target_ipv6.sin6_addr = in6addr_loopback;
849       target_ipv6.sin6_family = AF_INET6;
850       target_ipv6.sin6_port = v6->sin6_port;
851       v6 = &target_ipv6;
852     }
853     sc = service_try_to_connect ((const struct sockaddr *) v6, PF_INET6,
854                                  sizeof (struct sockaddr_in6), fc);
855     break;
856   case AF_UNIX:
857     sc = service_try_to_connect (fc->listen_info->service_addr, PF_UNIX,
858                                  fc->listen_info->service_addr_len, fc);
859     break;
860   default:
861     GNUNET_break (0);
862     closeClientAndServiceSockets (fc, REASON_ERROR);
863     return;
864   }
865   if (NULL == sc)
866   {
867     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
868                 _("Unable to start service `%s': %s\n"),
869                 fc->listen_info->serviceName, STRERROR (errno));
870     closeClientAndServiceSockets (fc, REASON_ERROR);
871     return;
872   }
873 }
874
875
876 /**
877  *
878  */
879 int
880 stop_listening (const char *serviceName)
881 {
882   struct ServiceListeningInfo *pos;
883   struct ServiceListeningInfo *next;
884   int ret;
885
886   ret = GNUNET_NO;
887   next = serviceListeningInfoList_head;
888   while (NULL != (pos = next))
889   {
890     next = pos->next;
891     if ((serviceName != NULL) && (strcmp (pos->serviceName, serviceName) != 0))
892       continue;
893     if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK)
894       GNUNET_SCHEDULER_cancel (pos->acceptTask);
895     GNUNET_break (GNUNET_OK ==
896                   GNUNET_NETWORK_socket_close (pos->listeningSocket));
897     GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
898                                  serviceListeningInfoList_tail, pos);
899     GNUNET_free (pos->serviceName);
900     GNUNET_free (pos->service_addr);
901     GNUNET_free (pos);
902     ret = GNUNET_OK;
903   }
904   return ret;
905 }
906
907 /**
908  * First connection has come to the listening socket associated with the service,
909  * create the service in order to relay the incoming connection to it
910  *
911  * @param cls callback data, struct ServiceListeningInfo describing a listen socket
912  * @param tc context
913  */
914 static void
915 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
916
917
918 static void
919 accept_and_forward (struct ServiceListeningInfo *serviceListeningInfo)
920 {
921   struct ForwardedConnection *fc;
922
923   fc = GNUNET_malloc (sizeof (struct ForwardedConnection));
924   fc->reference_count = 1;
925   fc->listen_info = serviceListeningInfo;
926   fc->service_to_client_bufferPos = fc->service_to_client_buffer;
927   fc->client_to_service_bufferPos = fc->client_to_service_buffer;
928   fc->client_addr_len = sizeof (fc->client_addr);
929   fc->armClientSocket =
930       GNUNET_NETWORK_socket_accept (serviceListeningInfo->listeningSocket,
931                                     (struct sockaddr *) fc->client_addr,
932                                     &fc->client_addr_len);
933   if (NULL == fc->armClientSocket)
934   {
935     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
936                 _("Unable to accept connection for service `%s': %s\n"),
937                 serviceListeningInfo->serviceName, STRERROR (errno));
938     GNUNET_free (fc);
939     GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
940                                  serviceListeningInfoList_tail,
941                                  serviceListeningInfo);
942     serviceListeningInfo->acceptTask =
943         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
944                                        serviceListeningInfo->listeningSocket,
945                                        &acceptConnection, serviceListeningInfo);
946     return;
947   }
948   GNUNET_break (GNUNET_OK ==
949                 GNUNET_NETWORK_socket_close
950                 (serviceListeningInfo->listeningSocket));
951   start_service (NULL, serviceListeningInfo->serviceName, NULL);
952   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Service `%s' started\n"),
953               fc->listen_info->serviceName);
954   fc->timeout =
955       GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_SERVICE_TIMEOUT);
956   fc->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
957   fc->client_to_service_task =
958       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
959                                      fc->armClientSocket, &receiveFromClient,
960                                      fc);
961   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
962   /* We're creating another chain of tasks for this fc that
963    * will have its own reference to it.
964    */
965   fc->reference_count += 1;
966   fc->start_task = GNUNET_SCHEDULER_add_now (&start_forwarding, fc);
967 }
968
969
970 /**
971  * First connection has come to the listening socket associated with the service,
972  * create the service in order to relay the incoming connection to it
973  *
974  * @param cls callback data, struct ServiceListeningInfo describing a listen socket
975  * @param tc context
976  */
977 static void
978 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
979 {
980   struct ServiceListeningInfo *sli = cls;
981   struct ServiceListeningInfo *pos;
982   struct ServiceListeningInfo *next;
983   int *lsocks;
984   unsigned int ls;
985   int use_lsocks;
986
987   sli->acceptTask = GNUNET_SCHEDULER_NO_TASK;
988   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
989     return;
990   GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
991                                serviceListeningInfoList_tail, sli);
992 #ifndef MINGW
993   use_lsocks = GNUNET_NO;
994   if (GNUNET_YES ==
995       GNUNET_CONFIGURATION_have_value (cfg, sli->serviceName,
996                                        "DISABLE_SOCKET_FORWARDING"))
997     use_lsocks =
998         GNUNET_CONFIGURATION_get_value_yesno (cfg, sli->serviceName,
999                                               "DISABLE_SOCKET_FORWARDING");
1000 #else
1001   use_lsocks = GNUNET_YES;
1002 #endif
1003   if (GNUNET_NO != use_lsocks)
1004   {
1005     accept_and_forward (sli);
1006     return;
1007   }
1008   lsocks = NULL;
1009   ls = 0;
1010   next = serviceListeningInfoList_head;
1011   while (NULL != (pos = next))
1012   {
1013     next = pos->next;
1014     if (0 == strcmp (pos->serviceName, sli->serviceName))
1015     {
1016       GNUNET_array_append (lsocks, ls,
1017                            GNUNET_NETWORK_get_fd (pos->listeningSocket));
1018       GNUNET_free (pos->listeningSocket);       /* deliberately no closing! */
1019       GNUNET_free (pos->service_addr);
1020       GNUNET_free (pos->serviceName);
1021       GNUNET_SCHEDULER_cancel (pos->acceptTask);
1022       GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
1023                                    serviceListeningInfoList_tail, pos);
1024       GNUNET_free (pos);
1025     }
1026   }
1027   GNUNET_array_append (lsocks, ls,
1028                        GNUNET_NETWORK_get_fd (sli->listeningSocket));
1029   GNUNET_free (sli->listeningSocket);   /* deliberately no closing! */
1030   GNUNET_free (sli->service_addr);
1031   GNUNET_array_append (lsocks, ls, -1);
1032   start_service (NULL, sli->serviceName, lsocks);
1033   ls = 0;
1034   while (lsocks[ls] != -1)
1035     GNUNET_break (0 == close (lsocks[ls++]));
1036   GNUNET_array_grow (lsocks, ls, 0);
1037   GNUNET_free (sli->serviceName);
1038   GNUNET_free (sli);
1039 }
1040
1041
1042 /**
1043  * Creating a listening socket for each of the service's addresses and
1044  * wait for the first incoming connection to it
1045  *
1046  * @param sa address associated with the service
1047  * @param addr_len length of sa
1048  * @param serviceName the name of the service in question
1049  */
1050 static void
1051 createListeningSocket (struct sockaddr *sa, socklen_t addr_len,
1052                        const char *serviceName)
1053 {
1054   const static int on = 1;
1055   struct GNUNET_NETWORK_Handle *sock;
1056   struct ServiceListeningInfo *serviceListeningInfo;
1057
1058   switch (sa->sa_family)
1059   {
1060   case AF_INET:
1061     sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
1062     break;
1063   case AF_INET6:
1064     sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
1065     break;
1066   case AF_UNIX:
1067     if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0)   /* Do not bind to blank UNIX path! */
1068       return;
1069     sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
1070     break;
1071   default:
1072     GNUNET_break (0);
1073     sock = NULL;
1074     errno = EAFNOSUPPORT;
1075     break;
1076   }
1077   if (NULL == sock)
1078   {
1079     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1080                 _("Unable to create socket for service `%s': %s\n"),
1081                 serviceName, STRERROR (errno));
1082     GNUNET_free (sa);
1083     return;
1084   }
1085   if (GNUNET_NETWORK_socket_setsockopt
1086       (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
1087     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1088                          "setsockopt");
1089 #ifdef IPV6_V6ONLY
1090   if ((sa->sa_family == AF_INET6) &&
1091       (GNUNET_NETWORK_socket_setsockopt
1092        (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
1093     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1094                          "setsockopt");
1095 #endif
1096
1097   if (GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) sa, addr_len)
1098       != GNUNET_OK)
1099   {
1100     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1101                 _
1102                 ("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
1103                 serviceName, GNUNET_a2s (sa, addr_len), STRERROR (errno));
1104     GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1105     GNUNET_free (sa);
1106     return;
1107   }
1108   if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
1109   {
1110     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
1111     GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1112     GNUNET_free (sa);
1113     return;
1114   }
1115   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1116               _("ARM now monitors connections to service `%s' at `%s'\n"),
1117               serviceName, GNUNET_a2s (sa, addr_len));
1118   serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
1119   serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
1120   serviceListeningInfo->service_addr = sa;
1121   serviceListeningInfo->service_addr_len = addr_len;
1122   serviceListeningInfo->listeningSocket = sock;
1123   serviceListeningInfo->acceptTask =
1124       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock,
1125                                      &acceptConnection, serviceListeningInfo);
1126   GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
1127                                serviceListeningInfoList_tail,
1128                                serviceListeningInfo);
1129 }
1130
1131
1132 /**
1133  * Callback function, checks whether the current tokens are representing a service,
1134  * gets its addresses and create listening socket for it.
1135  *
1136  * @param cls callback data, not used
1137  * @param section configuration section
1138  * @param option configuration option
1139  * @param value the option's value
1140  */
1141 static void
1142 checkPortNumberCB (void *cls, const char *section, const char *option,
1143                    const char *value)
1144 {
1145   struct sockaddr **addrs;
1146   socklen_t *addr_lens;
1147   int ret;
1148   unsigned int i;
1149
1150   if ((strcasecmp (section, "arm") == 0) ||
1151       (strcasecmp (option, "AUTOSTART") != 0) ||
1152       (strcasecmp (value, "YES") != 0) ||
1153       (isInDefaultList (section) == GNUNET_YES))
1154     return;
1155   if (0 >=
1156       (ret =
1157        GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs, &addr_lens)))
1158     return;
1159   /* this will free (or capture) addrs[i] */
1160   for (i = 0; i < ret; i++)
1161     createListeningSocket (addrs[i], addr_lens[i], section);
1162   GNUNET_free (addrs);
1163   GNUNET_free (addr_lens);
1164 }
1165
1166
1167 /**
1168  * Entry point to the Service Manager
1169  *
1170  * @param configurationHandle configuration to use to get services
1171  */
1172 void
1173 prepareServices (const struct GNUNET_CONFIGURATION_Handle *configurationHandle)
1174 {
1175   char *defaultServicesString;
1176
1177   cfg = configurationHandle;
1178   /* Split the default services into a list */
1179   if (GNUNET_OK ==
1180       GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
1181                                              &defaultServicesString))
1182   {
1183     addDefaultServicesToList (defaultServicesString);
1184     GNUNET_free (defaultServicesString);
1185   }
1186   /* Spot the services from the configuration and create a listening
1187    * socket for each */
1188   GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
1189 }
1190
1191 /* end of gnunet-service-arm_interceptor.c */