]> code.delx.au - gnu-emacs/blobdiff - src/process.c
Don't block in set-process-window-size
[gnu-emacs] / src / process.c
index 1329d968e281c76ee5ddb53eaca8463d759b5321..9c09aeefa6be62c724eaee540d4cb275e7259a74 100644 (file)
@@ -281,6 +281,11 @@ 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_processes;
+static void wait_for_socket_fds (Lisp_Object process, char *name);
+#endif
 
 /* Alist of elements (NAME . PROCESS).  */
 static Lisp_Object Vprocess_alist;
@@ -381,11 +386,6 @@ pset_sentinel (struct Lisp_Process *p, Lisp_Object val)
   p->sentinel = NILP (val) ? Qinternal_default_process_sentinel : val;
 }
 static void
-pset_status (struct Lisp_Process *p, Lisp_Object val)
-{
-  p->status = val;
-}
-static void
 pset_tty_name (struct Lisp_Process *p, Lisp_Object val)
 {
   p->tty_name = val;
@@ -711,6 +711,7 @@ make_process (Lisp_Object name)
 
 #ifdef HAVE_GNUTLS
   p->gnutls_initstage = GNUTLS_STAGE_EMPTY;
+  p->gnutls_boot_parameters = Qnil;
 #endif
 
   /* If name is already in use, modify it until it is unused.  */
@@ -1029,6 +1030,7 @@ The string argument is normally a multibyte string, except:
   struct Lisp_Process *p;
 
   CHECK_PROCESS (process);
+
   p = XPROCESS (process);
 
   /* Don't signal an error if the process's input file descriptor
@@ -1117,7 +1119,8 @@ DEFUN ("set-process-window-size", Fset_process_window_size,
   CHECK_RANGED_INTEGER (height, 0, USHRT_MAX);
   CHECK_RANGED_INTEGER (width, 0, USHRT_MAX);
 
-  if (XPROCESS (process)->infd < 0
+  if (NETCONN_P (process)
+      || XPROCESS (process)->infd < 0
       || (set_window_size (XPROCESS (process)->infd,
                           XINT (height), XINT (width))
          < 0))
@@ -1194,6 +1197,10 @@ list of keywords.  */)
   contact = XPROCESS (process)->childp;
 
 #ifdef DATAGRAM_SOCKETS
+
+  if (NETCONN_P (process))
+    wait_for_socket_fds (process, "process-contact");
+
   if (DATAGRAM_CONN_P (process)
       && (EQ (key, Qt) || EQ (key, QCremote)))
     contact = Fplist_put (contact, QCremote,
@@ -2372,7 +2379,7 @@ conv_lisp_to_sockaddr (int family, Lisp_Object address, struct sockaddr *sa, int
        {
          struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
          uint16_t *ip6 = (uint16_t *)&sin6->sin6_addr;
-         len = sizeof (sin6->sin6_addr) + 1;
+         len = sizeof (sin6->sin6_addr) / 2 + 1;
          hostport = XINT (p->contents[--len]);
          sin6->sin6_port = htons (hostport);
          for (i = 0; i < len; i++)
@@ -2423,6 +2430,9 @@ DEFUN ("process-datagram-address", Fprocess_datagram_address, Sprocess_datagram_
 
   CHECK_PROCESS (process);
 
+  if (NETCONN_P (process))
+    wait_for_socket_fds (process, "process-datagram-address");
+
   if (!DATAGRAM_CONN_P (process))
     return Qnil;
 
@@ -2442,6 +2452,9 @@ Returns nil upon error setting address, ADDRESS otherwise.  */)
 
   CHECK_PROCESS (process);
 
+  if (NETCONN_P (process))
+    wait_for_socket_fds (process, "set-process-datagram-address");
+
   if (!DATAGRAM_CONN_P (process))
     return Qnil;
 
@@ -2610,6 +2623,8 @@ OPTION is not a supported option, return nil instead; otherwise return t.  */)
   if (!NETCONN1_P (p))
     error ("Process is not a network process");
 
+  wait_for_socket_fds (process, "set-network-process-option");
+
   s = p->infd;
   if (s < 0)
     error ("Process is not running");
@@ -2904,11 +2919,14 @@ usage:  (make-serial-process &rest ARGS)  */)
   return proc;
 }
 
