From c38455c65f9968ccf7e870c3cce30fa19f319376 Mon Sep 17 00:00:00 2001 From: lurchi Date: Sun, 24 Jun 2018 23:31:07 +0200 Subject: [PATCH] use the asynchronous DNS resolution API (getaddrinfo_a) in the resolver module --- configure.ac | 8 +- src/util/Makefile.am | 3 + src/util/gnunet-service-resolver.c | 269 +++++++++++++++++++++++++---- src/util/resolver.h | 27 +++ src/util/resolver_api.c | 37 +++- src/util/test_resolver_api.c | 2 + 6 files changed, 299 insertions(+), 47 deletions(-) diff --git a/configure.ac b/configure.ac index 99eeeba8a..fc44dcf5a 100644 --- a/configure.ac +++ b/configure.ac @@ -58,7 +58,7 @@ fi # Use Linux interface name unless the OS has a different preference DEFAULT_INTERFACE="\"eth0\"" -funcstocheck="getnameinfo gethostname gethostbyname gethostbyaddr getaddrinfo" +funcstocheck="getnameinfo gethostname gethostbyname gethostbyaddr getaddrinfo getaddrinfo_a" # Srcdir in a form that native compiler understands (i.e. DOS path on W32) native_srcdir=$srcdir @@ -230,6 +230,10 @@ AC_CHECK_DECLS([_stati64]) # will be more selective! SAVE_LIBS=$LIBS +have_addrinfo_a=0 +AC_CHECK_LIB(anl, getaddrinfo_a, [have_addrinfo_a=1 AC_DEFINE(HAVE_GETADDRINFO_A,1,[getaddrinfo_a supported])]) +AM_CONDITIONAL(HAVE_GETADDRINFO_A, [test "$have_addrinfo_a" = 1]) + # tests only run on Windows if test "x$build_target" = "xmingw" then @@ -1537,7 +1541,7 @@ AC_LINK_IFELSE( ],[ AC_DEFINE_UNQUOTED([HAVE_GETADDRINFO],1,[Define this if getaddrinfo() is available]) ],[ - AC_DEFINE_UNQUOTED([HAVE_GETADDRINFO],1,[Define this if getaddrinfo() is available]) + AC_DEFINE_UNQUOTED([HAVE_GETADDRINFO],0,[Define this if getaddrinfo() is available]) ]) else diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 208cab07b..4296199db 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -199,6 +199,9 @@ gnunet_service_resolver_SOURCES = \ gnunet_service_resolver_LDADD = \ libgnunetutil.la \ $(GN_LIBINTL) +if HAVE_GETADDRINFO_A +gnunet_service_resolver_LDADD += -lanl +endif gnunet_resolver_SOURCES = \ diff --git a/src/util/gnunet-service-resolver.c b/src/util/gnunet-service-resolver.c index d26ec659f..d90d8ec10 100644 --- a/src/util/gnunet-service-resolver.c +++ b/src/util/gnunet-service-resolver.c @@ -84,6 +84,16 @@ static struct IPCache *cache_head; */ static struct IPCache *cache_tail; +/** + * Pipe for asynchronously notifying about resolve result + */ +static struct GNUNET_DISK_PipeHandle *resolve_result_pipe; + +/** + * Task for reading from resolve_result_pipe + */ +static struct GNUNET_SCHEDULER_Task *resolve_result_pipe_task; + #if HAVE_GETNAMEINFO /** @@ -223,14 +233,15 @@ notify_service_client_done (void *cls) static void get_ip_as_string (struct GNUNET_SERVICE_Client *client, int af, - const void *ip) + const void *ip, + uint32_t request_id) { struct IPCache *pos; struct IPCache *next; struct GNUNET_TIME_Absolute now; struct GNUNET_MQ_Envelope *env; struct GNUNET_MQ_Handle *mq; - struct GNUNET_MessageHeader *msg; + struct GNUNET_RESOLVER_ResponseMessage *msg; size_t ip_len; struct in6_addr ix; size_t alen; @@ -304,13 +315,16 @@ get_ip_as_string (struct GNUNET_SERVICE_Client *client, env = GNUNET_MQ_msg_extra (msg, alen, GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + msg->id = request_id; GNUNET_memcpy (&msg[1], pos->addr, alen); GNUNET_MQ_send (mq, env); + // send end message env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + msg->id = request_id; GNUNET_MQ_notify_sent (env, ¬ify_service_client_done, client); @@ -319,17 +333,152 @@ get_ip_as_string (struct GNUNET_SERVICE_Client *client, } -#if HAVE_GETADDRINFO +#if HAVE_GETADDRINFO_A +struct AsyncCls +{ + struct gaicb *host; + struct sigevent *sig; + struct GNUNET_MQ_Handle *mq; + uint32_t request_id; +}; + + +static void +resolve_result_pipe_cb (void *cls) +{ + struct AsyncCls *async_cls; + struct gaicb *host; + struct GNUNET_RESOLVER_ResponseMessage *msg; + struct GNUNET_MQ_Envelope *env; + + GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle (resolve_result_pipe, + GNUNET_DISK_PIPE_END_READ), + &async_cls, + sizeof (struct AsyncCls *)); + resolve_result_pipe_task = + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_DISK_pipe_handle (resolve_result_pipe, + GNUNET_DISK_PIPE_END_READ), + &resolve_result_pipe_cb, + NULL); + host = async_cls->host; + for (struct addrinfo *pos = host->ar_result; pos != NULL; pos = pos->ai_next) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Lookup result for hostname %s: %s (request ID %u)\n", + host->ar_name, + GNUNET_a2s (pos->ai_addr, pos->ai_addrlen), + async_cls->request_id); + switch (pos->ai_family) + { + case AF_INET: + env = GNUNET_MQ_msg_extra (msg, + sizeof (struct in_addr), + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + msg->id = async_cls->request_id; + GNUNET_memcpy (&msg[1], + &((struct sockaddr_in*) pos->ai_addr)->sin_addr, + sizeof (struct in_addr)); + GNUNET_MQ_send (async_cls->mq, + env); + break; + case AF_INET6: + env = GNUNET_MQ_msg_extra (msg, + sizeof (struct in6_addr), + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + msg->id = async_cls->request_id; + GNUNET_memcpy (&msg[1], + &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr, + sizeof (struct in6_addr)); + GNUNET_MQ_send (async_cls->mq, + env); + break; + default: + /* unsupported, skip */ + break; + } + } + // send end message + env = GNUNET_MQ_msg (msg, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + msg->id = async_cls->request_id; + GNUNET_MQ_send (async_cls->mq, + env); + freeaddrinfo (host->ar_result); + GNUNET_free ((struct gaicb *)host->ar_request); // free hints + GNUNET_free (host); + GNUNET_free (async_cls->sig); + GNUNET_free (async_cls); +} + + +static void +handle_async_result (union sigval val) +{ + GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (resolve_result_pipe, + GNUNET_DISK_PIPE_END_WRITE), + &val.sival_ptr, + sizeof (val.sival_ptr)); +} + + +static int +getaddrinfo_a_resolve (struct GNUNET_MQ_Handle *mq, + const char *hostname, + int af, + uint32_t request_id) +{ + int ret; + struct gaicb *host; + struct addrinfo *hints; + struct sigevent *sig; + struct AsyncCls *async_cls; + + host = GNUNET_new (struct gaicb); + hints = GNUNET_new (struct addrinfo); + sig = GNUNET_new (struct sigevent); + async_cls = GNUNET_new (struct AsyncCls); + memset (hints, + 0, + sizeof (struct addrinfo)); + memset (sig, + 0, + sizeof (struct sigevent)); + hints->ai_family = af; + hints->ai_socktype = SOCK_STREAM; /* go for TCP */ + host->ar_name = hostname; + host->ar_service = NULL; + host->ar_request = hints; + host->ar_result = NULL; + sig->sigev_notify = SIGEV_THREAD; + sig->sigev_value.sival_ptr = async_cls; + sig->sigev_notify_function = &handle_async_result; + async_cls->host = host; + async_cls->sig = sig; + async_cls->mq = mq; + async_cls->request_id = request_id; + ret = getaddrinfo_a (GAI_NOWAIT, + &host, + 1, + sig); + if (0 != ret) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +#elif HAVE_GETADDRINFO static int getaddrinfo_resolve (struct GNUNET_MQ_Handle *mq, const char *hostname, - int af) + int af, + uint32_t request_id) { int s; struct addrinfo hints; struct addrinfo *result; struct addrinfo *pos; - struct GNUNET_MessageHeader *msg; + struct GNUNET_RESOLVER_ResponseMessage *msg; struct GNUNET_MQ_Envelope *env; #ifdef WINDOWS @@ -340,10 +489,12 @@ getaddrinfo_resolve (struct GNUNET_MQ_Handle *mq, int ret2; ret1 = getaddrinfo_resolve (mq, hostname, - AF_INET); + AF_INET, + request_id); ret2 = getaddrinfo_resolve (mq, hostname, - AF_INET6); + AF_INET6, + request_id); if ( (ret1 == GNUNET_OK) || (ret2 == GNUNET_OK) ) return GNUNET_OK; @@ -389,6 +540,7 @@ getaddrinfo_resolve (struct GNUNET_MQ_Handle *mq, env = GNUNET_MQ_msg_extra (msg, sizeof (struct in_addr), GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + msg->id = request_id; GNUNET_memcpy (&msg[1], &((struct sockaddr_in*) pos->ai_addr)->sin_addr, sizeof (struct in_addr)); @@ -399,6 +551,7 @@ getaddrinfo_resolve (struct GNUNET_MQ_Handle *mq, env = GNUNET_MQ_msg_extra (msg, sizeof (struct in6_addr), GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + msg->id = request_id; GNUNET_memcpy (&msg[1], &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr, sizeof (struct in6_addr)); @@ -421,13 +574,14 @@ getaddrinfo_resolve (struct GNUNET_MQ_Handle *mq, static int gethostbyname2_resolve (struct GNUNET_MQ_Handle *mq, const char *hostname, - int af) + int af, + uint32_t request_id) { struct hostent *hp; int ret1; int ret2; struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *msg; + struct GNUNET_RESOLVER_ResponseMessage *msg; #ifdef WINDOWS /* gethostbyname2() in plibc is a compat dummy that calls gethostbyname(). */ @@ -438,10 +592,12 @@ gethostbyname2_resolve (struct GNUNET_MQ_Handle *mq, { ret1 = gethostbyname2_resolve (mq, hostname, - AF_INET); + AF_INET, + request_id); ret2 = gethostbyname2_resolve (mq, hostname, - AF_INET6); + AF_INET6, + request_id); if ( (ret1 == GNUNET_OK) || (ret2 == GNUNET_OK) ) return GNUNET_OK; @@ -468,6 +624,7 @@ gethostbyname2_resolve (struct GNUNET_MQ_Handle *mq, env = GNUNET_MQ_msg_extra (msg, hp->h_length, GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + msg->id = request_id; GNUNET_memcpy (&msg[1], hp->h_addr_list[0], hp->h_length); @@ -479,6 +636,7 @@ gethostbyname2_resolve (struct GNUNET_MQ_Handle *mq, env = GNUNET_MQ_msg_extra (msg, hp->h_length, GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + msg->id = request_id; GNUNET_memcpy (&msg[1], hp->h_addr_list[0], hp->h_length); @@ -497,10 +655,11 @@ gethostbyname2_resolve (struct GNUNET_MQ_Handle *mq, static int gethostbyname_resolve (struct GNUNET_MQ_Handle *mq, - const char *hostname) + const char *hostname, + uint32_t request_id) { struct hostent *hp; - struct GNUNET_MessageHeader *msg; + struct GNUNET_RESOLVER_ResponseMessage *msg; struct GNUNET_MQ_Envelope *env; hp = GETHOSTBYNAME (hostname); @@ -521,6 +680,7 @@ gethostbyname_resolve (struct GNUNET_MQ_Handle *mq, env = GNUNET_MQ_msg_extra (msg, hp->h_length, GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + msg->id = request_id; GNUNET_memcpy (&msg[1], hp->h_addr_list[0], hp->h_length); @@ -541,34 +701,42 @@ gethostbyname_resolve (struct GNUNET_MQ_Handle *mq, static void get_ip_from_hostname (struct GNUNET_SERVICE_Client *client, const char *hostname, - int af) + int af, + uint32_t request_id) { - int ret; - struct GNUNET_MQ_Handle *mq; struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *msg; + struct GNUNET_RESOLVER_ResponseMessage *msg; + struct GNUNET_MQ_Handle *mq; mq = GNUNET_SERVICE_client_get_mq (client); - ret = GNUNET_NO; -#if HAVE_GETADDRINFO - if (ret == GNUNET_NO) - ret = getaddrinfo_resolve (mq, - hostname, - af); +#if HAVE_GETADDRINFO_A + getaddrinfo_a_resolve (mq, + hostname, + af, + request_id); + GNUNET_SERVICE_client_continue (client); + return; +#elif HAVE_GETADDRINFO + getaddrinfo_resolve (mq, + hostname, + af, + request_id); #elif HAVE_GETHOSTBYNAME2 - if (ret == GNUNET_NO) - ret = gethostbyname2_resolve (mq, - hostname, - af); + gethostbyname2_resolve (mq, + hostname, + af, + request_id); #elif HAVE_GETHOSTBYNAME - if ( (ret == GNUNET_NO) && - ( (af == AF_UNSPEC) || + if ( ( (af == AF_UNSPEC) || (af == PF_INET) ) ) gethostbyname_resolve (mq, - hostname); + hostname, + request_id); #endif + // send end message env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + msg->id = request_id; GNUNET_MQ_notify_sent (env, ¬ify_service_client_done, client); @@ -647,21 +815,21 @@ handle_get (void *cls, const void *ip; int direction; int af; + uint32_t id; direction = ntohl (msg->direction); af = ntohl (msg->af); + id = ntohl (msg->id); if (GNUNET_NO == direction) { /* IP from hostname */ const char *hostname; hostname = (const char *) &msg[1]; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Resolver asked to look up `%s'.\n", - hostname); get_ip_from_hostname (client, hostname, - af); + af, + id); return; } ip = &msg[1]; @@ -671,16 +839,18 @@ handle_get (void *cls, char buf[INET6_ADDRSTRLEN]; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Resolver asked to look up IP address `%s'.\n", + "Resolver asked to look up IP address `%s (request ID %u)'.\n", inet_ntop (af, ip, buf, - sizeof (buf))); + sizeof (buf)), + id); } #endif get_ip_as_string (client, af, - ip); + ip, + id); } @@ -700,6 +870,19 @@ connect_cb (void *cls, (void) cls; (void) mq; +#if HAVE_GETADDRINFO_A + resolve_result_pipe = GNUNET_DISK_pipe (GNUNET_NO, + GNUNET_NO, + GNUNET_NO, + GNUNET_NO); + GNUNET_assert (NULL != resolve_result_pipe); + resolve_result_pipe_task = + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_DISK_pipe_handle (resolve_result_pipe, + GNUNET_DISK_PIPE_END_READ), + &resolve_result_pipe_cb, + NULL); +#endif return c; } @@ -718,6 +901,18 @@ disconnect_cb (void *cls, { (void) cls; +#if HAVE_GETADDRINFO_A + if (NULL != resolve_result_pipe_task) + { + GNUNET_SCHEDULER_cancel (resolve_result_pipe_task); + resolve_result_pipe_task = NULL; + } + if (NULL != resolve_result_pipe) + { + GNUNET_DISK_pipe_close (resolve_result_pipe); + resolve_result_pipe = NULL; + } +#endif GNUNET_assert (c == internal_cls); } diff --git a/src/util/resolver.h b/src/util/resolver.h index 0b137f085..a0f105afa 100644 --- a/src/util/resolver.h +++ b/src/util/resolver.h @@ -56,10 +56,37 @@ struct GNUNET_RESOLVER_GetMessage */ int32_t af GNUNET_PACKED; + /** + * identifies the request and is contained in the response message. The + * client has to match response to request by this identifier. + */ + uint32_t id GNUNET_PACKED; + /* followed by 0-terminated string for A/AAAA-lookup or by 'struct in_addr' / 'struct in6_addr' for reverse lookup */ }; + + +struct GNUNET_RESOLVER_ResponseMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE + */ + struct GNUNET_MessageHeader header; + + /** + * identifies the request this message responds to. The client + * has to match response to request by this identifier. + */ + uint32_t id GNUNET_PACKED; + + /* followed by 0-terminated string for response to a reverse lookup + * or by 'struct in_addr' / 'struct in6_addr' for response to + * A/AAAA-lookup + */ +}; + GNUNET_NETWORK_STRUCT_END #endif diff --git a/src/util/resolver_api.c b/src/util/resolver_api.c index afebabf08..b94819f06 100644 --- a/src/util/resolver_api.c +++ b/src/util/resolver_api.c @@ -68,6 +68,11 @@ static struct GNUNET_RESOLVER_RequestHandle *req_head; */ static struct GNUNET_RESOLVER_RequestHandle *req_tail; +/** + * ID of the last request we sent to the service + */ +static uint32_t last_request_id; + /** * How long should we wait to reconnect? */ @@ -136,6 +141,11 @@ struct GNUNET_RESOLVER_RequestHandle */ int af; + /** + * Identifies the request. The response will contain this id. + */ + uint32_t id; + /** * Has this request been transmitted to the service? * #GNUNET_YES if transmitted @@ -435,11 +445,13 @@ process_requests () GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST); msg->direction = htonl (rh->direction); msg->af = htonl (rh->af); + msg->id = htonl (rh->id); GNUNET_memcpy (&msg[1], &rh[1], rh->data_len); LOG (GNUNET_ERROR_TYPE_DEBUG, - "Transmitting DNS resolution request to DNS service\n"); + "Transmitting DNS resolution request (ID %u) to DNS service\n", + rh->id); GNUNET_MQ_send (mq, env); rh->was_transmitted = GNUNET_YES; @@ -454,7 +466,7 @@ process_requests () */ static int check_response (void *cls, - const struct GNUNET_MessageHeader *msg) + const struct GNUNET_RESOLVER_ResponseMessage *msg) { (void) cls; (void) msg; @@ -474,11 +486,18 @@ check_response (void *cls, */ static void handle_response (void *cls, - const struct GNUNET_MessageHeader *msg) + const struct GNUNET_RESOLVER_ResponseMessage *msg) { struct GNUNET_RESOLVER_RequestHandle *rh = req_head; uint16_t size; char *nret; + uint32_t request_id = msg->id; + + for (; rh != NULL; rh = rh->next) + { + if (rh->id == request_id) + break; + } (void) cls; if (NULL == rh) @@ -490,8 +509,8 @@ handle_response (void *cls, reconnect (); return; } - size = ntohs (msg->size); - if (size == sizeof (struct GNUNET_MessageHeader)) + size = ntohs (msg->header.size); + if (size == sizeof (struct GNUNET_RESOLVER_ResponseMessage)) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Received empty response from DNS service\n"); @@ -532,7 +551,7 @@ handle_response (void *cls, const char *hostname; hostname = (const char *) &msg[1]; - if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0') + if (hostname[size - sizeof (struct GNUNET_RESOLVER_ResponseMessage) - 1] != '\0') { GNUNET_break (0); if (GNUNET_SYSERR != rh->was_transmitted) @@ -566,7 +585,7 @@ handle_response (void *cls, size_t ip_len; ip = &msg[1]; - ip_len = size - sizeof (struct GNUNET_MessageHeader); + ip_len = size - sizeof (struct GNUNET_RESOLVER_ResponseMessage); if (ip_len == sizeof (struct in_addr)) { memset (&v4, 0, sizeof (v4)); @@ -763,7 +782,7 @@ reconnect_task (void *cls) struct GNUNET_MQ_MessageHandler handlers[] = { GNUNET_MQ_hd_var_size (response, GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE, - struct GNUNET_MessageHeader, + struct GNUNET_RESOLVER_ResponseMessage, NULL), GNUNET_MQ_handler_end () }; @@ -926,6 +945,7 @@ GNUNET_RESOLVER_ip_get (const char *hostname, hostname); rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen); rh->af = af; + rh->id = ++last_request_id; rh->addr_callback = callback; rh->cls = callback_cls; GNUNET_memcpy (&rh[1], @@ -1072,6 +1092,7 @@ GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa, rh->name_callback = callback; rh->cls = cls; rh->af = sa->sa_family; + rh->id = ++last_request_id; rh->timeout = GNUNET_TIME_relative_to_absolute (timeout); GNUNET_memcpy (&rh[1], ip, diff --git a/src/util/test_resolver_api.c b/src/util/test_resolver_api.c index c89fad865..5a8f95093 100644 --- a/src/util/test_resolver_api.c +++ b/src/util/test_resolver_api.c @@ -258,6 +258,8 @@ run (void *cls, char *const *args, const char *cfgfile, /* Resolve the same using GNUNET */ GNUNET_RESOLVER_ip_get (ROOTSERVER_NAME, AF_INET, timeout, &check_rootserver_ip, cls); + GNUNET_RESOLVER_ip_get (ROOTSERVER_NAME, AF_INET, timeout, + &check_rootserver_ip, cls); /* * Success: forward lookups work as expected -- 2.25.1