123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191 |
- /*
- This file is part of GNUnet.
- (C) 2009, 2010 Christian Grothoff (and other contributing authors)
- GNUnet is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 3, or (at your
- option) any later version.
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
- */
- /**
- * @file arm/gnunet-service-arm_interceptor.c
- * @brief listen to incoming connections from clients to services,
- * start services for which incoming an incoming connection occur,
- * and relay communication between the client and the service for
- * that first incoming connection.
- *
- * @author Safey Abdel Halim
- * @author Christian Grothoff
- */
- #include "platform.h"
- #include "gnunet_service_lib.h"
- #include "gnunet_configuration_lib.h"
- #include "gnunet_constants.h"
- #include "gnunet_client_lib.h"
- #include "gnunet_container_lib.h"
- #include "gnunet-service-arm.h"
- #define DEBUG_SERVICE_MANAGER GNUNET_NO
- #define BUFFER_SIZE (64 * 1024)
- /**
- * Problem forwarding from client to service.
- */
- #define REASON_CLIENT_TO_SERVICE 1
- /**
- * Problem forwarding from service to client.
- */
- #define REASON_SERVICE_TO_CLIENT 2
- /**
- * Problem in both directions.
- */
- #define REASON_ERROR 3
- struct ForwardedConnection;
- /**
- *
- */
- struct ServiceListeningInfo
- {
- /**
- * This is a linked list.
- */
- struct ServiceListeningInfo *next;
- /**
- * This is a linked list.
- */
- struct ServiceListeningInfo *prev;
- /**
- * Name of the service being forwarded.
- */
- char *serviceName;
- /**
- *
- */
- struct sockaddr *service_addr;
- /**
- *
- */
- socklen_t service_addr_len;
- /**
- * Our listening socket.
- */
- struct GNUNET_NETWORK_Handle *listeningSocket;
- /**
- *
- */
- struct ForwardedConnection *fc;
- /**
- * Task doing the accepting.
- */
- GNUNET_SCHEDULER_TaskIdentifier acceptTask;
- };
- /**
- * Information of the connection: client-arm-service
- */
- struct ForwardedConnection
- {
- /**
- *
- */
- struct GNUNET_NETWORK_Handle *armClientSocket;
- /**
- *
- */
- struct GNUNET_NETWORK_Handle *armServiceSocket;
- /**
- *
- */
- struct ServiceListeningInfo *listen_info;
- /**
- *
- */
- char service_to_client_buffer[BUFFER_SIZE];
- /**
- *
- */
- char client_to_service_buffer[BUFFER_SIZE];
- /**
- *
- */
- char client_addr[32];
- /**
- *
- */
- const char *client_to_service_bufferPos;
- /**
- *
- */
- const char *service_to_client_bufferPos;
- /**
- * Timeout for forwarding.
- */
- struct GNUNET_TIME_Absolute timeout;
- /**
- * Current back-off value.
- */
- struct GNUNET_TIME_Relative back_off;
- /**
- * Task that tries to initiate forwarding.
- */
- GNUNET_SCHEDULER_TaskIdentifier start_task;
- /**
- *
- */
- GNUNET_SCHEDULER_TaskIdentifier client_to_service_task;
- /**
- *
- */
- GNUNET_SCHEDULER_TaskIdentifier service_to_client_task;
- /**
- *
- */
- ssize_t client_to_service_bufferDataLength;
- /**
- *
- */
- ssize_t service_to_client_bufferDataLength;
- /**
- *
- */
- socklen_t client_addr_len;
- /**
- * Have we ever successfully written data to the service?
- */
- int first_write_done;
- /**
- * Reference count (the structure is freed when it reaches zero)
- */
- int reference_count;
- };
- /**
- * Array with the names of the services started by default.
- */
- static char **defaultServicesList;
- /**
- * Size of the defaultServicesList array.
- */
- static unsigned int numDefaultServices;
- /**
- *
- */
- static const struct GNUNET_CONFIGURATION_Handle *cfg;
- /**
- *
- */
- static struct ServiceListeningInfo *serviceListeningInfoList_head;
- /**
- *
- */
- static struct ServiceListeningInfo *serviceListeningInfoList_tail;
- /**
- * Put the default services represented by a space separated string into an array of strings
- *
- * @param services space separated string of default services
- */
- static void
- addDefaultServicesToList (const char *services)
- {
- unsigned int i;
- const char *token;
- char *s;
- if (strlen (services) == 0)
- return;
- s = GNUNET_strdup (services);
- token = strtok (s, " ");
- while (NULL != token)
- {
- numDefaultServices++;
- token = strtok (NULL, " ");
- }
- GNUNET_free (s);
- defaultServicesList = GNUNET_malloc (numDefaultServices * sizeof (char *));
- i = 0;
- s = GNUNET_strdup (services);
- token = strtok (s, " ");
- while (NULL != token)
- {
- defaultServicesList[i++] = GNUNET_strdup (token);
- token = strtok (NULL, " ");
- }
- GNUNET_free (s);
- GNUNET_assert (i == numDefaultServices);
- }
- /**
- * Checks whether the serviceName is in the list of default services
- *
- * @param serviceName string to check its existance in the list
- * @return GNUNET_YES if the service is started by default
- */
- static int
- isInDefaultList (const char *serviceName)
- {
- unsigned int i;
- for (i = 0; i < numDefaultServices; i++)
- if (strcmp (serviceName, defaultServicesList[i]) == 0)
- return GNUNET_YES;
- return GNUNET_NO;
- }
- /**
- * Close forwarded connection (partial or full).
- *
- * @param fc connection to close
- * @param reason which direction to close
- */
- static void
- closeClientAndServiceSockets (struct ForwardedConnection *fc, int reason)
- {
- if (0 != (REASON_SERVICE_TO_CLIENT & reason))
- {
- #if DEBUG_SERVICE_MANAGER
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Stopping forwarding from service to client\n");
- #endif
- if (fc->armClientSocket != NULL)
- GNUNET_NETWORK_socket_shutdown (fc->armClientSocket, SHUT_WR);
- if (fc->armServiceSocket != NULL)
- GNUNET_NETWORK_socket_shutdown (fc->armServiceSocket, SHUT_RD);
- }
- if (0 != (REASON_CLIENT_TO_SERVICE & reason))
- {
- #if DEBUG_SERVICE_MANAGER
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Stopping forwarding from client to service\n");
- #endif
- if (fc->armClientSocket != NULL)
- GNUNET_NETWORK_socket_shutdown (fc->armClientSocket, SHUT_RD);
- if (fc->armServiceSocket != NULL)
- GNUNET_NETWORK_socket_shutdown (fc->armServiceSocket, SHUT_WR);
- }
- #if DEBUG_SERVICE_MANAGER
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Closing forwarding connection (done with both directions)\n");
- #endif
- fc->reference_count -= 1;
- if (fc->reference_count <= 0)
- {
- if ((NULL != fc->armClientSocket) &&
- (GNUNET_SYSERR == GNUNET_NETWORK_socket_close (fc->armClientSocket)))
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
- fc->armClientSocket = NULL;
- }
- if ((NULL != fc->armServiceSocket) &&
- (GNUNET_SYSERR == GNUNET_NETWORK_socket_close (fc->armServiceSocket)))
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close");
- fc->armServiceSocket = NULL;
- }
- if (fc->listen_info != NULL)
- {
- if (fc->listen_info->serviceName != NULL)
- {
- GNUNET_free (fc->listen_info->serviceName);
- fc->listen_info->serviceName = NULL;
- }
- if (fc->listen_info->service_addr != NULL)
- {
- GNUNET_free (fc->listen_info->service_addr);
- fc->listen_info->service_addr = NULL;
- }
- GNUNET_free (fc->listen_info);
- fc->listen_info = NULL;
- }
- GNUNET_free (fc);
- }
- }
- /**
- * Read data from the client and then forward it to the service.
- *
- * @param cls callback data, struct ForwardedConnection for the communication between client and service
- * @param tc context
- */
- static void
- receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
- /**
- * Receive service messages sent by the service and forward it to client
- *
- * @param cls callback data, struct ForwardedConnection for the communication between client and service
- * @param tc scheduler context
- */
- static void
- receiveFromService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
- /**
- *
- */
- static void
- start_forwarding (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
- /**
- * Forward messages sent from service to client
- *
- * @param cls callback data, struct ForwardedConnection for the communication between client and service
- * @param tc context
- */
- static void
- forwardToClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
- {
- struct ForwardedConnection *fc = cls;
- ssize_t numberOfBytesSent;
- fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
- if (GNUNET_YES !=
- GNUNET_NETWORK_fdset_isset (tc->write_ready, fc->armClientSocket))
- {
- fc->service_to_client_task =
- GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armClientSocket, &forwardToClient,
- fc);
- return;
- }
- /* Forwarding service response to client */
- numberOfBytesSent =
- GNUNET_NETWORK_socket_send (fc->armClientSocket,
- fc->service_to_client_bufferPos,
- fc->service_to_client_bufferDataLength);
- if (numberOfBytesSent <= 0)
- {
- if ((errno != EPIPE) && (errno != ECONNRESET))
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to forward %u bytes of data to client: %s\n",
- fc->service_to_client_bufferDataLength, STRERROR (errno));
- closeClientAndServiceSockets (fc, REASON_SERVICE_TO_CLIENT);
- return;
- }
- #if DEBUG_SERVICE_MANAGER
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Forwarded %d bytes to client\n",
- numberOfBytesSent);
- #endif
- if (numberOfBytesSent < fc->service_to_client_bufferDataLength)
- {
- fc->service_to_client_bufferPos += numberOfBytesSent;
- fc->service_to_client_bufferDataLength -= numberOfBytesSent;
- fc->service_to_client_task =
- GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armClientSocket, &forwardToClient,
- fc);
- return;
- }
- fc->service_to_client_task =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armServiceSocket, &receiveFromService,
- fc);
- }
- /**
- * Receive service messages sent by the service and forward it to client
- *
- * @param cls callback data, struct ForwardedConnection for the communication between client and service
- * @param tc scheduler context
- */
- static void
- receiveFromService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
- {
- struct ForwardedConnection *fc = cls;
- struct GNUNET_TIME_Relative rem;
- fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
- if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
- (fc->first_write_done != GNUNET_YES))
- {
- closeClientAndServiceSockets (fc, REASON_ERROR);
- return;
- }
- if (GNUNET_YES !=
- GNUNET_NETWORK_fdset_isset (tc->read_ready, fc->armServiceSocket))
- {
- fc->service_to_client_task =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armServiceSocket,
- &receiveFromService, fc);
- return;
- }
- fc->service_to_client_bufferPos = fc->service_to_client_buffer;
- fc->service_to_client_bufferDataLength =
- GNUNET_NETWORK_socket_recv (fc->armServiceSocket,
- fc->service_to_client_buffer, BUFFER_SIZE);
- if (fc->service_to_client_bufferDataLength <= 0)
- {
- #if DEBUG_SERVICE_MANAGER
- if (fc->service_to_client_bufferDataLength == 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Service `%s' stopped sending data.\n",
- fc->listen_info->serviceName);
- }
- #endif
- if (fc->first_write_done != GNUNET_YES)
- {
- fc->service_to_client_bufferDataLength = 0;
- GNUNET_break (GNUNET_OK ==
- GNUNET_NETWORK_socket_close (fc->armServiceSocket));
- fc->armServiceSocket = NULL;
- if ((fc->client_to_service_bufferDataLength > 0) &&
- (fc->client_to_service_task != GNUNET_SCHEDULER_NO_TASK))
- {
- GNUNET_SCHEDULER_cancel (fc->client_to_service_task);
- fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
- }
- fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
- #if DEBUG_SERVICE_MANAGER
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Failed to connected to service `%s' at `%s', will try again in %llu ms\n",
- fc->listen_info->serviceName,
- GNUNET_a2s (fc->listen_info->service_addr,
- fc->listen_info->service_addr_len),
- (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
- rem).rel_value);
- #endif
- rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
- GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
- fc->start_task =
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_min
- (fc->back_off, rem), &start_forwarding,
- fc);
- }
- else
- {
- #if DEBUG_SERVICE_MANAGER
- if (fc->service_to_client_bufferDataLength != 0)
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Error receiving from service: %s\n", STRERROR (errno));
- #endif
- closeClientAndServiceSockets (fc, REASON_SERVICE_TO_CLIENT);
- }
- return;
- }
- fc->first_write_done = GNUNET_YES;
- #if DEBUG_SERVICE_MANAGER
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received %d bytes for client\n",
- fc->service_to_client_bufferDataLength);
- #endif
- fc->service_to_client_task =
- GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armClientSocket, &forwardToClient,
- fc);
- }
- /**
- * Forward client message to service
- *
- * @param cls callback data, struct ForwardedConnection for the communication between client and service
- * @param tc scheduler context
- */
- static void
- forwardToService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
- {
- struct ForwardedConnection *fc = cls;
- ssize_t numberOfBytesSent;
- struct GNUNET_TIME_Relative rem;
- fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
- if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
- (fc->first_write_done != GNUNET_YES))
- {
- closeClientAndServiceSockets (fc, REASON_ERROR);
- return;
- }
- if (GNUNET_YES !=
- GNUNET_NETWORK_fdset_isset (tc->write_ready, fc->armServiceSocket))
- {
- fc->client_to_service_task =
- GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armServiceSocket, &forwardToService,
- fc);
- return;
- }
- numberOfBytesSent =
- GNUNET_NETWORK_socket_send (fc->armServiceSocket,
- fc->client_to_service_bufferPos,
- fc->client_to_service_bufferDataLength);
- if (numberOfBytesSent <= 0)
- {
- if (GNUNET_YES != fc->first_write_done)
- {
- GNUNET_break (GNUNET_OK ==
- GNUNET_NETWORK_socket_close (fc->armServiceSocket));
- fc->armServiceSocket = NULL;
- if ((fc->service_to_client_bufferDataLength == 0) &&
- (fc->service_to_client_task != GNUNET_SCHEDULER_NO_TASK))
- {
- GNUNET_SCHEDULER_cancel (fc->service_to_client_task);
- fc->service_to_client_task = GNUNET_SCHEDULER_NO_TASK;
- }
- fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2);
- #if DEBUG_SERVICE_MANAGER
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Failed to connect to service `%s' at `%s', will try again in %llu ms\n",
- fc->listen_info->serviceName,
- GNUNET_a2s (fc->listen_info->service_addr,
- fc->listen_info->service_addr_len),
- (unsigned long long) GNUNET_TIME_relative_min (fc->back_off,
- rem).rel_value);
- #endif
- rem = GNUNET_TIME_absolute_get_remaining (fc->timeout);
- GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
- fc->start_task =
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_min
- (fc->back_off, rem), &start_forwarding,
- fc);
- }
- else
- {
- if ((errno != EPIPE) && (errno != ECONNRESET))
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to forward data to service: %s\n",
- STRERROR (errno));
- closeClientAndServiceSockets (fc, REASON_CLIENT_TO_SERVICE);
- }
- return;
- }
- #if DEBUG_SERVICE_MANAGER
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Forwarded %d bytes to service\n",
- numberOfBytesSent);
- #endif
- fc->first_write_done = GNUNET_YES;
- if (numberOfBytesSent < fc->client_to_service_bufferDataLength)
- {
- fc->client_to_service_bufferPos += numberOfBytesSent;
- fc->client_to_service_bufferDataLength -= numberOfBytesSent;
- fc->client_to_service_task =
- GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armServiceSocket, &forwardToService,
- fc);
- return;
- }
- fc->client_to_service_task =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armClientSocket, &receiveFromClient,
- fc);
- }
- /**
- * Read data from the client and then forward it to the service.
- *
- * @param cls callback data, struct ForwardedConnection for the communication between client and service
- * @param tc context
- */
- static void
- receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
- {
- struct ForwardedConnection *fc = cls;
- fc->client_to_service_task = GNUNET_SCHEDULER_NO_TASK;
- if (GNUNET_YES !=
- GNUNET_NETWORK_fdset_isset (tc->read_ready, fc->armClientSocket))
- {
- fc->client_to_service_task =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armClientSocket, &receiveFromClient,
- fc);
- return;
- }
- fc->client_to_service_bufferPos = fc->client_to_service_buffer;
- fc->client_to_service_bufferDataLength =
- GNUNET_NETWORK_socket_recv (fc->armClientSocket,
- fc->client_to_service_buffer, BUFFER_SIZE);
- if (fc->client_to_service_bufferDataLength <= 0)
- {
- if (fc->client_to_service_bufferDataLength == 0)
- {
- #if DEBUG_SERVICE_MANAGER
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Client closed connection with service `%s'\n",
- fc->listen_info->serviceName);
- #endif
- }
- else
- {
- #if DEBUG_SERVICE_MANAGER
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error receiving from client: %s\n",
- STRERROR (errno));
- #endif
- }
- closeClientAndServiceSockets (fc, REASON_CLIENT_TO_SERVICE);
- return;
- }
- #if DEBUG_SERVICE_MANAGER
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received %d bytes for service\n",
- fc->client_to_service_bufferDataLength);
- #endif
- if (fc->armServiceSocket != NULL)
- fc->client_to_service_task =
- GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armServiceSocket, &forwardToService,
- fc);
- else
- /* We have not added any task with fc as a closure, so we're
- * dropping our reference to fc
- */
- fc->reference_count -= 1;
- }
- static void
- fc_acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
- {
- struct ServiceListeningInfo *sli = cls;
- struct ForwardedConnection *fc = sli->fc;
- if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))
- {
- GNUNET_assert (GNUNET_OK ==
- GNUNET_NETWORK_socket_close (sli->listeningSocket));
- closeClientAndServiceSockets (fc, REASON_ERROR);
- GNUNET_free (sli);
- return;
- }
- #if DEBUG_SERVICE_MANAGER
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Connected to service, now starting forwarding\n");
- #endif
- fc->armServiceSocket = sli->listeningSocket;
- GNUNET_free (fc->listen_info->service_addr);
- fc->listen_info->service_addr = sli->service_addr;
- fc->listen_info->service_addr_len = sli->service_addr_len;
- /* Drop fc reference count prematurely, it'll be incremented
- * once or twice in the following conditional branches.
- * This is, apparently, the place where reference count increases
- * past 1.
- */
- fc->reference_count -= 1;
- if (fc->client_to_service_task == GNUNET_SCHEDULER_NO_TASK)
- {
- if (fc->client_to_service_bufferDataLength == 0)
- fc->client_to_service_task =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armClientSocket,
- &receiveFromClient, fc);
- else
- fc->client_to_service_task =
- GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armServiceSocket,
- &forwardToService, fc);
- fc->reference_count += 1;
- }
- if (fc->service_to_client_task == GNUNET_SCHEDULER_NO_TASK)
- {
- if (fc->service_to_client_bufferDataLength == 0)
- fc->service_to_client_task =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armServiceSocket,
- &receiveFromService, fc);
- else
- fc->service_to_client_task =
- GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armClientSocket, &forwardToClient,
- fc);
- fc->reference_count += 1;
- }
- GNUNET_free (sli);
- }
- static struct ServiceListeningInfo *
- service_try_to_connect (const struct sockaddr *addr, int pf, socklen_t addrlen,
- struct ForwardedConnection *fc)
- {
- struct GNUNET_NETWORK_Handle *sock;
- struct ServiceListeningInfo *serviceListeningInfo;
- sock = GNUNET_NETWORK_socket_create (pf, SOCK_STREAM, 0);
- if (sock == NULL)
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket");
- return NULL;
- }
- if ((GNUNET_SYSERR == GNUNET_NETWORK_socket_connect (sock, addr, addrlen)) &&
- (errno != EINPROGRESS))
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
- GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
- return NULL;
- }
- serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
- serviceListeningInfo->serviceName = NULL;
- serviceListeningInfo->service_addr = GNUNET_malloc (addrlen);
- memcpy (serviceListeningInfo->service_addr, addr, addrlen);
- serviceListeningInfo->service_addr_len = addrlen;
- serviceListeningInfo->listeningSocket = sock;
- serviceListeningInfo->fc = fc;
- serviceListeningInfo->acceptTask =
- GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
- serviceListeningInfo->listeningSocket,
- &fc_acceptConnection,
- serviceListeningInfo);
- return serviceListeningInfo;
- }
- /**
- *
- */
- static void
- start_forwarding (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
- {
- struct ForwardedConnection *fc = cls;
- struct ServiceListeningInfo *sc;
- struct sockaddr_in target_ipv4;
- struct sockaddr_in6 target_ipv6;
- const struct sockaddr_in *v4;
- const struct sockaddr_in6 *v6;
- char listen_address[INET6_ADDRSTRLEN];
- fc->start_task = GNUNET_SCHEDULER_NO_TASK;
- if ((NULL != tc) && (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("Unable to forward to service `%s': shutdown\n"),
- fc->listen_info->serviceName);
- closeClientAndServiceSockets (fc, REASON_ERROR);
- return;
- }
- if (0 == GNUNET_TIME_absolute_get_remaining (fc->timeout).rel_value)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _
- ("Unable to forward to service `%s': timeout before connect\n"),
- fc->listen_info->serviceName);
- closeClientAndServiceSockets (fc, REASON_ERROR);
- return;
- }
- switch (fc->listen_info->service_addr->sa_family)
- {
- case AF_UNSPEC:
- GNUNET_break (0);
- closeClientAndServiceSockets (fc, REASON_ERROR);
- return;
- case AF_INET:
- v4 = (const struct sockaddr_in *) fc->listen_info->service_addr;
- inet_ntop (fc->listen_info->service_addr->sa_family,
- (const void *) &v4->sin_addr, listen_address, INET_ADDRSTRLEN);
- if (0 == strncmp (listen_address, "0.0.0.0", 7))
- {
- /* connect to [::1] and 127.0.0.1 instead of [::] and 0.0.0.0 */
- memset (&target_ipv4, 0, sizeof (target_ipv4));
- GNUNET_assert (1 ==
- inet_pton (AF_INET, "127.0.0.1", &target_ipv4.sin_addr));
- target_ipv4.sin_family = AF_INET;
- target_ipv4.sin_port = v4->sin_port;
- v4 = &target_ipv4;
- }
- sc = service_try_to_connect ((const struct sockaddr *) v4, PF_INET,
- sizeof (struct sockaddr_in), fc);
- break;
- case AF_INET6:
- v6 = (struct sockaddr_in6 *) fc->listen_info->service_addr;
- inet_ntop (fc->listen_info->service_addr->sa_family,
- (const void *) &v6->sin6_addr, listen_address, INET6_ADDRSTRLEN);
- if ((strncmp (listen_address, "[::]:", 5) == 0) ||
- (strncmp (listen_address, "::", 2) == 0))
- {
- memset (&target_ipv6, 0, sizeof (target_ipv6));
- target_ipv6.sin6_addr = in6addr_loopback;
- target_ipv6.sin6_family = AF_INET6;
- target_ipv6.sin6_port = v6->sin6_port;
- v6 = &target_ipv6;
- }
- sc = service_try_to_connect ((const struct sockaddr *) v6, PF_INET6,
- sizeof (struct sockaddr_in6), fc);
- break;
- case AF_UNIX:
- sc = service_try_to_connect (fc->listen_info->service_addr, PF_UNIX,
- fc->listen_info->service_addr_len, fc);
- break;
- default:
- GNUNET_break (0);
- closeClientAndServiceSockets (fc, REASON_ERROR);
- return;
- }
- if (NULL == sc)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Unable to start service `%s': %s\n"),
- fc->listen_info->serviceName, STRERROR (errno));
- closeClientAndServiceSockets (fc, REASON_ERROR);
- return;
- }
- }
- /**
- *
- */
- int
- stop_listening (const char *serviceName)
- {
- struct ServiceListeningInfo *pos;
- struct ServiceListeningInfo *next;
- int ret;
- ret = GNUNET_NO;
- next = serviceListeningInfoList_head;
- while (NULL != (pos = next))
- {
- next = pos->next;
- if ((serviceName != NULL) && (strcmp (pos->serviceName, serviceName) != 0))
- continue;
- if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK)
- GNUNET_SCHEDULER_cancel (pos->acceptTask);
- GNUNET_break (GNUNET_OK ==
- GNUNET_NETWORK_socket_close (pos->listeningSocket));
- GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
- serviceListeningInfoList_tail, pos);
- GNUNET_free (pos->serviceName);
- GNUNET_free (pos->service_addr);
- GNUNET_free (pos);
- ret = GNUNET_OK;
- }
- return ret;
- }
- /**
- * First connection has come to the listening socket associated with the service,
- * create the service in order to relay the incoming connection to it
- *
- * @param cls callback data, struct ServiceListeningInfo describing a listen socket
- * @param tc context
- */
- static void
- acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
- static void
- accept_and_forward (struct ServiceListeningInfo *serviceListeningInfo)
- {
- struct ForwardedConnection *fc;
- fc = GNUNET_malloc (sizeof (struct ForwardedConnection));
- fc->reference_count = 1;
- fc->listen_info = serviceListeningInfo;
- fc->service_to_client_bufferPos = fc->service_to_client_buffer;
- fc->client_to_service_bufferPos = fc->client_to_service_buffer;
- fc->client_addr_len = sizeof (fc->client_addr);
- fc->armClientSocket =
- GNUNET_NETWORK_socket_accept (serviceListeningInfo->listeningSocket,
- (struct sockaddr *) fc->client_addr,
- &fc->client_addr_len);
- if (NULL == fc->armClientSocket)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Unable to accept connection for service `%s': %s\n"),
- serviceListeningInfo->serviceName, STRERROR (errno));
- GNUNET_free (fc);
- GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
- serviceListeningInfoList_tail,
- serviceListeningInfo);
- serviceListeningInfo->acceptTask =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- serviceListeningInfo->listeningSocket,
- &acceptConnection, serviceListeningInfo);
- return;
- }
- GNUNET_break (GNUNET_OK ==
- GNUNET_NETWORK_socket_close
- (serviceListeningInfo->listeningSocket));
- start_service (NULL, serviceListeningInfo->serviceName, NULL);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Service `%s' started\n"),
- fc->listen_info->serviceName);
- fc->timeout =
- GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_SERVICE_TIMEOUT);
- fc->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
- fc->client_to_service_task =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- fc->armClientSocket, &receiveFromClient,
- fc);
- GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task);
- /* We're creating another chain of tasks for this fc that
- * will have its own reference to it.
- */
- fc->reference_count += 1;
- fc->start_task = GNUNET_SCHEDULER_add_now (&start_forwarding, fc);
- }
- /**
- * First connection has come to the listening socket associated with the service,
- * create the service in order to relay the incoming connection to it
- *
- * @param cls callback data, struct ServiceListeningInfo describing a listen socket
- * @param tc context
- */
- static void
- acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
- {
- struct ServiceListeningInfo *sli = cls;
- struct ServiceListeningInfo *pos;
- struct ServiceListeningInfo *next;
- int *lsocks;
- unsigned int ls;
- int use_lsocks;
- sli->acceptTask = GNUNET_SCHEDULER_NO_TASK;
- if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
- return;
- GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
- serviceListeningInfoList_tail, sli);
- #ifndef MINGW
- use_lsocks = GNUNET_NO;
- if (GNUNET_YES ==
- GNUNET_CONFIGURATION_have_value (cfg, sli->serviceName,
- "DISABLE_SOCKET_FORWARDING"))
- use_lsocks =
- GNUNET_CONFIGURATION_get_value_yesno (cfg, sli->serviceName,
- "DISABLE_SOCKET_FORWARDING");
- #else
- use_lsocks = GNUNET_YES;
- #endif
- if (GNUNET_NO != use_lsocks)
- {
- accept_and_forward (sli);
- return;
- }
- lsocks = NULL;
- ls = 0;
- next = serviceListeningInfoList_head;
- while (NULL != (pos = next))
- {
- next = pos->next;
- if (0 == strcmp (pos->serviceName, sli->serviceName))
- {
- GNUNET_array_append (lsocks, ls,
- GNUNET_NETWORK_get_fd (pos->listeningSocket));
- GNUNET_free (pos->listeningSocket); /* deliberately no closing! */
- GNUNET_free (pos->service_addr);
- GNUNET_free (pos->serviceName);
- GNUNET_SCHEDULER_cancel (pos->acceptTask);
- GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
- serviceListeningInfoList_tail, pos);
- GNUNET_free (pos);
- }
- }
- GNUNET_array_append (lsocks, ls,
- GNUNET_NETWORK_get_fd (sli->listeningSocket));
- GNUNET_free (sli->listeningSocket); /* deliberately no closing! */
- GNUNET_free (sli->service_addr);
- GNUNET_array_append (lsocks, ls, -1);
- start_service (NULL, sli->serviceName, lsocks);
- ls = 0;
- while (lsocks[ls] != -1)
- GNUNET_break (0 == close (lsocks[ls++]));
- GNUNET_array_grow (lsocks, ls, 0);
- GNUNET_free (sli->serviceName);
- GNUNET_free (sli);
- }
- /**
- * Creating a listening socket for each of the service's addresses and
- * wait for the first incoming connection to it
- *
- * @param sa address associated with the service
- * @param addr_len length of sa
- * @param serviceName the name of the service in question
- */
- static void
- createListeningSocket (struct sockaddr *sa, socklen_t addr_len,
- const char *serviceName)
- {
- const static int on = 1;
- struct GNUNET_NETWORK_Handle *sock;
- struct ServiceListeningInfo *serviceListeningInfo;
- switch (sa->sa_family)
- {
- case AF_INET:
- sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
- break;
- case AF_INET6:
- sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
- break;
- case AF_UNIX:
- if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0) /* Do not bind to blank UNIX path! */
- return;
- sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
- break;
- default:
- GNUNET_break (0);
- sock = NULL;
- errno = EAFNOSUPPORT;
- break;
- }
- if (NULL == sock)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Unable to create socket for service `%s': %s\n"),
- serviceName, STRERROR (errno));
- GNUNET_free (sa);
- return;
- }
- if (GNUNET_NETWORK_socket_setsockopt
- (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
- "setsockopt");
- #ifdef IPV6_V6ONLY
- if ((sa->sa_family == AF_INET6) &&
- (GNUNET_NETWORK_socket_setsockopt
- (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
- "setsockopt");
- #endif
- if (GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) sa, addr_len)
- != GNUNET_OK)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- _
- ("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
- serviceName, GNUNET_a2s (sa, addr_len), STRERROR (errno));
- GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
- GNUNET_free (sa);
- return;
- }
- if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
- GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
- GNUNET_free (sa);
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("ARM now monitors connections to service `%s' at `%s'\n"),
- serviceName, GNUNET_a2s (sa, addr_len));
- serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
- serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
- serviceListeningInfo->service_addr = sa;
- serviceListeningInfo->service_addr_len = addr_len;
- serviceListeningInfo->listeningSocket = sock;
- serviceListeningInfo->acceptTask =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock,
- &acceptConnection, serviceListeningInfo);
- GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
- serviceListeningInfoList_tail,
- serviceListeningInfo);
- }
- /**
- * Callback function, checks whether the current tokens are representing a service,
- * gets its addresses and create listening socket for it.
- *
- * @param cls callback data, not used
- * @param section configuration section
- * @param option configuration option
- * @param value the option's value
- */
- static void
- checkPortNumberCB (void *cls, const char *section, const char *option,
- const char *value)
- {
- struct sockaddr **addrs;
- socklen_t *addr_lens;
- int ret;
- unsigned int i;
- if ((strcasecmp (section, "arm") == 0) ||
- (strcasecmp (option, "AUTOSTART") != 0) ||
- (strcasecmp (value, "YES") != 0) ||
- (isInDefaultList (section) == GNUNET_YES))
- return;
- if (0 >=
- (ret =
- GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs, &addr_lens)))
- return;
- /* this will free (or capture) addrs[i] */
- for (i = 0; i < ret; i++)
- createListeningSocket (addrs[i], addr_lens[i], section);
- GNUNET_free (addrs);
- GNUNET_free (addr_lens);
- }
- /**
- * Entry point to the Service Manager
- *
- * @param configurationHandle configuration to use to get services
- */
- void
- prepareServices (const struct GNUNET_CONFIGURATION_Handle *configurationHandle)
- {
- char *defaultServicesString;
- cfg = configurationHandle;
- /* Split the default services into a list */
- if (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
- &defaultServicesString))
- {
- addDefaultServicesToList (defaultServicesString);
- GNUNET_free (defaultServicesString);
- }
- /* Spot the services from the configuration and create a listening
- * socket for each */
- GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
- }
- /* end of gnunet-service-arm_interceptor.c */
|