-void set_network_socket_coding_system (Lisp_Object proc) {
+void set_network_socket_coding_system (Lisp_Object proc)
+{
   Lisp_Object tem;
   struct Lisp_Process *p = XPROCESS (proc);
   Lisp_Object contact = p->childp;
   Lisp_Object service, host, name;
+  Lisp_Object coding_systems = Qt;
+  Lisp_Object val;
 
   service = Fplist_get (contact, QCservice);
   host = Fplist_get (contact, QChost);
@@ -2918,76 +2936,71 @@ void set_network_socket_coding_system (Lisp_Object proc) {
   if (!NILP (tem) && (!CONSP (tem) || !CONSP (XCDR (tem))))
     tem = Qnil;  /* No error message (too late!).  */
 
-  {
-    /* Setup coding systems for communicating with the network stream.  */
-    /* Qt denotes we have not yet called Ffind_operation_coding_system.  */
-    Lisp_Object coding_systems = Qt;
-    Lisp_Object val;
+  /* Setup coding systems for communicating with the network stream.  */
+  /* Qt denotes we have not yet called Ffind_operation_coding_system.  */
 
-    if (!NILP (tem))
-      {
-       val = XCAR (XCDR (tem));
-       if (CONSP (val))
-         val = XCAR (val);
-      }
-    else if (!NILP (Vcoding_system_for_read))
-      val = Vcoding_system_for_read;
-    else if ((!NILP (p->buffer) &&
-             NILP (BVAR (XBUFFER (p->buffer), enable_multibyte_characters)))
-            || (NILP (p->buffer) && NILP (BVAR (&buffer_defaults, enable_multibyte_characters))))
-      /* We dare not decode end-of-line format by setting VAL to
-        Qraw_text, because the existing Emacs Lisp libraries
-        assume that they receive bare code including a sequence of
-        CR LF.  */
-      val = Qnil;
-    else
-      {
-       if (NILP (host) || NILP (service))
-         coding_systems = Qnil;
-       else
-         coding_systems = CALLN (Ffind_operation_coding_system,
-                                 Qopen_network_stream, name, p->buffer,
-                                 host, service);
-       if (CONSP (coding_systems))
-         val = XCAR (coding_systems);
-       else if (CONSP (Vdefault_process_coding_system))
-         val = XCAR (Vdefault_process_coding_system);
-       else
-         val = Qnil;
-      }
-    pset_decode_coding_system (p, val);
+  if (!NILP (tem))
+    {
+      val = XCAR (XCDR (tem));
+      if (CONSP (val))
+       val = XCAR (val);
+    }
+  else if (!NILP (Vcoding_system_for_read))
+    val = Vcoding_system_for_read;
+  else if ((!NILP (p->buffer) &&
+           NILP (BVAR (XBUFFER (p->buffer), enable_multibyte_characters)))
+          || (NILP (p->buffer) && NILP (BVAR (&buffer_defaults, enable_multibyte_characters))))
+    /* We dare not decode end-of-line format by setting VAL to
+       Qraw_text, because the existing Emacs Lisp libraries
+       assume that they receive bare code including a sequence of
+       CR LF.  */
+    val = Qnil;
+  else
+    {
+      if (NILP (host) || NILP (service))
+       coding_systems = Qnil;
+      else
+       coding_systems = CALLN (Ffind_operation_coding_system,
+                               Qopen_network_stream, name, p->buffer,
+                               host, service);
+      if (CONSP (coding_systems))
+       val = XCAR (coding_systems);
+      else if (CONSP (Vdefault_process_coding_system))
+       val = XCAR (Vdefault_process_coding_system);
+      else
+       val = Qnil;
+    }
+  pset_decode_coding_system (p, val);
 
-    if (!NILP (tem))
-      {
-       val = XCAR (XCDR (tem));
-       if (CONSP (val))
-         val = XCDR (val);
-      }
-    else if (!NILP (Vcoding_system_for_write))
-      val = Vcoding_system_for_write;
-    else if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
-      val = Qnil;
-    else
-      {
-       if (EQ (coding_systems, Qt))
-         {
-           if (NILP (host) || NILP (service))
-             coding_systems = Qnil;
-           else
-             coding_systems = CALLN (Ffind_operation_coding_system,
-                                     Qopen_network_stream, name, p->buffer,
-                                     host, service);
-         }
-       if (CONSP (coding_systems))
-         val = XCDR (coding_systems);
-       else if (CONSP (Vdefault_process_coding_system))
-         val = XCDR (Vdefault_process_coding_system);
-       else
-         val = Qnil;
-      }
-    pset_encode_coding_system (p, val);
-  }
-  setup_process_coding_systems (proc);
+  if (!NILP (tem))
+    {
+      val = XCAR (XCDR (tem));
+      if (CONSP (val))
+       val = XCDR (val);
+    }
+  else if (!NILP (Vcoding_system_for_write))
+    val = Vcoding_system_for_write;
+  else if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
+    val = Qnil;
+  else
+    {
+      if (EQ (coding_systems, Qt))
+       {
+         if (NILP (host) || NILP (service))
+           coding_systems = Qnil;
+         else
+           coding_systems = CALLN (Ffind_operation_coding_system,
+                                   Qopen_network_stream, name, p->buffer,
+                                   host, service);
+       }
+      if (CONSP (coding_systems))
+       val = XCDR (coding_systems);
+      else if (CONSP (Vdefault_process_coding_system))
+       val = XCDR (Vdefault_process_coding_system);
+      else
+       val = Qnil;
+    }
+  pset_encode_coding_system (p, val);
 
   pset_decoding_buf (p, empty_unibyte_string);
   p->decoding_carryover = 0;
@@ -2997,35 +3010,38 @@ void set_network_socket_coding_system (Lisp_Object proc) {
     = !(!NILP (tem) || NILP (p->buffer) || !inherit_process_coding_system);
 }
 
-void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) {
+void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
+{
   ptrdiff_t count = SPECPDL_INDEX ();
   ptrdiff_t count1;
   int s = -1, outch, inch;
   int xerrno = 0;
   Lisp_Object ip_address;
   int family;
-  struct sockaddr *sa;
+  struct sockaddr *sa = NULL;
   int ret;
   int addrlen;
   struct Lisp_Process *p = XPROCESS (proc);
   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;
 
   while (!NILP (ip_addresses))
     {
-      ip_address = Fcar (ip_addresses);
-      ip_addresses = Fcdr (ip_addresses);
+      ip_address = XCAR (ip_addresses);
+      ip_addresses = XCDR (ip_addresses);
 
 #ifdef WINDOWSNT
     retry_connect:
 #endif
 
       addrlen = get_lisp_to_sockaddr_size (ip_address, &family);
-      sa = alloca (addrlen);
+      if (sa)
+       free (sa);
+      sa = xmalloc (addrlen);
       conv_lisp_to_sockaddr (family, ip_address, sa, addrlen);
 
       s = socket (family, p->socktype | SOCK_CLOEXEC, p->ai_protocol);
@@ -3063,13 +3079,14 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) {
       {
        Lisp_Object params = contact, key, val;
 
-       while (!NILP (params)) {
-         key = Fcar (params);
-         params = Fcdr (params);
-         val = Fcar (params);
-         params = Fcdr (params);
-         optbits |= set_socket_option (s, key, val);
-       }
+       while (!NILP (params))
+         {
+           key = XCAR (params);
+           params = XCDR (params);
+           val = XCAR (params);
+           params = XCDR (params);
+           optbits |= set_socket_option (s, key, val);
+         }
       }
 
       if (p->is_server)
@@ -3101,6 +3118,9 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) {
                  Lisp_Object service;
                  service = make_number (ntohs (sa1.sin_port));
                  contact = Fplist_put (contact, QCservice, service);
+                 // Save the port number so that we can stash it in
+                 // the process object later.
+                 ((struct sockaddr_in *)sa)->sin_port = sa1.sin_port;
                }
            }
 #endif
@@ -3211,7 +3231,7 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) {
        }
 #endif
 
-      contact = Fplist_put (contact, p->is_server? QCremote: QClocal,
+      contact = Fplist_put (contact, p->is_server? QClocal: QCremote,
                            conv_sockaddr_to_lisp (sa, addrlen));
 #ifdef HAVE_GETSOCKNAME
       if (!p->is_server)
@@ -3297,9 +3317,78 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) {
   if (inch > max_process_desc)
     max_process_desc = inch;
 
-  set_network_socket_coding_system (proc);
+  setup_process_coding_systems (proc);
+
+#ifdef HAVE_GNUTLS
+  /* Continue the asynchronous connection. */
+  if (!NILP (p->gnutls_boot_parameters))
+    {
+      Lisp_Object boot, params = p->gnutls_boot_parameters;
+
+      boot = Fgnutls_boot (proc, XCAR (params), XCDR (params));
+      p->gnutls_boot_parameters = Qnil;
+
+      if (NILP (boot) || STRINGP (boot) ||
+         p->gnutls_initstage != GNUTLS_STAGE_READY)
+       {
+         deactivate_process (proc);
+         if (NILP (boot))
+           pset_status (p, list2 (Qfailed,
+                                  build_string ("TLS negotiation failed")));
+         else
+           pset_status (p, list2 (Qfailed, boot));
+       }
+      else
+       {
+         Lisp_Object result = Qt;
+
+         if (!NILP (Ffboundp (Qnsm_verify_connection)))
+           result = call3 (Qnsm_verify_connection,
+                           proc,
+                           Fplist_get (contact, QChost),
+                           Fplist_get (contact, QCservice));
+
+         if (NILP (result))
+           {
+             pset_status (p, list2 (Qfailed,
+                                    build_string ("The Network Security Manager stopped the connections")));
+             deactivate_process (proc);
+           }
+         else
+           {
+             /* If we cleared the connection wait mask before we did
+                the TLS setup, then we have to say that the process
+                is finally "open" here. */
+             if (! FD_ISSET (p->outfd, &connect_wait_mask))
+               {
+                 pset_status (p, Qrun);
+                 /* Execute the sentinel here.  If we had relied on
+                    status_notify to do it later, it will read input
+                    from the process before calling the sentinel.  */
+                 exec_sentinel (proc, build_string ("open\n"));
+               }
+           }
+       }
+    }
+#endif
+
 }
 
+#ifndef HAVE_GETADDRINFO
+static Lisp_Object
+conv_numerical_to_lisp (unsigned char *number, unsigned int length, int port)
+{
+  Lisp_Object address = Fmake_vector (make_number (length + 1), Qnil);
+  register struct Lisp_Vector *p = XVECTOR (address);
+  int i;
+
+  p->contents[length] = make_number (port);
+  for (i = 0; i < length; i++)
+    p->contents[i] = make_number (*(number + i));
+
+  return address;
+}
+#endif
 
 /* Create a network stream/datagram client/server process.  Treated
    exactly like a normal process when reading and writing.  Primary
@@ -3379,11 +3468,12 @@ system used for both reading and writing for this process.  If CODING
 is a cons (DECODING . ENCODING), DECODING is used for reading, and
 ENCODING is used for writing.
 
-:nowait BOOL -- If BOOL is non-nil for a stream type client process,
-return without waiting for the connection to complete; instead, the
-sentinel function will be called with second arg matching "open" (if
-successful) or "failed" when the connect completes.  Default is to use
-a blocking connect (i.e. wait) for stream type connections.
+:nowait BOOL -- If NOWAIT is non-nil for a stream type client
+process, return without waiting for the connection to complete;
+instead, the sentinel function will be called with second arg matching
+"open" (if successful) or "failed" when the connect completes.
+Default is to use a blocking connect (i.e. wait) for stream type
+connections.
 
 :noquery BOOL -- Query the user unless BOOL is non-nil, and process is
 running when Emacs is exited.
@@ -3411,6 +3501,12 @@ and MESSAGE is a string.
 
 :plist PLIST -- Install PLIST as the new process's initial plist.
 
+:tls-parameters LIST -- is a list that should be supplied if you're
+opening a TLS connection.  The first element is the TLS type (either
+`gnutls-x509pki' or `gnutls-anon'), and the remaining elements should
+be a keyword list accepted by gnutls-boot (as returned by
+`gnutls-boot-parameters').
+
 :server QLEN -- if QLEN is non-nil, create a server process for the
 specified FAMILY, SERVICE, and connection type (stream or datagram).
 If QLEN is an integer, it is used as the max. length of the server's
@@ -3464,17 +3560,15 @@ usage: (make-network-process &rest ARGS)  */)
   Lisp_Object proc;
   Lisp_Object contact;
   struct Lisp_Process *p;
-#ifdef HAVE_GETADDRINFO
-  struct addrinfo ai, *res, *lres;
-  struct addrinfo hints;
+#if defined(HAVE_GETADDRINFO) || defined(HAVE_GETADDRINFO_A)
+  struct addrinfo *hints;
   const char *portstring;
   char portbuf[128];
-#endif /* HAVE_GETADDRINFO */
+#endif
 #ifdef HAVE_LOCAL_SOCKETS
   struct sockaddr_un address_un;
 #endif
   int port = 0;
-  int ret = 0;
   Lisp_Object tem;
   Lisp_Object name, buffer, host, service, address;
   Lisp_Object filter, sentinel;
@@ -3482,6 +3576,9 @@ usage: (make-network-process &rest ARGS)  */)
   int socktype;
   int family = -1;
   int ai_protocol = 0;
