Commit fdfb6869 authored by Lars Ingebrigtsen's avatar Lars Ingebrigtsen

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.
parent 44e235dd
......@@ -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);
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment