X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Futil%2Fresolver_api.c;h=0c915932c4cadd176d4ff3fb903e79406b588197;hb=17d34d5e094c2f8a90717b07e3a711d6e2c15903;hp=3eff02c5c1efa261bf8401cd3dadf1f5346e3313;hpb=bb5fe91d23b0938baa3c4f0e92a83df659df216a;p=oweals%2Fgnunet.git diff --git a/src/util/resolver_api.c b/src/util/resolver_api.c index 3eff02c5c..0c915932c 100644 --- a/src/util/resolver_api.c +++ b/src/util/resolver_api.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2009-2015 GNUnet e.V. + Copyright (C) 2009-2016 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -29,9 +29,9 @@ #include "gnunet_resolver_service.h" #include "resolver.h" -#define LOG(kind,...) GNUNET_log_from (kind, "resolver-api", __VA_ARGS__) +#define LOG(kind,...) GNUNET_log_from (kind, "util-resolver-api", __VA_ARGS__) -#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "resolver-api", syscall) +#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-resolver-api", syscall) /** * Maximum supported length for a hostname @@ -58,7 +58,7 @@ static const struct GNUNET_CONFIGURATION_Handle *resolver_cfg; * Our connection to the resolver service, created on-demand, but then * persists until error or shutdown. */ -static struct GNUNET_CLIENT_Connection *client; +static struct GNUNET_MQ_Handle *mq; /** * Head of DLL of requests. @@ -118,7 +118,7 @@ struct GNUNET_RESOLVER_RequestHandle GNUNET_RESOLVER_HostnameCallback name_callback; /** - * Closure for the respective "callback". + * Closure for the callbacks. */ void *cls; @@ -131,7 +131,7 @@ struct GNUNET_RESOLVER_RequestHandle * Task handle for making reply callbacks in numeric lookups * asynchronous, and for timeout handling. */ - struct GNUNET_SCHEDULER_Task * task; + struct GNUNET_SCHEDULER_Task *task; /** * Desired address family. @@ -171,8 +171,11 @@ struct GNUNET_RESOLVER_RequestHandle /** * Check that the resolver service runs on localhost * (or equivalent). + * + * @return #GNUNET_OK if the resolver is properly configured, + * #GNUNET_SYSERR otherwise. */ -static void +static int check_config () { char *hostname; @@ -197,32 +200,32 @@ check_config () "HOSTNAME", &hostname)) { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Must specify `%s' for `%s' in configuration!\n"), + LOG (GNUNET_ERROR_TYPE_INFO, + _("Missing `%s' for `%s' in configuration, DNS resolution will be unavailable.\n"), "HOSTNAME", "resolver"); - GNUNET_assert (0); + return GNUNET_SYSERR; } - if ((1 != inet_pton (AF_INET, hostname, &v4)) || - (1 != inet_pton (AF_INET6, hostname, &v6))) + if ((1 == inet_pton (AF_INET, hostname, &v4)) || + (1 == inet_pton (AF_INET6, hostname, &v6))) { GNUNET_free (hostname); - return; + return GNUNET_OK; } i = 0; while (NULL != loopback[i]) if (0 == strcasecmp (loopback[i++], hostname)) { GNUNET_free (hostname); - return; + return GNUNET_OK; } - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Must specify `%s' or numeric IP address for `%s' of `%s' in configuration!\n"), + LOG (GNUNET_ERROR_TYPE_INFO, + _("Missing `%s' or numeric IP address for `%s' of `%s' in configuration, DNS resolution will be unavailable.\n"), "localhost", "HOSTNAME", "resolver"); GNUNET_free (hostname); - GNUNET_assert (0); + return GNUNET_SYSERR; } @@ -237,7 +240,6 @@ GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) GNUNET_assert (NULL != cfg); backoff = GNUNET_TIME_UNIT_MILLISECONDS; resolver_cfg = cfg; - check_config (); } @@ -247,14 +249,22 @@ GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) void GNUNET_RESOLVER_disconnect () { - GNUNET_assert (NULL == req_head); - GNUNET_assert (NULL == req_tail); - if (NULL != client) + struct GNUNET_RESOLVER_RequestHandle *rh; + + while (NULL != (rh = req_head)) + { + GNUNET_assert (GNUNET_SYSERR == rh->was_transmitted); + GNUNET_CONTAINER_DLL_remove (req_head, + req_tail, + rh); + GNUNET_free (rh); + } + if (NULL != mq) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from DNS service\n"); - GNUNET_CLIENT_disconnect (client); - client = NULL; + GNUNET_MQ_destroy (mq); + mq = NULL; } if (NULL != r_task) { @@ -269,6 +279,42 @@ GNUNET_RESOLVER_disconnect () } +/** + * Task executed on system shutdown. + */ +static void +shutdown_task (void *cls) +{ + s_task = NULL; + GNUNET_RESOLVER_disconnect (); + backoff = GNUNET_TIME_UNIT_MILLISECONDS; +} + + +/** + * Consider disconnecting if we have no further requests pending. + */ +static void +check_disconnect () +{ + struct GNUNET_RESOLVER_RequestHandle *rh; + + for (rh = req_head; NULL != rh; rh = rh->next) + if (GNUNET_SYSERR != rh->was_transmitted) + return; + if (NULL != r_task) + { + GNUNET_SCHEDULER_cancel (r_task); + r_task = NULL; + } + if (NULL != s_task) + return; + s_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, + &shutdown_task, + NULL); +} + + /** * Convert IP address to string without DNS resolution. * @@ -279,7 +325,8 @@ GNUNET_RESOLVER_disconnect () */ static char * no_resolve (int af, - const void *ip, socklen_t ip_len) + const void *ip, + socklen_t ip_len) { char buf[INET6_ADDRSTRLEN]; @@ -323,81 +370,99 @@ reconnect (void); /** - * Process pending requests to the resolver. + * Generic error handler, called with the appropriate error code and + * the same closure specified at the creation of the message queue. + * Not every message queue implementation supports an error handler. + * + * @param cls NULL + * @param error error code */ static void -process_requests (void); +mq_error_handler (void *cls, + enum GNUNET_MQ_Error error) +{ + GNUNET_MQ_destroy (mq); + mq = NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "MQ error, reconnecting\n"); + reconnect (); +} /** - * Process response with a hostname for a DNS lookup. - * - * @param cls our `struct GNUNET_RESOLVER_RequestHandle *` context - * @param msg message with the hostname, NULL on error + * Process pending requests to the resolver. */ static void -handle_response (void *cls, - const struct GNUNET_MessageHeader *msg) +process_requests () { - struct GNUNET_RESOLVER_RequestHandle *rh = cls; - uint16_t size; - char *nret; + struct GNUNET_RESOLVER_GetMessage *msg; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_RESOLVER_RequestHandle *rh = req_head; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Receiving response from DNS service\n"); - if (NULL == msg) + if (NULL == mq) { - char buf[INET6_ADDRSTRLEN]; - - if (NULL != rh->name_callback) - LOG (GNUNET_ERROR_TYPE_INFO, - _("Timeout trying to resolve IP address `%s'.\n"), - inet_ntop (rh->af, - (const void *) &rh[1], - buf, - sizeof(buf))); - else - LOG (GNUNET_ERROR_TYPE_INFO, - _("Timeout trying to resolve hostname `%s'.\n"), - (const char *) &rh[1]); - /* check if request was canceled */ - if (GNUNET_SYSERR != rh->was_transmitted) - { - if (NULL != rh->name_callback) - { - /* no reverse lookup was successful, return IP as string */ - if (GNUNET_NO == rh->received_response) - { - nret = no_resolve (rh->af, - &rh[1], - rh->data_len); - rh->name_callback (rh->cls, nret); - GNUNET_free (nret); - } - /* finally, make termination call */ - rh->name_callback (rh->cls, - NULL); - } - if (NULL != rh->addr_callback) - rh->addr_callback (rh->cls, - NULL, - 0); - } - rh->was_transmitted = GNUNET_NO; - GNUNET_RESOLVER_request_cancel (rh); - GNUNET_CLIENT_disconnect (client); - client = NULL; reconnect (); return; } - if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type)) + if (NULL == rh) { - GNUNET_break (0); - GNUNET_CLIENT_disconnect (client); - client = NULL; - reconnect (); + /* nothing to do, release socket really soon if there is nothing + * else happening... */ + s_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, + &shutdown_task, + NULL); return; } + if (GNUNET_NO != rh->was_transmitted) + return; /* waiting for reply */ + env = GNUNET_MQ_msg_extra (msg, + rh->data_len, + GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST); + msg->direction = htonl (rh->direction); + msg->af = htonl (rh->af); + GNUNET_memcpy (&msg[1], + &rh[1], + rh->data_len); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting DNS resolution request to DNS service\n"); + GNUNET_MQ_send (mq, + env); + rh->was_transmitted = GNUNET_YES; +} + + +/** + * Check validity of response with a hostname for a DNS lookup. + * + * @param cls NULL + * @param msg message with the hostname + */ +static int +check_response (void *cls, + const struct GNUNET_MessageHeader *msg) +{ + /* implemented in #handle_response() for now */ + return GNUNET_OK; +} + + +/** + * Check validity of response with a hostname for a DNS lookup. + * NOTE: right now rather messy, might want to use different + * message types for different response formats in the future. + * + * @param cls NULL + * @param msg message with the response + */ +static void +handle_response (void *cls, + const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_RESOLVER_RequestHandle *rh = req_head; + uint16_t size; + char *nret; + size = ntohs (msg->size); if (size == sizeof (struct GNUNET_MessageHeader)) { @@ -446,8 +511,8 @@ handle_response (void *cls, NULL); rh->was_transmitted = GNUNET_NO; GNUNET_RESOLVER_request_cancel (rh); - GNUNET_CLIENT_disconnect (client); - client = NULL; + GNUNET_MQ_destroy (mq); + mq = NULL; reconnect (); return; } @@ -504,8 +569,8 @@ handle_response (void *cls, 0); rh->was_transmitted = GNUNET_NO; GNUNET_RESOLVER_request_cancel (rh); - GNUNET_CLIENT_disconnect (client); - client = NULL; + GNUNET_MQ_destroy (mq); + mq = NULL; reconnect (); return; } @@ -516,10 +581,6 @@ handle_response (void *cls, sa, salen); } - GNUNET_CLIENT_receive (client, - &handle_response, - rh, - GNUNET_TIME_absolute_get_remaining (rh->timeout)); } @@ -529,11 +590,9 @@ handle_response (void *cls, * numeric addresses. * * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request - * @param tc unused scheduler context */ static void -numeric_resolution (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +numeric_resolution (void *cls) { struct GNUNET_RESOLVER_RequestHandle *rh = cls; struct sockaddr_in v4; @@ -574,7 +633,9 @@ numeric_resolution (void *cls, } if ( ( (rh->af == AF_UNSPEC) || (rh->af == AF_INET6) ) && - (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr) ) ) + (1 == inet_pton (AF_INET6, + hostname, + &v6.sin6_addr) ) ) { rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, @@ -597,11 +658,9 @@ numeric_resolution (void *cls, * respective loopback numeric addresses. * * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request - * @param tc unused scheduler context */ static void -loopback_resolution (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +loopback_resolution (void *cls) { struct GNUNET_RESOLVER_RequestHandle *rh = cls; struct sockaddr_in v4; @@ -639,6 +698,7 @@ loopback_resolution (void *cls, rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4)); + break; default: GNUNET_break (0); @@ -647,77 +707,10 @@ loopback_resolution (void *cls, rh->addr_callback (rh->cls, NULL, 0); - GNUNET_free (rh); -} - - -/** - * Task executed on system shutdown. - */ -static void -shutdown_task (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - s_task = NULL; - GNUNET_RESOLVER_disconnect (); - backoff = GNUNET_TIME_UNIT_MILLISECONDS; -} - - -/** - * Process pending requests to the resolver. - */ -static void -process_requests () -{ - struct GNUNET_RESOLVER_GetMessage *msg; - char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN; - struct GNUNET_RESOLVER_RequestHandle *rh; - - if (NULL == client) - { - reconnect (); - return; - } - rh = req_head; - if (NULL == rh) - { - /* nothing to do, release socket really soon if there is nothing - * else happening... */ - s_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, - &shutdown_task, - NULL); - return; - } - if (GNUNET_NO != rh->was_transmitted) - return; /* waiting for reply */ - msg = (struct GNUNET_RESOLVER_GetMessage *) buf; - msg->header.size = - htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + rh->data_len); - msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST); - msg->direction = htonl (rh->direction); - msg->af = htonl (rh->af); - memcpy (&msg[1], - &rh[1], - rh->data_len); LOG (GNUNET_ERROR_TYPE_DEBUG, - "Transmitting DNS resolution request to DNS service\n"); - if (GNUNET_OK != - GNUNET_CLIENT_transmit_and_get_response (client, - &msg->header, - GNUNET_TIME_absolute_get_remaining (rh->timeout), - GNUNET_YES, - &handle_response, - rh)) - { - GNUNET_CLIENT_disconnect (client); - client = NULL; - GNUNET_break (0); - reconnect (); - return; - } - rh->was_transmitted = GNUNET_YES; + "Finished resolving hostname `%s'.\n", + (const char *) &rh[1]); + GNUNET_free (rh); } @@ -725,22 +718,29 @@ process_requests () * Now try to reconnect to the resolver service. * * @param cls NULL - * @param tc scheduler context */ static void -reconnect_task (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +reconnect_task (void *cls) { + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (response, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_handler_end () + }; + r_task = NULL; if (NULL == req_head) return; /* no work pending */ - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - return; LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to connect to DNS service\n"); - client = GNUNET_CLIENT_connect ("resolver", - resolver_cfg); - if (NULL == client) + mq = GNUNET_CLIENT_connect (resolver_cfg, + "resolver", + handlers, + &mq_error_handler, + NULL); + if (NULL == mq) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect, will try again later\n"); @@ -761,7 +761,7 @@ reconnect () if (NULL != r_task) return; - GNUNET_assert (NULL == client); + GNUNET_assert (NULL == mq); if (NULL != (rh = req_head)) { switch (rh->was_transmitted) @@ -775,8 +775,11 @@ reconnect () break; case GNUNET_SYSERR: /* request was cancelled, remove entirely */ - GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh); + GNUNET_CONTAINER_DLL_remove (req_head, + req_tail, + rh); GNUNET_free (rh); + check_disconnect (); break; default: GNUNET_assert (0); @@ -799,19 +802,52 @@ reconnect () * A DNS resolution timed out. Notify the application. * * @param cls the `struct GNUNET_RESOLVER_RequestHandle *` - * @param tc scheduler context */ static void -handle_lookup_timeout (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +handle_lookup_timeout (void *cls) { struct GNUNET_RESOLVER_RequestHandle *rh = cls; rh->task = NULL; - rh->addr_callback (rh->cls, - NULL, - 0); + if (GNUNET_NO == rh->direction) + { + LOG (GNUNET_ERROR_TYPE_INFO, + _("Timeout trying to resolve hostname `%s'.\n"), + (const char *) &rh[1]); + if (NULL != rh->addr_callback) + rh->addr_callback (rh->cls, + NULL, + 0); + } + else + { + char buf[INET6_ADDRSTRLEN]; + + LOG (GNUNET_ERROR_TYPE_INFO, + _("Timeout trying to resolve IP address `%s'.\n"), + inet_ntop (rh->af, + (const void *) &rh[1], + buf, + sizeof(buf))); + if (GNUNET_NO == rh->received_response) + { + char *nret; + + nret = no_resolve (rh->af, + &rh[1], + rh->data_len); + if (NULL != rh->name_callback) + rh->name_callback (rh->cls, nret); + GNUNET_free (nret); + } + /* finally, make termination call */ + if (NULL != rh->name_callback) + rh->name_callback (rh->cls, + NULL); + } + rh->was_transmitted = GNUNET_NO; GNUNET_RESOLVER_request_cancel (rh); + process_requests (); } @@ -840,18 +876,21 @@ GNUNET_RESOLVER_ip_get (const char *hostname, slen = strlen (hostname) + 1; if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >= - GNUNET_SERVER_MAX_MESSAGE_SIZE) + GNUNET_MAX_MESSAGE_SIZE) { GNUNET_break (0); return NULL; } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Trying to resolve hostname `%s'.\n", + hostname); rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen); rh->af = af; rh->addr_callback = callback; rh->cls = callback_cls; - memcpy (&rh[1], - hostname, - slen); + GNUNET_memcpy (&rh[1], + hostname, + slen); rh->data_len = slen; rh->timeout = GNUNET_TIME_relative_to_absolute (timeout); rh->direction = GNUNET_NO; @@ -875,6 +914,11 @@ GNUNET_RESOLVER_ip_get (const char *hostname, rh); return rh; } + if (GNUNET_OK != check_config ()) + { + GNUNET_free (rh); + return NULL; + } rh->task = GNUNET_SCHEDULER_add_delayed (timeout, &handle_lookup_timeout, rh); @@ -899,11 +943,9 @@ GNUNET_RESOLVER_ip_get (const char *hostname, * conversion and invoke the callback. * * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request - * @param tc unused scheduler context */ static void -numeric_reverse (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +numeric_reverse (void *cls) { struct GNUNET_RESOLVER_RequestHandle *rh = cls; char *result; @@ -923,6 +965,11 @@ numeric_reverse (void *cls, } rh->name_callback (rh->cls, NULL); + if (NULL != rh->task) + { + GNUNET_SCHEDULER_cancel (rh->task); + rh->task = NULL; + } GNUNET_free (rh); } @@ -951,7 +998,13 @@ GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa, size_t ip_len; const void *ip; - check_config (); + if (GNUNET_OK != check_config ()) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Resolver not configured correctly.\n")); + return NULL; + } + switch (sa->sa_family) { case AF_INET: @@ -973,15 +1026,21 @@ GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa, rh->cls = cls; rh->af = sa->sa_family; rh->timeout = GNUNET_TIME_relative_to_absolute (timeout); - memcpy (&rh[1], ip, ip_len); + GNUNET_memcpy (&rh[1], + ip, + ip_len); rh->data_len = ip_len; rh->direction = GNUNET_YES; rh->received_response = GNUNET_NO; if (GNUNET_NO == do_resolve) { - rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse, rh); + rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse, + rh); return rh; } + rh->task = GNUNET_SCHEDULER_add_delayed (timeout, + &handle_lookup_timeout, + rh); GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh); @@ -1117,6 +1176,10 @@ GNUNET_RESOLVER_hostname_resolve (int af, void GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh) { + if (GNUNET_NO == rh->direction) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Asked to cancel request to resolve hostname `%s'.\n", + (const char *) &rh[1]); if (NULL != rh->task) { GNUNET_SCHEDULER_cancel (rh->task); @@ -1129,10 +1192,12 @@ GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh) req_tail, rh); GNUNET_free (rh); + check_disconnect (); return; } GNUNET_assert (GNUNET_YES == rh->was_transmitted); rh->was_transmitted = GNUNET_SYSERR; /* mark as cancelled */ + check_disconnect (); }