+#ifdef HAVE_GETADDRINFO_A
+  struct gaicb **dns_requests = NULL;
+#endif
   ptrdiff_t count = SPECPDL_INDEX ();
 
   if (nargs == 0)
@@ -3517,12 +3614,6 @@ usage: (make-network-process &rest ARGS)  */)
 
   CHECK_STRING (name);
 
-  /* Initialize addrinfo structure in case we don't use getaddrinfo.  */
-  ai.ai_socktype = socktype;
-  ai.ai_protocol = 0;
-  ai.ai_next = NULL;
-  res = &ai;
-
   /* :local ADDRESS or :remote ADDRESS */
   tem = Fplist_get (contact, QCserver);
   if (!NILP (tem))
@@ -3570,7 +3661,16 @@ usage: (make-network-process &rest ARGS)  */)
 
   /* :host HOST -- hostname, ip address, or 'local for localhost.  */
   host = Fplist_get (contact, QChost);
-  if (!NILP (host))
+  if (NILP (host))
+    {
+      /* The "connection" function gets it bind info from the address we're
+        given, so use this dummy address if nothing is specified. */
+#ifdef HAVE_LOCAL_SOCKETS
+      if (family != AF_LOCAL)
+#endif
+       host = build_string ("127.0.0.1");
+    }
+  else
     {
       if (EQ (host, Qlocal))
        /* Depending on setup, "localhost" may map to different IPv4 and/or
@@ -3607,10 +3707,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))
     {
 
@@ -3629,19 +3726,54 @@ usage: (make-network-process &rest ARGS)  */)
          portstring = SSDATA (service);
        }
 
