From: Lars Ingebrigtsen Date: Fri, 29 Jan 2016 23:49:18 +0000 (+0100) Subject: Implement asynchronous name resolution X-Git-Tag: emacs-26.0.90~2520^2~65 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=fdfb68690f75a77a32d286e17a39e2543e07e58c;p=emacs.git Implement asynchronous name resolution * process.c (Fmake_network_process): Do asynchronous DNS lookups if we have getaddrinfo_a and the user requests :nowait. (check_for_dns): New function. (wait_reading_process_output): Check for pending name resolution in the idle loop. * process.h: Add structure for async DNS. --- diff --git a/src/process.c b/src/process.c index b5d306f7de9..4f0c4e9bcdc 100644 --- a/src/process.c +++ b/src/process.c @@ -281,6 +281,10 @@ static int max_input_desc; /* Indexed by descriptor, gives the process (if any) for that descriptor. */ static Lisp_Object chan_process[FD_SETSIZE]; +#ifdef HAVE_GETADDRINFO_A +/* Pending DNS requests. */ +static Lisp_Object dns_process[FD_SETSIZE]; +#endif /* Alist of elements (NAME . PROCESS). */ static Lisp_Object Vprocess_alist; @@ -3012,7 +3016,7 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) Lisp_Object contact = p->childp; int optbits = 0; - /* Do this in case we never enter the for-loop below. */ + /* Do this in case we never enter the while-loop below. */ count1 = SPECPDL_INDEX (); s = -1; @@ -3028,7 +3032,7 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) addrlen = get_lisp_to_sockaddr_size (ip_address, &family); if (sa) free (sa); - sa = alloca (addrlen); + sa = xmalloc (addrlen); conv_lisp_to_sockaddr (family, ip_address, sa, addrlen); s = socket (family, p->socktype | SOCK_CLOEXEC, p->ai_protocol); @@ -3469,7 +3473,7 @@ usage: (make-network-process &rest ARGS) */) struct Lisp_Process *p; #ifdef HAVE_GETADDRINFO struct addrinfo ai, *res, *lres; - struct addrinfo hints; + struct addrinfo *hints; const char *portstring; char portbuf[128]; #endif /* HAVE_GETADDRINFO */ @@ -3485,6 +3489,9 @@ usage: (make-network-process &rest ARGS) */) int socktype; int family = -1; int ai_protocol = 0; +#ifdef HAVE_GETADDRINFO_A + struct gaicb *dns_request = NULL; +#endif ptrdiff_t count = SPECPDL_INDEX (); if (nargs == 0) @@ -3610,10 +3617,7 @@ usage: (make-network-process &rest ARGS) */) } #endif -#ifdef HAVE_GETADDRINFO - /* If we have a host, use getaddrinfo to resolve both host and service. - Otherwise, use getservbyname to lookup the service. */ - +#if defined (HAVE_GETADDRINFO) || defined (HAVE_GETADDRINFO_A) if (!NILP (host)) { @@ -3632,19 +3636,51 @@ usage: (make-network-process &rest ARGS) */) portstring = SSDATA (service); } + hints = xmalloc (sizeof (struct addrinfo)); + memset (hints, 0, sizeof (struct addrinfo)); + hints->ai_flags = 0; + hints->ai_family = family; + hints->ai_socktype = socktype; + hints->ai_protocol = 0; + } + +#endif + +#ifdef HAVE_GETADDRINFO_A + if (!NILP (Fplist_get (contact, QCnowait)) && + !NILP (host)) + { + struct gaicb **reqs = xmalloc (sizeof (struct gaicb*)); + + dns_request = xmalloc (sizeof (struct gaicb)); + reqs[0] = dns_request; + dns_request->ar_name = strdup (SSDATA (host)); + dns_request->ar_service = strdup (portstring); + dns_request->ar_request = hints; + dns_request->ar_result = NULL; + + ret = getaddrinfo_a (GAI_NOWAIT, reqs, 1, NULL); + if (ret) + error ("%s/%s getaddrinfo_a error %d", SSDATA (host), portstring, ret); + + goto open_socket; + } +#endif /* HAVE_GETADDRINFO_A */ + +#ifdef HAVE_GETADDRINFO + /* If we have a host, use getaddrinfo to resolve both host and service. + Otherwise, use getservbyname to lookup the service. */ + + if (!NILP (host)) + { immediate_quit = 1; QUIT; - memset (&hints, 0, sizeof (hints)); - hints.ai_flags = 0; - hints.ai_family = family; - hints.ai_socktype = socktype; - hints.ai_protocol = 0; #ifdef HAVE_RES_INIT res_init (); #endif - ret = getaddrinfo (SSDATA (host), portstring, &hints, &res); + ret = getaddrinfo (SSDATA (host), portstring, hints, &res); if (ret) #ifdef HAVE_GAI_STRERROR error ("%s/%s %s", SSDATA (host), portstring, gai_strerror (ret)); @@ -3663,6 +3699,7 @@ usage: (make-network-process &rest ARGS) */) } ip_addresses = Fnreverse (ip_addresses); + xfree (hints); goto open_socket; } @@ -3749,6 +3786,7 @@ usage: (make-network-process &rest ARGS) */) p->port = port; p->socktype = socktype; p->ai_protocol = ai_protocol; + p->dns_request = NULL; unbind_to (count, Qnil); @@ -3774,7 +3812,26 @@ usage: (make-network-process &rest ARGS) */) #endif } - connect_network_socket (proc, ip_addresses); + /* If we're doing async address resolution, the list of addresses + here will be nil, so we postpone connecting to the server. */ + if (NILP (ip_addresses)) + { + int channel; + + p->dns_request = dns_request; + p->status = Qconnect; + for (channel = 0; channel < FD_SETSIZE; ++channel) + if (NILP (dns_process[channel])) + { + dns_process[channel] = proc; + break; + } + } + else + { + connect_network_socket (proc, ip_addresses); + } + return proc; } @@ -4479,6 +4536,40 @@ server_accept_connection (Lisp_Object server, int channel) exec_sentinel (proc, concat3 (open_from, host_string, nl)); } +#ifdef HAVE_GETADDRINFO_A +static int +check_for_dns (Lisp_Object proc) +{ + struct Lisp_Process *p = XPROCESS (proc); + Lisp_Object ip_addresses = Qnil; + int ret = 0; + + ret = gai_error (p->dns_request); + if (ret == EAI_INPROGRESS) + return 0; + + /* We got a response. */ + if (ret == 0) + { + struct addrinfo *res; + + for (res = p->dns_request->ar_result; res; res = res->ai_next) + { + ip_addresses = Fcons (conv_sockaddr_to_lisp + (res->ai_addr, res->ai_addrlen), + ip_addresses); + } + + ip_addresses = Fnreverse (ip_addresses); + connect_network_socket (proc, ip_addresses); + return 1; + } + + pset_status (p, Qfailed); + return 0; +} +#endif /* HAVE_GETADDRINFO_A */ + /* This variable is different from waiting_for_input in keyboard.c. It is used to communicate to a lisp process-filter/sentinel (via the function Fwaiting_for_user_input_p below) whether Emacs was waiting @@ -4604,6 +4695,22 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell))) break; +#ifdef HAVE_GETADDRINFO_A + for (channel = 0; channel < FD_SETSIZE; ++channel) + { + if (! NILP (dns_process[channel])) + { + struct Lisp_Process *p = XPROCESS (dns_process[channel]); + if (p && p->dns_request && + (! wait_proc || p == wait_proc) && + check_for_dns (dns_process[channel])) + { + dns_process[channel] = Qnil; + } + } + } +#endif /* HAVE_GETADDRINFO_A */ + /* Compute time from now till when time limit is up. */ /* Exit if already run out. */ if (wait == TIMEOUT) @@ -7450,6 +7557,9 @@ init_process_emacs (void) { chan_process[i] = Qnil; proc_buffered_char[i] = -1; +#ifdef HAVE_GETADDRINFO_A + dns_process[i] = Qnil; +#endif } memset (proc_decode_coding_system, 0, sizeof proc_decode_coding_system); memset (proc_encode_coding_system, 0, sizeof proc_encode_coding_system); diff --git a/src/process.h b/src/process.h index e2e6ca92984..684434c1748 100644 --- a/src/process.h +++ b/src/process.h @@ -161,14 +161,26 @@ struct Lisp_Process flag indicates that `raw_status' contains a new status that still needs to be synced to `status'. */ bool_bf raw_status_new : 1; + /* Whether this is a nonblocking socket. */ bool_bf is_non_blocking_client : 1; + /* Whether this is a server or a client socket. */ bool_bf is_server : 1; int raw_status; + /* The length of the socket backlog. */ int backlog; + /* The port number. */ int port; + /* The socket type. */ int socktype; + /* The socket protocol. */ int ai_protocol; +#ifdef HAVE_GETADDRINFO_A + /* Whether the socket is waiting for response from an asynchronous + DNS call. */ + struct gaicb* dns_request; +#endif + #ifdef HAVE_GNUTLS gnutls_initstage_t gnutls_initstage; gnutls_session_t gnutls_state;