diff --git a/configure.ac b/configure.ac index 5a6a72a..94c2947 100644 --- a/configure.ac +++ b/configure.ac @@ -2436,6 +2436,12 @@ if test "${HAVE_GETADDRINFO_A}" = "yes"; then AC_SUBST(GETADDRINFO_A_LIBS) fi +AC_CHECK_HEADER(CFNetwork/CFHost.h, + dnl XXX: Do we need to test for existence here? + LIBS="$LIBS -framework CFNetwork" + AC_DEFINE(HAVE_CFHOSTCREATEWITHNAME, 1, +[Define to 1 if you have CFHostCreateWithName for asynchronous DNS resolution.])) + HAVE_GTK=no GTK_OBJ= gtk_term_header=$term_header diff --git a/src/process.c b/src/process.c index 2b674e5..de27c31 100644 --- a/src/process.c +++ b/src/process.c @@ -87,6 +87,10 @@ along with GNU Emacs. If not, see . */ #include #include +#ifdef HAVE_CFHOSTCREATEWITHNAME +#include +#endif + #endif /* subprocesses */ #include "systime.h" @@ -125,6 +129,10 @@ along with GNU Emacs. If not, see . */ #define ASYNC_RETRY_NSEC 100000000 #endif +#ifdef HAVE_CFHOSTCREATEWITHNAME +static void host_client_callback (CFHostRef, CFHostInfoType, const CFStreamError *, void *); +#endif + #ifdef WINDOWSNT extern int sys_select (int, fd_set *, fd_set *, fd_set *, struct timespec *, void *); @@ -733,15 +741,24 @@ remove_process (register Lisp_Object proc) deactivate_process (proc); } -#ifdef HAVE_GETADDRINFO_A +#if defined HAVE_GETADDRINFO_A || defined HAVE_CFHOSTCREATEWITHNAME static void free_dns_request (Lisp_Object proc) { struct Lisp_Process *p = XPROCESS (proc); +#ifdef HAVE_GETADDRINFO_A if (p->dns_request->ar_result) freeaddrinfo (p->dns_request->ar_result); xfree (p->dns_request); +#elif defined HAVE_CFHOSTCREATEWITHNAME + CFRunLoopRef run_loop = CFRunLoopGetCurrent (); + CFHostUnscheduleFromRunLoop (p->dns_request, run_loop, kCFRunLoopDefaultMode); + CFHostCancelInfoResolution (p->dns_request, kCFHostAddresses); + CFRelease (p->dns_request); + xfree (p->dns_error); + p->dns_error = NULL; +#endif p->dns_request = NULL; } #endif @@ -853,6 +870,10 @@ nil, indicating the current buffer's process. */) if (canceled) free_dns_request (process); } +#elif defined HAVE_CFHOSTCREATEWITHNAME + if (p->dns_request) + /* free_dns_request will cancel the request for us. */ + free_dns_request (process); #endif p->raw_status_new = 0; @@ -3611,6 +3632,9 @@ usage: (make-network-process &rest ARGS) */) int ai_protocol = 0; #ifdef HAVE_GETADDRINFO_A struct gaicb *dns_request = NULL; +#elif defined HAVE_CFHOSTCREATEWITHNAME + CFHostRef dns_request = NULL; + CFStreamError *dns_error = NULL; #endif ptrdiff_t count = SPECPDL_INDEX (); @@ -3791,7 +3815,41 @@ usage: (make-network-process &rest ARGS) */) goto open_socket; } -#endif /* HAVE_GETADDRINFO_A */ +#elif defined(HAVE_CFHOSTCREATEWITHNAME) + if (!NILP (host) && !NILP (Fplist_get (contact, QCnowait))) + { + CFStringRef stringref; + + stringref = CFStringCreateWithCString (NULL, SSDATA (host), + kCFStringEncodingASCII); + if (! stringref) + error ("%s CFStringCreateWithCString error", SSDATA (host)); + /* XXX: when should I deallocate stringref? */ + dns_request = CFHostCreateWithName (NULL, stringref); + if (! dns_request) + error ("%s CFHostCreateWithName error", SSDATA (host)); + + dns_error = xmalloc (sizeof *dns_error); + memset (dns_error, 0, sizeof *dns_error); + CFHostClientContext client_context; + memset (&client_context, 0, sizeof client_context); + client_context.version = 0; + client_context.info = (void*)dns_error; + if (! CFHostSetClient (dns_request, host_client_callback, &client_context)) + error ("%s CFHostSetClient error", SSDATA (host)); + + CFRunLoopRef run_loop = CFRunLoopGetCurrent (); + CFHostScheduleWithRunLoop (dns_request, run_loop, kCFRunLoopDefaultMode); + + /* TODO: need error information? */ + if (! CFHostStartInfoResolution (dns_request, kCFHostAddresses, NULL)) + error ("%s CFHostStartInfoResolution error", SSDATA (host)); + + /* Unlike getaddrinfo_a, this doesn't resolve the service name + for us. */ + goto resolve_service; + } +#endif /* HAVE_GETADDRINFO_A | HAVE_CFHOSTCREATEWITHNAME */ /* If we have a host, use getaddrinfo to resolve both host and service. Otherwise, use getservbyname to lookup the service. */ @@ -3842,6 +3900,8 @@ usage: (make-network-process &rest ARGS) */) /* No hostname has been specified (e.g., a local server process). */ + resolve_service: + if (EQ (service, Qt)) port = 0; else if (INTEGERP (service)) @@ -3903,6 +3963,9 @@ usage: (make-network-process &rest ARGS) */) p->ai_protocol = ai_protocol; #ifdef HAVE_GETADDRINFO_A p->dns_request = NULL; +#elif defined HAVE_CFHOSTCREATEWITHNAME + p->dns_request = NULL; + p->dns_error = NULL; #endif #ifdef HAVE_GNUTLS tem = Fplist_get (contact, QCtls_parameters); @@ -3930,12 +3993,15 @@ usage: (make-network-process &rest ARGS) */) && !NILP (Fplist_get (contact, QCnowait))) p->is_non_blocking_client = true; -#ifdef HAVE_GETADDRINFO_A +#if defined HAVE_GETADDRINFO_A || defined HAVE_CFHOSTCREATEWITHNAME /* With async address resolution, the list of addresses is empty, so postpone connecting to the server. */ if (!p->is_server && NILP (ip_addresses)) { p->dns_request = dns_request; +#ifdef HAVE_CFHOSTCREATEWITHNAME + p->dns_error = dns_error; +#endif p->status = Qconnect; return proc; } @@ -4644,7 +4710,21 @@ server_accept_connection (Lisp_Object server, int channel) exec_sentinel (proc, concat3 (open_from, host_string, nl)); } -#ifdef HAVE_GETADDRINFO_A +#ifdef HAVE_CFHOSTCREATEWITHNAME +static void +host_client_callback (CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError *error, void *info) +{ + if (error) + { + CFStreamError *dns_error = (CFStreamError *)info; + /* XXX: how can I know that the process hasn't been deallocated? */ + dns_error->domain = error->domain; + dns_error->error = error->error; + } +} +#endif + +#if defined HAVE_GETADDRINFO_A || defined HAVE_CFHOSTCREATEWITHNAME static Lisp_Object check_for_dns (Lisp_Object proc) { @@ -4655,6 +4735,7 @@ check_for_dns (Lisp_Object proc) if (! p->dns_request) return Qnil; +#ifdef HAVE_GETADDRINFO_A int ret = gai_error (p->dns_request); if (ret == EAI_INPROGRESS) return Qt; @@ -4683,6 +4764,55 @@ check_for_dns (Lisp_Object proc) build_string (p->dns_request->ar_name), build_string (" failed"))))); } +#elif defined HAVE_CFHOSTCREATEWITHNAME + + Boolean has_been_resolved; + CFArrayRef res; + + res = CFHostGetAddressing (p->dns_request, &has_been_resolved); + + /* XXX: if has_been_resolved is false, does that mean there was an error? */ + if (has_been_resolved && res) + { + CFIndex n; + n = CFArrayGetCount (res); + for (CFIndex i = 0; i < n; i++) + { + CFDataRef entry = CFArrayGetValueAtIndex (res, i); + const struct sockaddr *addr = (const struct sockaddr *) CFDataGetBytePtr (entry); + CFIndex len = CFDataGetLength (entry); + /* Need to insert the port number. */ + struct sockaddr *addr_with_port = xmalloc (len); + memcpy (addr_with_port, addr, len); + /* XXX: this should work for IPv4 and IPv6, I think */ + ((struct sockaddr_in*)addr_with_port)->sin_port = htons (p->port); + ip_addresses = Fcons (conv_sockaddr_to_lisp + (addr_with_port, len), + ip_addresses); + xfree (addr_with_port); + + ip_addresses = Fnreverse (ip_addresses); + } + } + else if (p->dns_error->domain) + { + add_to_log ("async DNS resulution error"); /* XXX */ + deactivate_process (proc); + pset_status (p, (list2 + (Qfailed, + concat2 ( + concat2 (build_string ("Name lookup failed; error domain "), + Fnumber_to_string (make_number (p->dns_error->domain))), + concat2 (build_string (", error number "), + Fnumber_to_string (make_number (p->dns_error->error))))))); + } + else + { + /* Still in progress ??? */ + add_to_log ("Name resolution still in progress"); /* XXX */ + return Qt; + } +#endif free_dns_request (proc); @@ -4693,7 +4823,7 @@ check_for_dns (Lisp_Object proc) return ip_addresses; } -#endif /* HAVE_GETADDRINFO_A */ +#endif /* HAVE_GETADDRINFO_A || HAVE_CFHOSTCREATEWITHNAME */ static void wait_for_socket_fds (Lisp_Object process, char const *name) @@ -4805,9 +4935,10 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, Lisp_Object proc; struct timespec timeout, end_time, timer_delay; struct timespec got_output_end_time = invalid_timespec (); +#undef INFINITY enum { MINIMUM = -1, TIMEOUT, INFINITY } wait; int got_some_output = -1; -#if defined HAVE_GETADDRINFO_A || defined HAVE_GNUTLS +#if defined HAVE_GETADDRINFO_A || defined HAVE_CFHOSTCREATEWITHNAME || defined HAVE_GNUTLS bool retry_for_async; #endif ptrdiff_t count = SPECPDL_INDEX (); @@ -4857,7 +4988,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell))) break; -#if defined HAVE_GETADDRINFO_A || defined HAVE_GNUTLS +#if defined HAVE_GETADDRINFO_A || defined HAVE_CFHOSTCREATEWITHNAME || defined HAVE_GNUTLS { Lisp_Object process_list_head, aproc; struct Lisp_Process *p; @@ -4869,7 +5000,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, if (! wait_proc || p == wait_proc) { -#ifdef HAVE_GETADDRINFO_A +#if defined HAVE_GETADDRINFO_A || defined HAVE_CFHOSTCREATEWITHNAME /* Check for pending DNS requests. */ if (p->dns_request) { diff --git a/src/process.h b/src/process.h index bf1eadc..e47a53e 100644 --- a/src/process.h +++ b/src/process.h @@ -25,6 +25,10 @@ along with GNU Emacs. If not, see . */ #include +#ifdef HAVE_CFHOSTCREATEWITHNAME +#include +#endif + #ifdef HAVE_GNUTLS #include "gnutls.h" #endif @@ -180,6 +184,9 @@ struct Lisp_Process /* Whether the socket is waiting for response from an asynchronous DNS call. */ struct gaicb *dns_request; +#elif defined (HAVE_CFHOSTCREATEWITHNAME) + CFHostRef dns_request; + CFStreamError *dns_error; #endif #ifdef HAVE_GNUTLS