+      hints = xzalloc (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))
+    {
+      int ret;
+
+      printf("Async DNS for '%s'\n", SSDATA (host));
+      dns_requests = xmalloc (sizeof (struct gaicb*));
+      dns_requests[0] = xmalloc (sizeof (struct gaicb));
+      dns_requests[0]->ar_name = strdup (SSDATA (host));
+      dns_requests[0]->ar_service = strdup (portstring);
+      dns_requests[0]->ar_request = hints;
+      dns_requests[0]->ar_result = NULL;
+
+      ret = getaddrinfo_a (GAI_NOWAIT, dns_requests, 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))
+    {
+      struct addrinfo *res, *lres;
+      int ret;
+
       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));
@@ -3656,9 +3788,13 @@ usage: (make-network-process &rest ARGS)  */)
                                (lres->ai_addr, lres->ai_addrlen),
                                ip_addresses);
          ai_protocol = lres->ai_protocol;
-         family = lres->ai_family;
        }
 
+      ip_addresses = Fnreverse (ip_addresses);
+
+      freeaddrinfo (res);
+      xfree (hints);
+
       goto open_socket;
     }
 #endif /* HAVE_GETADDRINFO */
@@ -3669,7 +3805,7 @@ usage: (make-network-process &rest ARGS)  */)
   if (EQ (service, Qt))
     port = 0;
   else if (INTEGERP (service))
