X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Futil%2Fservice.c;h=0594149d96e79798697daecf13daaabf57fece8f;hb=48718834d4fb6c411ff5b00b86662a3dee3ac6cc;hp=8803490739b6b22bb0cae1e6aa21dc4c9624724d;hpb=baac4b5251625c616da4b8369fb1ac2581d48076;p=oweals%2Fgnunet.git diff --git a/src/util/service.c b/src/util/service.c index 880349073..0594149d9 100644 --- a/src/util/service.c +++ b/src/util/service.c @@ -35,6 +35,8 @@ #include "gnunet_server_lib.h" #include "gnunet_service_lib.h" +#define DEBUG_SERVICE GNUNET_NO + /* ******************* access control ******************** */ /** @@ -333,7 +335,7 @@ parse_ipv6_specification (const char *routeListX) _("Wrong format `%s' for network\n"), &routeList[slash + 1]); else - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "inet_pton"); + GNUNET_log_strerror(GNUNET_ERROR_TYPE_ERROR, "inet_pton"); GNUNET_free (result); GNUNET_free (routeList); return NULL; @@ -428,14 +430,10 @@ struct GNUNET_SERVICE_Context struct GNUNET_SERVER_Handle *server; /** - * Scheduler for the server. + * NULL-terminated array of addresses to bind to, NULL if we got pre-bound + * listen sockets. */ - struct GNUNET_SCHEDULER_Handle *sched; - - /** - * Address to bind to. - */ - struct sockaddr *addr; + struct sockaddr **addrs; /** * Name of our service. @@ -481,14 +479,19 @@ struct GNUNET_SERVICE_Context struct GNUNET_SERVER_MessageHandler *my_handlers; /** - * Idle timeout for server. + * Array of the lengths of the entries in addrs. */ - struct GNUNET_TIME_Relative timeout; + socklen_t * addrlens; + + /** + * NULL-terminated array of listen sockets we should take over. + */ + struct GNUNET_NETWORK_Handle **lsocks; /** - * Maximum buffer size for the server. + * Idle timeout for server. */ - size_t maxbuf; + struct GNUNET_TIME_Relative timeout; /** * Overall success/failure of the service start. @@ -509,14 +512,21 @@ struct GNUNET_SERVICE_Context int require_found; /** - * Can clients ask us to initiate a shutdown? + * Do we require a matching UID for UNIX domain socket + * connections? + */ + int match_uid; + + /** + * Do we require a matching GID for UNIX domain socket + * connections? */ - int allow_shutdown; + int match_gid; /** - * Length of addr. + * Our options. */ - socklen_t addrlen; + enum GNUNET_SERVICE_Options options; }; @@ -563,35 +573,6 @@ handle_test (void *cls, } -/** - * Handler for SHUTDOWN message. - * - * @param cls closure (refers to service) - * @param client identification of the client - * @param message the actual message - */ -static void -handle_shutdown (void *cls, - struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - struct GNUNET_SERVICE_Context *service = cls; - if (!service->allow_shutdown) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ - ("Received shutdown request, but configured to ignore!\n")); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Initiating shutdown as requested by client.\n")); - GNUNET_assert (service->sched != NULL); - GNUNET_SCHEDULER_shutdown (service->sched); - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - /** * Default handlers for all services. Will be copied and the * "callback_cls" fields will be replaced with the specific service @@ -600,8 +581,6 @@ handle_shutdown (void *cls, static const struct GNUNET_SERVER_MessageHandler defhandlers[] = { {&handle_test, NULL, GNUNET_MESSAGE_TYPE_TEST, sizeof (struct GNUNET_MessageHeader)}, - {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_SHUTDOWN, - sizeof (struct GNUNET_MessageHeader)}, {NULL, NULL, 0, 0} }; @@ -612,9 +591,18 @@ static const struct GNUNET_SERVER_MessageHandler defhandlers[] = { /** * Check if access to the service is allowed from the given address. + * + * @param cls closure + * @param uc credentials, if available, otherwise NULL + * @param addr address + * @param addrlen length of address + * @return GNUNET_YES to allow, GNUNET_NO to deny, GNUNET_SYSERR + * for unknown address family (will be denied). */ static int -check_access (void *cls, const struct sockaddr *addr, socklen_t addrlen) +check_access (void *cls, + const struct GNUNET_CONNECTION_Credentials *uc, + const struct sockaddr *addr, socklen_t addrlen) { struct GNUNET_SERVICE_Context *sctx = cls; const struct sockaddr_in *i4; @@ -641,6 +629,25 @@ check_access (void *cls, const struct sockaddr *addr, socklen_t addrlen) && ((sctx->v6_denied == NULL) || (!check_ipv6_listed (sctx->v6_denied, &i6->sin6_addr))); break; + case AF_UNIX: + ret = GNUNET_OK; /* always OK for now */ + if ( (sctx->match_uid == GNUNET_YES) || + (sctx->match_gid == GNUNET_YES) ) + ret = GNUNET_NO; + if ( (uc != NULL) && + ( (sctx->match_uid != GNUNET_YES) || + (uc->uid == geteuid()) || + (uc->uid == getuid()) ) && + ( (sctx->match_gid != GNUNET_YES) || + (uc->gid == getegid()) || + (uc->gid == getgid())) ) + ret = GNUNET_YES; + else + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Access denied to UID %d / GID %d\n"), + (uc == NULL) ? -1 : uc->uid, + (uc == NULL) ? -1 : uc->gid); + break; default: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Unknown address family %d\n"), addr->sa_family); @@ -650,7 +657,7 @@ check_access (void *cls, const struct sockaddr *addr, socklen_t addrlen) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Access from `%s' denied to service `%s'\n"), - GNUNET_a2s(addr, addrlen), sctx->serviceName); + GNUNET_a2s (addr, addrlen), sctx->serviceName); } return ret; } @@ -731,91 +738,108 @@ process_acl6 (struct IPv6NetworkSet **ret, return GNUNET_OK; } - /** - * Setup addr, addrlen, maxbuf, idle_timeout - * based on configuration! + * Add the given UNIX domain path as an address to the + * list (as the first entry). * - * Configuration must specify a "PORT". It may - * specify: - * - TIMEOUT (after how many ms does an inactive service timeout); - * - MAXBUF (maximum incoming message size supported) - * - DISABLEV6 (disable support for IPv6, otherwise we use dual-stack) - * - ALLOW_SHUTDOWN (allow clients to shutdown this service) - * - BINDTO (hostname or IP address to bind to, otherwise we take everything) - * - ACCEPT_FROM (only allow connections from specified IPv4 subnets) - * - ACCEPT_FROM6 (only allow connections from specified IPv6 subnets) - * - REJECT_FROM (disallow allow connections from specified IPv4 subnets) - * - REJECT_FROM6 (disallow allow connections from specified IPv6 subnets) + * @param saddrs array to update + * @param saddrlens where to store the address length + * @param unixpath path to add + */ +static void +add_unixpath (struct sockaddr **saddrs, + socklen_t *saddrlens, + const char *unixpath) +{ +#ifdef AF_UNIX + struct sockaddr_un *un; + size_t slen; + + un = GNUNET_malloc (sizeof (struct sockaddr_un)); + un->sun_family = AF_UNIX; + slen = strlen (unixpath) + 1; + if (slen >= sizeof (un->sun_path)) + slen = sizeof (un->sun_path) - 1; + memcpy (un->sun_path, + unixpath, + slen); + un->sun_path[slen] = '\0'; +#if LINUX + un->sun_path[0] = '\0'; + slen = sizeof (struct sockaddr_un); +#else + slen += sizeof (sa_family_t); +#endif + *saddrs = (struct sockaddr*) un; + *saddrlens = slen; +#else + /* this function should never be called + unless AF_UNIX is defined! */ + GNUNET_assert (0); +#endif +} + + +/** + * Get the list of addresses that a server for the given service + * should bind to. * - * @return GNUNET_OK if configuration succeeded + * @param serviceName name of the service + * @param cfg configuration (which specifies the addresses) + * @param addrs set (call by reference) to an array of pointers to the + * addresses the server should bind to and listen on; the + * array will be NULL-terminated (on success) + * @param addr_lens set (call by reference) to an array of the lengths + * of the respective 'struct sockaddr' struct in the 'addrs' + * array (on success) + * @return number of addresses found on success, + * GNUNET_SYSERR if the configuration + * did not specify reasonable finding information or + * if it specified a hostname that could not be resolved; + * GNUNET_NO if the number of addresses configured is + * zero (in this case, '*addrs' and '*addr_lens' will be + * set to NULL). */ -static int -setup_service (struct GNUNET_SERVICE_Context *sctx) +int +GNUNET_SERVICE_get_server_addresses (const char *serviceName, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct sockaddr ***addrs, + socklen_t **addr_lens) { - unsigned long long maxbuf; - struct GNUNET_TIME_Relative idleout; - char *hostname; - unsigned long long port; int disablev6; + struct GNUNET_NETWORK_Handle *desc; + unsigned long long port; + char *unixpath; struct addrinfo hints; struct addrinfo *res; struct addrinfo *pos; + struct addrinfo *next; + unsigned int i; + int resi; int ret; - int tolerant; - struct GNUNET_NETWORK_Handle *desc; - - if (GNUNET_CONFIGURATION_have_value (sctx->cfg, - sctx->serviceName, "TIMEOUT")) - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (sctx->cfg, - sctx->serviceName, - "TIMEOUT", &idleout)) - return GNUNET_SYSERR; + struct sockaddr **saddrs; + socklen_t *saddrlens; + char *hostname; - sctx->timeout = idleout; - } - else - sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; - if (GNUNET_CONFIGURATION_have_value (sctx->cfg, - sctx->serviceName, "MAXBUF")) - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (sctx->cfg, - sctx->serviceName, - "MAXBUF", &maxbuf)) - return GNUNET_SYSERR; - } - else - maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE; - if (GNUNET_CONFIGURATION_have_value (sctx->cfg, - sctx->serviceName, "DISABLEV6")) + *addrs = NULL; + *addr_lens = NULL; + desc = NULL; + if (GNUNET_CONFIGURATION_have_value (cfg, + serviceName, "DISABLEV6")) { if (GNUNET_SYSERR == - (disablev6 = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, - sctx->serviceName, + (disablev6 = GNUNET_CONFIGURATION_get_value_yesno (cfg, + serviceName, "DISABLEV6"))) return GNUNET_SYSERR; } else disablev6 = GNUNET_NO; - if (GNUNET_CONFIGURATION_have_value (sctx->cfg, - sctx->serviceName, "ALLOW_SHUTDOWN")) - { - if (GNUNET_SYSERR == - (sctx->allow_shutdown = - GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->serviceName, - "ALLOW_SHUTDOWN"))) - return GNUNET_SYSERR; - } - else - sctx->allow_shutdown = GNUNET_NO; if (!disablev6) { /* probe IPv6 support */ - desc = GNUNET_NETWORK_socket_socket (PF_INET6, SOCK_STREAM, 0); + desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); if (NULL == desc) { if ((errno == ENOBUFS) || @@ -824,60 +848,130 @@ setup_service (struct GNUNET_SERVICE_Context *sctx) GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); return GNUNET_SYSERR; } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"), - sctx->serviceName, - STRERROR (errno)); - disablev6 = GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"), + serviceName, STRERROR (errno)); + disablev6 = GNUNET_YES; } else + { + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); + desc = NULL; + } + } + + port = 0; + if (GNUNET_CONFIGURATION_have_value (cfg, + serviceName, "PORT")) + { + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_number (cfg, + serviceName, + "PORT", + &port)); + if (port > 65535) { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Require valid port number for service `%s' in configuration!\n"), + serviceName); + return GNUNET_SYSERR; } } - - - if (GNUNET_CONFIGURATION_have_value (sctx->cfg, - sctx->serviceName, "TOLERANT")) + if (GNUNET_CONFIGURATION_have_value (cfg, + serviceName, "BINDTO")) { - if (GNUNET_SYSERR == - (tolerant = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, - sctx->serviceName, - "TOLERANT"))) - return GNUNET_SYSERR; + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, + serviceName, + "BINDTO", + &hostname)); } else - tolerant = GNUNET_NO; - sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES; + hostname = NULL; +#ifdef AF_UNIX + if (GNUNET_CONFIGURATION_have_value (cfg, + serviceName, "UNIXPATH")) + { + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, + serviceName, + "UNIXPATH", + &unixpath)); + + /* probe UNIX support */ + struct sockaddr_un s_un; + if (strlen(unixpath) >= sizeof(s_un.sun_path)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("UNIXPATH `%s' too long, maximum length is %llu\n"),unixpath, sizeof(s_un.sun_path)); + GNUNET_free_non_null (hostname); + GNUNET_free (unixpath); + return GNUNET_SYSERR; + } + + desc = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0); + if (NULL == desc) + { + if ((errno == ENOBUFS) || + (errno == ENOMEM) || (errno == ENFILE) || (errno == EACCES)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); + GNUNET_free_non_null (hostname); + GNUNET_free (unixpath); + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Disabling UNIX domain socket support for service `%s', failed to create UNIX domain socket: %s\n"), + serviceName, STRERROR (errno)); + GNUNET_free (unixpath); + unixpath = NULL; + } + else + { + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); + desc = NULL; + } + } + else + unixpath = NULL; +#else + unixpath = NULL; +#endif - if ((GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (sctx->cfg, - sctx->serviceName, - "PORT", - &port)) || (port > 65535)) + if ( (port == 0) && + (unixpath == NULL) ) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ - ("Require valid port number for service `%s' in configuration!\n"), - sctx->serviceName); + _("Have neither PORT nor UNIXPATH for service `%s', but one is required\n"), + serviceName); + GNUNET_free_non_null(hostname); return GNUNET_SYSERR; } - if (GNUNET_CONFIGURATION_have_value (sctx->cfg, - sctx->serviceName, "BINDTO")) + if (port == 0) { - GNUNET_break (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (sctx->cfg, - sctx->serviceName, - "BINDTO", - &hostname)); + saddrs = GNUNET_malloc (2 * sizeof(struct sockaddr*)); + saddrlens = GNUNET_malloc (2 * sizeof (socklen_t)); + add_unixpath (saddrs, saddrlens, unixpath); + GNUNET_free_non_null (unixpath); + GNUNET_free_non_null(hostname); + *addrs = saddrs; + *addr_lens = saddrlens; + return 1; } - else - hostname = NULL; - + if (hostname != NULL) { +#if DEBUG_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Resolving `%s' since that is where `%s' will bind to.\n", + hostname, + serviceName); +#endif memset (&hints, 0, sizeof (struct addrinfo)); if (disablev6) hints.ai_family = AF_INET; @@ -888,49 +982,72 @@ setup_service (struct GNUNET_SERVICE_Context *sctx) _("Failed to resolve `%s': %s\n"), hostname, gai_strerror (ret)); GNUNET_free (hostname); + GNUNET_free (unixpath); return GNUNET_SYSERR; } - pos = res; - while ((NULL != pos) && - (((disablev6) && - (pos->ai_family != AF_INET)) || - ((pos->ai_family != AF_INET) && (pos->ai_family != AF_INET6)))) - pos = pos->ai_next; - if (pos == NULL) + next = res; + i = 0; + while (NULL != (pos = next)) + { + next = pos->ai_next; + if ( (disablev6) && (pos->ai_family == AF_INET6)) + continue; + i++; + } + if (0 == i) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to find %saddress for `%s'.\n"), - disablev6 ? "IPv4 " : "", hostname); + disablev6 ? "IPv4 " : "", hostname); freeaddrinfo (res); GNUNET_free (hostname); + GNUNET_free (unixpath); return GNUNET_SYSERR; } + resi = i; + if (NULL != unixpath) + resi++; + saddrs = GNUNET_malloc ((resi+1) * sizeof(struct sockaddr*)); + saddrlens = GNUNET_malloc ((resi+1) * sizeof (socklen_t)); + i = 0; + if (NULL != unixpath) + { + add_unixpath (saddrs, saddrlens, unixpath); + i++; + } + next = res; + while (NULL != (pos = next)) + { + next = pos->ai_next; + if ( (disablev6) && (pos->ai_family == AF_INET6)) + continue; +#if DEBUG_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Service `%s' will bind to `%s'\n", + serviceName, + GNUNET_a2s (pos->ai_addr, + pos->ai_addrlen)); +#endif + if (pos->ai_family == AF_INET) + { + GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in)); + saddrlens[i] = pos->ai_addrlen; + saddrs[i] = GNUNET_malloc (saddrlens[i]); + memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); + ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); + } + else + { + GNUNET_assert (pos->ai_family == AF_INET6); + GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in6)); + saddrlens[i] = pos->ai_addrlen; + saddrs[i] = GNUNET_malloc (saddrlens[i]); + memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); + ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); + } + i++; + } GNUNET_free (hostname); - if (pos->ai_family == AF_INET) - { - GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in)); - sctx->addrlen = pos->ai_addrlen; - sctx->addr = GNUNET_malloc (sctx->addrlen); - memcpy (sctx->addr, res->ai_addr, sctx->addrlen); - ((struct sockaddr_in *) sctx->addr)->sin_port = htons (port); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ - ("Configured to bind to %s address; %s connections to this service will fail!\n"), - "IPv4", "IPv6"); - } - else - { - GNUNET_assert (pos->ai_family == AF_INET6); - GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in6)); - sctx->addrlen = pos->ai_addrlen; - sctx->addr = GNUNET_malloc (sctx->addrlen); - memcpy (sctx->addr, res->ai_addr, sctx->addrlen); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ - ("Configured to bind to %s address; %s connections to this service will fail!\n"), - "IPv6", "IPv4"); - ((struct sockaddr_in6 *) sctx->addr)->sin6_port = htons (port); - } freeaddrinfo (res); } else @@ -939,55 +1056,184 @@ setup_service (struct GNUNET_SERVICE_Context *sctx) if (disablev6) { /* V4-only */ - sctx->addrlen = sizeof (struct sockaddr_in); - sctx->addr = GNUNET_malloc (sctx->addrlen); + resi = 1; + if (NULL != unixpath) + resi++; + i = 0; + saddrs = GNUNET_malloc ((resi+1) * sizeof(struct sockaddr*)); + saddrlens = GNUNET_malloc ((resi+1) * sizeof (socklen_t)); + if (NULL != unixpath) + { + add_unixpath (saddrs, saddrlens, unixpath); + i++; + } + saddrlens[i] = sizeof (struct sockaddr_in); + saddrs[i] = GNUNET_malloc (saddrlens[i]); #if HAVE_SOCKADDR_IN_SIN_LEN - ((struct sockaddr_in *) sctx->addr)->sin_len = sctx->addrlen; + ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i]; #endif - ((struct sockaddr_in *) sctx->addr)->sin_family = AF_INET; - ((struct sockaddr_in *) sctx->addr)->sin_port = htons (port); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ - ("Configured to bind to %s address; %s connections to this service will fail!\n"), - "IPv4", "IPv6"); + ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; + ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); } else { /* dual stack */ - sctx->addrlen = sizeof (struct sockaddr_in6); - sctx->addr = GNUNET_malloc (sctx->addrlen); + resi = 2; + if (NULL != unixpath) + resi++; + saddrs = GNUNET_malloc ((resi+1) * sizeof(struct sockaddr*)); + saddrlens = GNUNET_malloc ((resi+1) * sizeof (socklen_t)); + i = 0; + if (NULL != unixpath) + { + add_unixpath (saddrs, saddrlens, unixpath); + i++; + } + saddrlens[i] = sizeof (struct sockaddr_in6); + saddrs[i] = GNUNET_malloc (saddrlens[i]); +#if HAVE_SOCKADDR_IN_SIN_LEN + ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0]; +#endif + ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6; + ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); + i++; + saddrlens[i] = sizeof (struct sockaddr_in); + saddrs[i] = GNUNET_malloc (saddrlens[i]); #if HAVE_SOCKADDR_IN_SIN_LEN - ((struct sockaddr_in6 *) sctx->addr)->sin6_len = sctx->addrlen; + ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1]; #endif - ((struct sockaddr_in6 *) sctx->addr)->sin6_family = AF_INET6; - ((struct sockaddr_in6 *) sctx->addr)->sin6_port = htons (port); + ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; + ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); } } - sctx->maxbuf = (size_t) maxbuf; - if (sctx->maxbuf != maxbuf) + GNUNET_free_non_null (unixpath); + *addrs = saddrs; + *addr_lens = saddrlens; + return resi; +} + + +/** + * Setup addr, addrlen, idle_timeout + * based on configuration! + * + * Configuration may specify: + * - PORT (where to bind to for TCP) + * - UNIXPATH (where to bind to for UNIX domain sockets) + * - TIMEOUT (after how many ms does an inactive service timeout); + * - DISABLEV6 (disable support for IPv6, otherwise we use dual-stack) + * - BINDTO (hostname or IP address to bind to, otherwise we take everything) + * - ACCEPT_FROM (only allow connections from specified IPv4 subnets) + * - ACCEPT_FROM6 (only allow connections from specified IPv6 subnets) + * - REJECT_FROM (disallow allow connections from specified IPv4 subnets) + * - REJECT_FROM6 (disallow allow connections from specified IPv6 subnets) + * + * @return GNUNET_OK if configuration succeeded + */ +static int +setup_service (struct GNUNET_SERVICE_Context *sctx) +{ + struct GNUNET_TIME_Relative idleout; + int tolerant; +#ifndef MINGW + const char *lpid; + unsigned int pid; + const char *nfds; + unsigned int cnt; + int flags; +#endif + + if (GNUNET_CONFIGURATION_have_value (sctx->cfg, + sctx->serviceName, "TIMEOUT")) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ - ("Value in configuration for `%s' and service `%s' too large!\n"), - "MAXBUF", sctx->serviceName); - return GNUNET_SYSERR; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (sctx->cfg, + sctx->serviceName, + "TIMEOUT", &idleout)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Specified value for `%s' of service `%s' is invalid\n"), + "TIMEOUT", + sctx->serviceName); + return GNUNET_SYSERR; + } + sctx->timeout = idleout; } + else + sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; + if (GNUNET_CONFIGURATION_have_value (sctx->cfg, + sctx->serviceName, "TOLERANT")) + { + if (GNUNET_SYSERR == + (tolerant = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, + sctx->serviceName, + "TOLERANT"))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Specified value for `%s' of service `%s' is invalid\n"), + "TOLERANT", + sctx->serviceName); + return GNUNET_SYSERR; + } + } + else + tolerant = GNUNET_NO; + +#ifndef MINGW + errno = 0; + if ( (NULL != (lpid = getenv ("LISTEN_PID"))) && + (1 == sscanf (lpid, "%u", &pid)) && + (getpid () == (pid_t) pid) && + (NULL != (nfds = getenv ("LISTEN_FDS"))) && + (1 == sscanf (nfds, "%u", &cnt)) && + (cnt > 0) && + (cnt < FD_SETSIZE) && + (cnt + 4 < FD_SETSIZE) ) + { + sctx->lsocks = GNUNET_malloc (sizeof(struct GNUNET_NETWORK_Handle*) * (cnt+1)); + while (0 < cnt--) + { + flags = fcntl (3 + cnt, F_GETFD); + if ( (flags < 0) || + (0 != (flags & FD_CLOEXEC)) || + (NULL == (sctx->lsocks[cnt] = GNUNET_NETWORK_socket_box_native (3 + cnt))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Could not access pre-bound socket %u, will try to bind myself\n"), + (unsigned int) 3 +cnt); + cnt++; + while (sctx->lsocks[cnt] != NULL) + GNUNET_break (0 == GNUNET_NETWORK_socket_close (sctx->lsocks[cnt++])); + GNUNET_free (sctx->lsocks); + sctx->lsocks = NULL; + break; + } + } + unsetenv ("LISTEN_PID"); + unsetenv ("LISTEN_FDS"); + } +#endif - if ((GNUNET_OK != - process_acl4 (&sctx->v4_denied, - sctx, - "REJECT_FROM")) || - (GNUNET_OK != - process_acl4 (&sctx->v4_allowed, - sctx, - "ACCEPT_FROM")) || - (GNUNET_OK != - process_acl6 (&sctx->v6_denied, - sctx, - "REJECT_FROM6")) || - (GNUNET_OK != process_acl6 (&sctx->v6_allowed, sctx, "ACCEPT_FROM6"))) + if ( (sctx->lsocks == NULL) && + (GNUNET_SYSERR == + GNUNET_SERVICE_get_server_addresses (sctx->serviceName, + sctx->cfg, + &sctx->addrs, + &sctx->addrlens)) ) return GNUNET_SYSERR; + sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES; + sctx->match_uid = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, + sctx->serviceName, + "UNIX_MATCH_UID"); + sctx->match_gid = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, + sctx->serviceName, + "UNIX_MATCH_GID"); + process_acl4 (&sctx->v4_denied, sctx, "REJECT_FROM"); + process_acl4 (&sctx->v4_allowed, sctx, "ACCEPT_FROM"); + process_acl6 (&sctx->v6_denied, sctx, "REJECT_FROM6"); + process_acl6 (&sctx->v6_allowed, sctx, "ACCEPT_FROM6"); + return GNUNET_OK; } @@ -1066,6 +1312,21 @@ write_pid_file (struct GNUNET_SERVICE_Context *sctx, pid_t pid) } +/** + * Task run during shutdown. + * + * @param cls unused + * @param tc unused + */ +static void +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_SERVER_Handle *server = cls; + + GNUNET_SERVER_destroy (server); +} + + /** * Initial task for the service. */ @@ -1075,24 +1336,41 @@ service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) struct GNUNET_SERVICE_Context *sctx = cls; unsigned int i; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Trying to start `%s' at `%s'\n"), - sctx->serviceName, - GNUNET_a2s (sctx->addr, - sctx->addrlen)); - sctx->sched = tc->sched; - sctx->server = GNUNET_SERVER_create (tc->sched, - &check_access, - sctx, - sctx->addr, - sctx->addrlen, - sctx->maxbuf, - sctx->timeout, sctx->require_found); + if (sctx->lsocks != NULL) + sctx->server = GNUNET_SERVER_create_with_sockets (&check_access, + sctx, + sctx->lsocks, + sctx->timeout, sctx->require_found); + else + sctx->server = GNUNET_SERVER_create (&check_access, + sctx, + sctx->addrs, + sctx->addrlens, + sctx->timeout, sctx->require_found); if (sctx->server == NULL) { + if (sctx->addrs != NULL) + { + i = 0; + while (sctx->addrs[i] != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Failed to start `%s' at `%s'\n"), + sctx->serviceName, + GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); + i++; + } + } sctx->ret = GNUNET_SYSERR; return; } + if (0 == (sctx->options & GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN)) + { + /* install a task that will kill the server + process if the scheduler ever gets a shutdown signal */ + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &shutdown_task, sctx->server); + } sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers)); memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers)); i = 0; @@ -1106,12 +1384,19 @@ service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) sctx->ready_confirm_fd = -1; write_pid_file (sctx, getpid ()); } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Service `%s' runs at %s\n"), - sctx->serviceName, - GNUNET_a2s (sctx->addr, - sctx->addrlen)); - sctx->task (sctx->task_cls, tc->sched, sctx->server, sctx->cfg); + if (sctx->addrs != NULL) + { + i = 0; + while (sctx->addrs[i] != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Service `%s' runs at %s\n"), + sctx->serviceName, + GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); + i++; + } + } + sctx->task (sctx->task_cls, sctx->server, sctx->cfg); } @@ -1121,11 +1406,11 @@ service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) static int detach_terminal (struct GNUNET_SERVICE_Context *sctx) { +#ifndef MINGW pid_t pid; int nullfd; int filedes[2]; -#ifndef MINGW if (0 != PIPE (filedes)) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe"); @@ -1175,10 +1460,12 @@ detach_terminal (struct GNUNET_SERVICE_Context *sctx) return GNUNET_SYSERR; /* set stdin/stdout to /dev/null */ if ((dup2 (nullfd, 0) < 0) || (dup2 (nullfd, 1) < 0)) - { + { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2"); + (void) CLOSE (nullfd); return GNUNET_SYSERR; } + (void) CLOSE (nullfd); /* Detach from controlling terminal */ pid = setsid (); if (pid == -1) @@ -1252,6 +1539,7 @@ pid_file_delete (struct GNUNET_SERVICE_Context *sctx) GNUNET_free (pif); } + /** * Run a standard GNUnet service startup sequence (initialize loggers * and configuration, parse options). @@ -1259,10 +1547,9 @@ pid_file_delete (struct GNUNET_SERVICE_Context *sctx) * @param argc number of command line arguments * @param argv command line arguments * @param serviceName our service name + * @param opt service options * @param task main task of the service * @param task_cls closure for task - * @param term termination task of the service - * @param term_cls closure for term * @return GNUNET_SYSERR on error, GNUNET_OK * if we shutdown nicely */ @@ -1270,13 +1557,17 @@ int GNUNET_SERVICE_run (int argc, char *const *argv, const char *serviceName, - GNUNET_SERVICE_Main task, - void *task_cls, GNUNET_SERVICE_Term term, void *term_cls) + enum GNUNET_SERVICE_Options opt, + GNUNET_SERVICE_Main task, void *task_cls) { +#define HANDLE_ERROR do { err = 1; GNUNET_break (0); goto shutdown; } while (0) + + int err; char *cfg_fn; char *loglev; char *logfile; int do_daemonize; + unsigned int i; struct GNUNET_SERVICE_Context sctx; struct GNUNET_CONFIGURATION_Handle *cfg; struct GNUNET_GETOPT_CommandLineOption service_options[] = { @@ -1290,71 +1581,62 @@ GNUNET_SERVICE_run (int argc, GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION), GNUNET_GETOPT_OPTION_END }; + err = 0; do_daemonize = 0; logfile = NULL; loglev = GNUNET_strdup ("WARNING"); - cfg_fn = GNUNET_strdup (GNUNET_DEFAULT_DAEMON_CONFIG_FILE); + cfg_fn = GNUNET_strdup (GNUNET_DEFAULT_USER_CONFIG_FILE); memset (&sctx, 0, sizeof (sctx)); + sctx.options = opt; sctx.ready_confirm_fd = -1; sctx.ret = GNUNET_OK; sctx.timeout = GNUNET_TIME_UNIT_FOREVER_REL; - sctx.maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE; sctx.task = task; + sctx.task_cls = task_cls; sctx.serviceName = serviceName; sctx.cfg = cfg = GNUNET_CONFIGURATION_create (); /* setup subsystems */ - if ((GNUNET_SYSERR == - GNUNET_GETOPT_run (serviceName, - service_options, - argc, - argv)) || - (GNUNET_OK != - GNUNET_log_setup (serviceName, loglev, logfile)) || - (GNUNET_OK != - GNUNET_CONFIGURATION_load (cfg, cfg_fn)) || - (GNUNET_OK != - setup_service (&sctx)) || - ((do_daemonize == 1) && - (GNUNET_OK != detach_terminal (&sctx))) || - (GNUNET_OK != set_user_id (&sctx))) - { - if (sctx.ready_confirm_fd != -1) - { - if (1 != WRITE (sctx.ready_confirm_fd, "I", 1)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write"); - GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd)); - } - GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_free_non_null (sctx.addr); - GNUNET_free_non_null (logfile); - GNUNET_free (loglev); - GNUNET_free (cfg_fn); - GNUNET_free_non_null (sctx.v4_denied); - GNUNET_free_non_null (sctx.v6_denied); - GNUNET_free_non_null (sctx.v4_allowed); - GNUNET_free_non_null (sctx.v6_allowed); - return GNUNET_SYSERR; - } - + if (GNUNET_SYSERR == GNUNET_GETOPT_run (serviceName, service_options, argc, + argv)) + goto shutdown; + if (GNUNET_OK != GNUNET_log_setup (serviceName, loglev, logfile)) + HANDLE_ERROR; + if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfg_fn)) + goto shutdown; + if (GNUNET_OK != setup_service (&sctx)) + goto shutdown; + if ( (do_daemonize == 1) && (GNUNET_OK != detach_terminal (&sctx))) + HANDLE_ERROR; + if (GNUNET_OK != set_user_id (&sctx)) + goto shutdown; +#if DEBUG_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Service `%s' runs with configuration from `%s'\n", + serviceName, cfg_fn); +#endif /* actually run service */ GNUNET_SCHEDULER_run (&service_task, &sctx); + + /* shutdown */ + if ((do_daemonize == 1) && (sctx.server != NULL)) + pid_file_delete (&sctx); + GNUNET_free_non_null (sctx.my_handlers); + +shutdown: if (sctx.ready_confirm_fd != -1) { - if (1 != WRITE (sctx.ready_confirm_fd, "S", 1)) + if (1 != WRITE (sctx.ready_confirm_fd, err ? "I" : "S", 1)) GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write"); GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd)); } - /* shutdown */ - if (term != NULL) - term (term_cls, sctx.cfg); - if ((do_daemonize == 1) && (sctx.server != NULL)) - pid_file_delete (&sctx); - if (sctx.server != NULL) - GNUNET_SERVER_destroy (sctx.server); - GNUNET_free_non_null (sctx.my_handlers); GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_free_non_null (sctx.addr); + i = 0; + if (sctx.addrs != NULL) + while (sctx.addrs[i] != NULL) + GNUNET_free (sctx.addrs[i++]); + GNUNET_free_non_null (sctx.addrs); + GNUNET_free_non_null (sctx.addrlens); GNUNET_free_non_null (logfile); GNUNET_free (loglev); GNUNET_free (cfg_fn); @@ -1362,7 +1644,8 @@ GNUNET_SERVICE_run (int argc, GNUNET_free_non_null (sctx.v6_denied); GNUNET_free_non_null (sctx.v4_allowed); GNUNET_free_non_null (sctx.v6_allowed); - return sctx.ret; + + return err ? GNUNET_SYSERR : sctx.ret; } @@ -1371,13 +1654,11 @@ GNUNET_SERVICE_run (int argc, * initialized system. * * @param serviceName our service name - * @param sched scheduler to use * @param cfg configuration to use * @return NULL on error, service handle */ struct GNUNET_SERVICE_Context * GNUNET_SERVICE_start (const char *serviceName, - struct GNUNET_SCHEDULER_Handle *sched, const struct GNUNET_CONFIGURATION_Handle *cfg) { int i; @@ -1387,21 +1668,29 @@ GNUNET_SERVICE_start (const char *serviceName, sctx->ready_confirm_fd = -1; /* no daemonizing */ sctx->ret = GNUNET_OK; sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; - sctx->maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE; sctx->serviceName = serviceName; sctx->cfg = cfg; - sctx->sched = sched; /* setup subsystems */ - if ((GNUNET_OK != setup_service (sctx)) || - (NULL == (sctx->server = GNUNET_SERVER_create (sched, - &check_access, - sctx, - sctx->addr, - sctx->addrlen, - sctx->maxbuf, - sctx->timeout, - sctx->require_found)))) + if (GNUNET_OK != setup_service (sctx)) + { + GNUNET_SERVICE_stop (sctx); + return NULL; + } + if (sctx->lsocks != NULL) + sctx->server = GNUNET_SERVER_create_with_sockets (&check_access, + sctx, + sctx->lsocks, + sctx->timeout, sctx->require_found); + else + sctx->server = GNUNET_SERVER_create (&check_access, + sctx, + sctx->addrs, + sctx->addrlens, + sctx->timeout, + sctx->require_found); + + if (NULL == sctx->server) { GNUNET_SERVICE_stop (sctx); return NULL; @@ -1412,8 +1701,6 @@ GNUNET_SERVICE_start (const char *serviceName, while ((sctx->my_handlers[i].callback != NULL)) sctx->my_handlers[i++].callback_cls = sctx; GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers); - - return sctx; } @@ -1439,10 +1726,18 @@ GNUNET_SERVICE_get_server (struct GNUNET_SERVICE_Context *ctx) void GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *sctx) { + unsigned int i; if (NULL != sctx->server) GNUNET_SERVER_destroy (sctx->server); GNUNET_free_non_null (sctx->my_handlers); - GNUNET_free_non_null (sctx->addr); + if (sctx->addrs != NULL) + { + i = 0; + while (sctx->addrs[i] != NULL) + GNUNET_free (sctx->addrs[i++]); + GNUNET_free (sctx->addrs); + } + GNUNET_free_non_null (sctx->addrlens); GNUNET_free_non_null (sctx->v4_denied); GNUNET_free_non_null (sctx->v6_denied); GNUNET_free_non_null (sctx->v4_allowed);