-    port = htons ((unsigned short) XINT (service));
+    port = (unsigned short) XINT (service);
   else
     {
       struct servent *svc_info;
@@ -3678,7 +3814,7 @@ usage: (make-network-process &rest ARGS)  */)
                                (socktype == SOCK_DGRAM ? "udp" : "tcp"));
       if (svc_info == 0)
        error ("Unknown service: %s", SDATA (service));
-      port = svc_info->s_port;
+      port = ntohs (svc_info->s_port);
     }
 
 #ifndef HAVE_GETADDRINFO
@@ -3695,25 +3831,29 @@ usage: (make-network-process &rest ARGS)  */)
       res_init ();
 #endif
 
-      host_info_ptr = gethostbyname (SDATA (host));
+      host_info_ptr = gethostbyname ((const char *) SDATA (host));
       immediate_quit = 0;
 
       if (host_info_ptr)
        {
-         ip_addresses = Ncons (make_number (host_info_ptr->h_addr,
-                                            host_info_ptr->h_length),
+         ip_addresses = Fcons (conv_numerical_to_lisp
+                               ((unsigned char *) host_info_ptr->h_addr,
+                                host_info_ptr->h_length,
+                                port),
                                Qnil);
-         family = host_info_ptr->h_addrtype;
        }
       else
-       /* Attempt to interpret host as numeric inet address.  */
+       /* Attempt to interpret host as numeric inet address.  This
+          only works for IPv4 addresses. */
        {
-         unsigned long numeric_addr;
-         numeric_addr = inet_addr (SSDATA (host));
+         unsigned long numeric_addr = inet_addr (SSDATA (host));
+
          if (numeric_addr == -1)
            error ("Unknown host \"%s\"", SDATA (host));
 
-         ip_addresses = Ncons (make_number (numeric_addr), Qnil);
+         ip_addresses = Fcons (conv_numerical_to_lisp
+                               ((unsigned char *) &numeric_addr, 4, port),
+                               Qnil);
        }
 
     }
@@ -3744,6 +3884,16 @@ usage: (make-network-process &rest ARGS)  */)
   p->port = port;
   p->socktype = socktype;
   p->ai_protocol = ai_protocol;
+#ifdef HAVE_GETADDRINFO_A
+  p->dns_requests = NULL;
+#endif
+#ifdef HAVE_GNUTLS
+  tem = Fplist_get (contact, QCtls_parameters);
+  CHECK_LIST (tem);
+  p->gnutls_boot_parameters = tem;
+#endif
+
+  set_network_socket_coding_system (proc);
 
   unbind_to (count, Qnil);
 
@@ -3769,7 +3919,23 @@ usage: (make-network-process &rest ARGS)  */)
 #endif
     }
 
+#ifdef HAVE_GETADDRINFO_A
+  /* If we're doing async address resolution, the list of addresses
+     here will be nil, so we postpone connecting to the server. */
+  if (!p->is_server && NILP (ip_addresses))
+    {
+      p->dns_requests = dns_requests;
+      p->status = Qconnect;
+      dns_processes = Fcons (proc, dns_processes);
+    }
+  else
+    {
+      connect_network_socket (proc, ip_addresses);
+    }
+#else /* HAVE_GETADDRINFO_A */
   connect_network_socket (proc, ip_addresses);
+#endif
+
   return proc;
 }
 
@@ -4474,6 +4640,98 @@ server_accept_connection (Lisp_Object server, int channel)
   exec_sentinel (proc, concat3 (open_from, host_string, nl));
 }
 
+#ifdef HAVE_GETADDRINFO_A
+static Lisp_Object
+check_for_dns (Lisp_Object proc)
+{
+  struct Lisp_Process *p = XPROCESS (proc);
+  Lisp_Object ip_addresses = Qnil;
+  int ret = 0;
+
+  /* Sanity check. */
+  if (! p->dns_requests)
+    return Qnil;
+
+  /* This process should not already be connected (or killed). */
+  if (!EQ (p->status, Qconnect))
+    return Qnil;
+
+  ret = gai_error (p->dns_requests[0]);
+  if (ret == EAI_INPROGRESS)
+    return Qt;
+
+  /* We got a response. */
+  if (ret == 0)
+    {
+      struct addrinfo *res;
+
+      for (res = p->dns_requests[0]->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);
+      freeaddrinfo (p->dns_requests[0]->ar_result);
+    }
+  /* The DNS lookup failed. */
+  else
+    {
+      deactivate_process (proc);
+      pset_status (p, (list2
+                      (Qfailed,
+                       concat3 (build_string ("Name lookup of "),
+                                build_string (p->dns_requests[0]->ar_name),
+                                build_string (" failed")))));
+    }
+
+  xfree ((void *)p->dns_requests[0]->ar_request);
+  xfree ((void *)p->dns_requests[0]->ar_name);
+  xfree ((void *)p->dns_requests[0]->ar_service);
+  xfree (p->dns_requests[0]);
+  xfree (p->dns_requests);
+  p->dns_requests = NULL;
+
+  return ip_addresses;
+}
+
+#endif /* HAVE_GETADDRINFO_A */
+
+static void
+wait_for_socket_fds (Lisp_Object process, char *name)
+{
+  while (XPROCESS (process)->infd < 0 &&
+        EQ (XPROCESS (process)->status, Qconnect))
+    {
+      printf("Waiting for socket from %s...\n", name);
+      wait_reading_process_output (0, 20 * 1000 * 1000, 0, 0, Qnil, NULL, 0);
+    }
+}
+
+static void
+wait_while_connecting (Lisp_Object process)
+{
+  while (EQ (XPROCESS (process)->status, Qconnect))
+    {
+      printf("Waiting for connection...\n");
+      wait_reading_process_output (0, 20 * 1000 * 1000, 0, 0, Qnil, NULL, 0);
+    }
+}
+
+static void
+wait_for_tls_negotiation (Lisp_Object process)
+{
+#ifdef HAVE_GNUTLS
+  while (EQ (XPROCESS (process)->status, Qconnect) &&
+        !NILP (XPROCESS (process)->gnutls_boot_parameters))
+    {
+      printf("Waiting for TLS...\n");
+      wait_reading_process_output (0, 20 * 1000 * 1000, 0, 0, Qnil, NULL, 0);
+    }
+#endif
+}
+
 /* 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
@@ -4599,6 +4857,54 @@ 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
+      if (!NILP (dns_processes))
+       {
+         Lisp_Object dns_list = dns_processes, dns, ip_addresses,
+           answers = Qnil, answer, new = Qnil;
+         struct Lisp_Process *p;
+
+         /* This is programmed in a somewhat awkward fashion because
+         calling connect_network_socket might make us end up back
+         here again, and we would have a race condition with
+         segfaults.  So first go through all pending requests and see
+         whether we got any answers. */
+         while (!NILP (dns_list))
+           {
+             dns = XCAR (dns_list);
+             dns_list = XCDR (dns_list);
+             p = XPROCESS (dns);
+             if (p && p->dns_requests)
+               {
+                 if (! wait_proc || p == wait_proc)
+                   {
+                     ip_addresses = check_for_dns (dns);
+                     if (EQ (ip_addresses, Qt))
+                       new = Fcons (dns, new);
+                     else
+                       answers = Fcons (Fcons (dns, ip_addresses), answers);
+                   }
+                 else
+                   new = Fcons (dns, new);
+               }
+           }
+
+         /* Replace with the list of DNS requests still not responded
+            to. */
+         dns_processes = new;
+
+         /* Then continue the connection for the successful
+            requests. */
+         while (!NILP (answers))
+           {
+             answer = XCAR (answers);
+             answers = XCDR (answers);
+             if (!NILP (XCDR (answer)))
+               connect_network_socket (XCAR (answer), XCDR (answer));
+           }
+       }
+#endif /* HAVE_GETADDRINFO_A */
+
       /* Compute time from now till when time limit is up.  */
       /* Exit if already run out.  */
       if (wait == TIMEOUT)
@@ -5218,11 +5524,15 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
                }
              else
                {
-                 pset_status (p, Qrun);
-                 /* Execute the sentinel here.  If we had relied on
-                    status_notify to do it later, it will read input
-                    from the process before calling the sentinel.  */
-                 exec_sentinel (proc, build_string ("open\n"));
+                 if (NILP (p->gnutls_boot_parameters))
+                   {
+                     pset_status (p, Qrun);
+                     /* Execute the sentinel here.  If we had relied on
+                        status_notify to do it later, it will read input
+                        from the process before calling the sentinel.  */
+                     exec_sentinel (proc, build_string ("open\n"));
+                   }
+
                  if (0 <= p->infd && !EQ (p->filter, Qt)
                      && !EQ (p->command, Qt))
                    {
@@ -5679,6 +5989,11 @@ send_process (Lisp_Object proc, const char *buf, ptrdiff_t len,
   ssize_t rv;
   struct coding_system *coding;
 
+  if (NETCONN_P (proc)) {
+    wait_while_connecting (proc);
+    wait_for_tls_negotiation (proc);
+  }
+
   if (p->raw_status_new)
     update_status (p);
   if (! EQ (p->status, Qrun))
@@ -5910,6 +6225,9 @@ Output from processes can arrive in between bunches.  */)
   if (XINT (start) < GPT && XINT (end) > GPT)
     move_gap_both (XINT (start), start_byte);
 
+  if (NETCONN_P (proc))
+    wait_while_connecting (proc);
+
   send_process (proc, (char *) BYTE_POS_ADDR (start_byte),
                end_byte - start_byte, Fcurrent_buffer ());
 
@@ -5929,6 +6247,7 @@ Output from processes can arrive in between bunches.  */)
   Lisp_Object proc;
   CHECK_STRING (string);
   proc = get_process (process);
+
   send_process (proc, SSDATA (string),
                SBYTES (string), string);
   return Qnil;
@@ -6343,10 +6662,15 @@ process has been transmitted to the serial port.  */)
   struct coding_system *coding = NULL;
   int outfd;
 
-  if (DATAGRAM_CONN_P (process))
+  proc = get_process (process);
+
+  if (NETCONN_P (proc))
+    wait_while_connecting (proc);
+
+  if (DATAGRAM_CONN_P (proc))
     return process;
 
-  proc = get_process (process);
+
   outfd = XPROCESS (proc)->outfd;
   if (outfd >= 0)
     coding = proc_encode_coding_system[outfd];
@@ -6797,7 +7121,12 @@ encode subprocess input.  */)
   register struct Lisp_Process *p;
 
   CHECK_PROCESS (process);
+
+  if (NETCONN_P (process))
+    wait_for_socket_fds (process, "set-process-coding-system");
+
   p = XPROCESS (process);
+
   if (p->infd < 0)
     error ("Input file descriptor of %s closed", SDATA (p->name));
   if (p->outfd < 0)
@@ -6834,6 +7163,10 @@ suppressed.  */)
   register struct Lisp_Process *p;
 
   CHECK_PROCESS (process);
+
+  if (NETCONN_P (process))
+    wait_for_socket_fds (process, "set-process-filter-multibyte");
+
   p = XPROCESS (process);
   if (NILP (flag))
     pset_decode_coding_system
@@ -7451,6 +7784,9 @@ init_process_emacs (void)
 #ifdef DATAGRAM_SOCKETS
   memset (datagram_address, 0, sizeof datagram_address);
 #endif
+#ifdef HAVE_GETADDRINFO_A
+  dns_processes = Qnil;
+#endif
 
 #if defined (DARWIN_OS)
   /* PTYs are broken on Darwin < 6, but are sometimes useful for interactive
@@ -7522,6 +7858,8 @@ syms_of_process (void)
   DEFSYM (QCserver, ":server");
   DEFSYM (QCnowait, ":nowait");
   DEFSYM (QCsentinel, ":sentinel");
+  DEFSYM (QCtls_parameters, ":tls-parameters");
+  DEFSYM (Qnsm_verify_connection, "nsm-verify-connection");
   DEFSYM (QClog, ":log");
   DEFSYM (QCnoquery, ":noquery");
   DEFSYM (QCstop, ":stop");
@@ -7536,6 +7874,9 @@ syms_of_process (void)
 
   staticpro (&Vprocess_alist);
   staticpro (&deleted_pid_list);
+#ifdef HAVE_GETADDRINFO_A
+  staticpro (&dns_processes);
+#endif
 
 #endif /* subprocesses */