]> code.delx.au - gnu-emacs/blobdiff - src/process.c
Ibuffer: Mark locked buffers
[gnu-emacs] / src / process.c
index dd508836f7905dc74affc849fc25d8951679d77c..bdbdefabb64513a5e51d84f00c3b66243451db41 100644 (file)
@@ -75,11 +75,6 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 # include <sys/stropts.h>
 #endif
 
-#ifdef HAVE_RES_INIT
-#include <arpa/nameser.h>
-#include <resolv.h>
-#endif
-
 #ifdef HAVE_UTIL_H
 #include <util.h>
 #endif
@@ -125,15 +120,20 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #endif
 #endif
 
+#if defined HAVE_GETADDRINFO_A || defined HAVE_GNUTLS
+/* This is 0.1s in nanoseconds. */
+#define ASYNC_RETRY_NSEC 100000000
+#endif
+
 #ifdef WINDOWSNT
 extern int sys_select (int, fd_set *, fd_set *, fd_set *,
                       struct timespec *, void *);
 #endif
 
-/* Work around GCC 4.7.0 bug with strict overflow checking; see
+/* Work around GCC 4.3.0 bug with strict overflow checking; see
    <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52904>.
    This bug appears to be fixed in GCC 5.1, so don't work around it there.  */
-#if __GNUC__ == 4 && __GNUC_MINOR__ >= 3
+#if GNUC_PREREQ (4, 3, 0) && ! GNUC_PREREQ (5, 1, 0)
 # pragma GCC diagnostic ignored "-Wstrict-overflow"
 #endif
 \f
@@ -150,6 +150,21 @@ bool inhibit_sentinels;
 #ifndef SOCK_CLOEXEC
 # define SOCK_CLOEXEC 0
 #endif
+#ifndef SOCK_NONBLOCK
+# define SOCK_NONBLOCK 0
+#endif
+
+/* True if ERRNUM represents an error where the system call would
+   block if a blocking variant were used.  */
+static bool
+would_block (int errnum)
+{
+#ifdef EWOULDBLOCK
+  if (EWOULDBLOCK != EAGAIN && errnum == EWOULDBLOCK)
+    return true;
+#endif
+  return errnum == EAGAIN;
+}
 
 #ifndef HAVE_ACCEPT4
 
@@ -193,16 +208,6 @@ static EMACS_INT process_tick;
 /* Number of events for which the user or sentinel has been notified.  */
 static EMACS_INT update_tick;
 
-/* Define NON_BLOCKING_CONNECT if we can support non-blocking connects.
-   The code can be simplified by assuming NON_BLOCKING_CONNECT once
-   Emacs starts assuming POSIX 1003.1-2001 or later.  */
-
-#if (defined HAVE_SELECT                               \
-     && (defined GNU_LINUX || defined HAVE_GETPEERNAME)        \
-     && (defined EWOULDBLOCK || defined EINPROGRESS))
-# define NON_BLOCKING_CONNECT
-#endif
-
 /* Define DATAGRAM_SOCKETS if datagrams can be used safely on
    this system.  We need to read full packets, so we need a
    "non-destructive" select.  So we require either native select,
@@ -262,7 +267,6 @@ static fd_set non_process_wait_mask;
 
 static fd_set write_mask;
 
-#ifdef NON_BLOCKING_CONNECT
 /* Mask of bits indicating the descriptors that we wait for connect to
    complete on.  Once they complete, they are removed from this mask
    and added to the input_wait_mask and non_keyboard_wait_mask.  */
@@ -271,7 +275,6 @@ static fd_set connect_wait_mask;
 
 /* Number of bits set in connect_wait_mask.  */
 static int num_pending_connects;
-#endif /* NON_BLOCKING_CONNECT */
 
 /* The largest descriptor currently in use for a process object; -1 if none.  */
 static int max_process_desc;
@@ -279,8 +282,15 @@ static int max_process_desc;
 /* The largest descriptor currently in use for input; -1 if none.  */
 static int max_input_desc;
 
+/* Set the external socket descriptor for Emacs to use when
+   `make-network-process' is called with a non-nil
+   `:use-external-socket' option.  The value should be either -1, or
+   the file descriptor of a socket that is already bound.  */
+static int external_sock_fd;
+
 /* Indexed by descriptor, gives the process (if any) for that descriptor.  */
 static Lisp_Object chan_process[FD_SETSIZE];
+static void wait_for_socket_fds (Lisp_Object, char const *);
 
 /* Alist of elements (NAME . PROCESS).  */
 static Lisp_Object Vprocess_alist;
@@ -301,7 +311,7 @@ static struct coding_system *proc_encode_coding_system[FD_SETSIZE];
 /* Table of `partner address' for datagram sockets.  */
 static struct sockaddr_and_len {
   struct sockaddr *sa;
-  int len;
+  ptrdiff_t len;
 } datagram_address[FD_SETSIZE];
 #define DATAGRAM_CHAN_P(chan)  (datagram_address[chan].sa != 0)
 #define DATAGRAM_CONN_P(proc)                                           \
@@ -309,7 +319,6 @@ static struct sockaddr_and_len {
    XPROCESS (proc)->infd >= 0 &&                                        \
    datagram_address[XPROCESS (proc)->infd].sa != 0)
 #else
-#define DATAGRAM_CHAN_P(chan)  (0)
 #define DATAGRAM_CONN_P(proc)  (0)
 #endif
 
@@ -381,11 +390,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;
@@ -529,25 +533,37 @@ status_convert (int w)
     return Qrun;
 }
 
+/* True if STATUS is that of a process attempting connection.  */
+
+static bool
+connecting_status (Lisp_Object status)
+{
+  return CONSP (status) && EQ (XCAR (status), Qconnect);
+}
+
 /* Given a status-list, extract the three pieces of information
    and store them individually through the three pointers.  */
 
 static void
-decode_status (Lisp_Object l, Lisp_Object *symbol, int *code, bool *coredump)
+decode_status (Lisp_Object l, Lisp_Object *symbol, Lisp_Object *code,
+              bool *coredump)
 {
   Lisp_Object tem;
 
+  if (connecting_status (l))
+    l = XCAR (l);
+
   if (SYMBOLP (l))
     {
       *symbol = l;
-      *code = 0;
+      *code = make_number (0);
       *coredump = 0;
     }
   else
     {
       *symbol = XCAR (l);
       tem = XCDR (l);
-      *code = XFASTINT (XCAR (tem));
+      *code = XCAR (tem);
       tem = XCDR (tem);
       *coredump = !NILP (tem);
     }
@@ -559,8 +575,7 @@ static Lisp_Object
 status_message (struct Lisp_Process *p)
 {
   Lisp_Object status = p->status;
-  Lisp_Object symbol;
-  int code;
+  Lisp_Object symbol, code;
   bool coredump;
   Lisp_Object string;
 
@@ -570,7 +585,7 @@ status_message (struct Lisp_Process *p)
     {
       char const *signame;
       synchronize_system_messages_locale ();
-      signame = strsignal (code);
+      signame = strsignal (XFASTINT (code));
       if (signame == 0)
        string = build_string ("unknown");
       else
@@ -592,20 +607,20 @@ status_message (struct Lisp_Process *p)
   else if (EQ (symbol, Qexit))
     {
       if (NETCONN1_P (p))
-       return build_string (code == 0 ? "deleted\n" : "connection broken by remote peer\n");
-      if (code == 0)
+       return build_string (XFASTINT (code) == 0
+                            ? "deleted\n"
+                            : "connection broken by remote peer\n");
+      if (XFASTINT (code) == 0)
        return build_string ("finished\n");
       AUTO_STRING (prefix, "exited abnormally with code ");
-      string = Fnumber_to_string (make_number (code));
+      string = Fnumber_to_string (code);
       AUTO_STRING (suffix, coredump ? " (core dumped)\n" : "\n");
       return concat3 (prefix, string, suffix);
     }
   else if (EQ (symbol, Qfailed))
     {
-      AUTO_STRING (prefix, "failed with code ");
-      string = Fnumber_to_string (make_number (code));
-      AUTO_STRING (suffix, "\n");
-      return concat3 (prefix, string, suffix);
+      AUTO_STRING (format, "failed with code %s\n");
+      return CALLN (Fformat, format, code);
     }
   else
     return Fcopy_sequence (Fsymbol_name (symbol));
@@ -691,12 +706,7 @@ allocate_process (void)
 static Lisp_Object
 make_process (Lisp_Object name)
 {
-  register Lisp_Object val, tem, name1;
-  register struct Lisp_Process *p;
-  char suffix[sizeof "<>" + INT_STRLEN_BOUND (printmax_t)];
-  printmax_t i;
-
-  p = allocate_process ();
+  struct Lisp_Process *p = allocate_process ();
   /* Initialize Lisp data.  Note that allocate_process initializes all
      Lisp data to nil, so do it only for slots which should not be nil.  */
   pset_status (p, Qrun);
@@ -706,26 +716,32 @@ make_process (Lisp_Object name)
      non-Lisp data, so do it only for slots which should not be zero.  */
   p->infd = -1;
   p->outfd = -1;
-  for (i = 0; i < PROCESS_OPEN_FDS; i++)
+  for (int i = 0; i < PROCESS_OPEN_FDS; i++)
     p->open_fd[i] = -1;
 
 #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.  */
 
-  name1 = name;
-  for (i = 1; ; i++)
+  Lisp_Object name1 = name;
+  for (printmax_t i = 1; ; i++)
     {
-      tem = Fget_process (name1);
-      if (NILP (tem)) break;
-      name1 = concat2 (name, make_formatted_string (suffix, "<%"pMd">", i));
+      Lisp_Object tem = Fget_process (name1);
+      if (NILP (tem))
+       break;
+      char const suffix_fmt[] = "<%"pMd">";
+      char suffix[sizeof suffix_fmt + INT_STRLEN_BOUND (printmax_t)];
+      AUTO_STRING_WITH_LEN (lsuffix, suffix, sprintf (suffix, suffix_fmt, i));
+      name1 = concat2 (name, lsuffix);
     }
   name = name1;
   pset_name (p, name);
   pset_sentinel (p, Qinternal_default_process_sentinel);
   pset_filter (p, Qinternal_default_process_filter);
+  Lisp_Object val;
   XSETPROCESS (val, p);
   Vprocess_alist = Fcons (Fcons (name, val), Vprocess_alist);
   return val;
@@ -742,6 +758,19 @@ remove_process (register Lisp_Object proc)
   deactivate_process (proc);
 }
 
+#ifdef HAVE_GETADDRINFO_A
+static void
+free_dns_request (Lisp_Object proc)
+{
+  struct Lisp_Process *p = XPROCESS (proc);
+
+  if (p->dns_request->ar_result)
+    freeaddrinfo (p->dns_request->ar_result);
+  xfree (p->dns_request);
+  p->dns_request = NULL;
+}
+#endif
+
 \f
 DEFUN ("processp", Fprocessp, Sprocessp, 1, 1, 0,
        doc: /* Return t if OBJECT is a process.  */)
@@ -832,6 +861,25 @@ nil, indicating the current buffer's process.  */)
   process = get_process (process);
   p = XPROCESS (process);
 
+#ifdef HAVE_GETADDRINFO_A
+  if (p->dns_request)
+    {
+      /* Cancel the request.  Unless shutting down, wait until
+        completion.  Free the request if completely canceled. */
+
+      bool canceled = gai_cancel (p->dns_request) != EAI_NOTCANCELED;
+      if (!canceled && !inhibit_sentinels)
+       {
+         struct gaicb const *req = p->dns_request;
+         while (gai_suspend (&req, 1, NULL) != 0)
+           continue;
+         canceled = true;
+       }
+      if (canceled)
+       free_dns_request (process);
+    }
+#endif
+
   p->raw_status_new = 0;
   if (NETCONN1_P (p) || SERIALCONN1_P (p) || PIPECONN1_P (p))
     {
@@ -1008,6 +1056,23 @@ DEFUN ("process-mark", Fprocess_mark, Sprocess_mark,
   return XPROCESS (process)->mark;
 }
 
+static void
+set_process_filter_masks (struct Lisp_Process *p)
+{
+  if (EQ (p->filter, Qt) && !EQ (p->status, Qlisten))
+    {
+      FD_CLR (p->infd, &input_wait_mask);
+      FD_CLR (p->infd, &non_keyboard_wait_mask);
+    }
+  else if (EQ (p->filter, Qt)
+          /* Network or serial process not stopped:  */
+          && !EQ (p->command, Qt))
+    {
+      FD_SET (p->infd, &input_wait_mask);
+      FD_SET (p->infd, &non_keyboard_wait_mask);
+    }
+}
+
 DEFUN ("set-process-filter", Fset_process_filter, Sset_process_filter,
        2, 2, 0,
        doc: /* Give PROCESS the filter function FILTER; nil means default.
@@ -1024,12 +1089,10 @@ The string argument is normally a multibyte string, except:
 - if `default-enable-multibyte-characters' is nil, it is a unibyte
   string (the result of converting the decoded input multibyte
   string to unibyte with `string-make-unibyte').  */)
-  (register Lisp_Object process, Lisp_Object filter)
+  (Lisp_Object process, Lisp_Object filter)
 {
-  struct Lisp_Process *p;
-
   CHECK_PROCESS (process);
-  p = XPROCESS (process);
+  struct Lisp_Process *p = XPROCESS (process);
 
   /* Don't signal an error if the process's input file descriptor
      is closed.  This could make debugging Lisp more difficult,
@@ -1042,23 +1105,11 @@ The string argument is normally a multibyte string, except:
   if (NILP (filter))
     filter = Qinternal_default_process_filter;
 
+  pset_filter (p, filter);
+
   if (p->infd >= 0)
-    {
-      if (EQ (filter, Qt) && !EQ (p->status, Qlisten))
-       {
-         FD_CLR (p->infd, &input_wait_mask);
-         FD_CLR (p->infd, &non_keyboard_wait_mask);
-       }
-      else if (EQ (p->filter, Qt)
-              /* Network or serial process not stopped:  */
-              && !EQ (p->command, Qt))
-       {
-         FD_SET (p->infd, &input_wait_mask);
-         FD_SET (p->infd, &non_keyboard_wait_mask);
-       }
-    }
+    set_process_filter_masks (p);
 
-  pset_filter (p, filter);
   if (NETCONN1_P (p) || SERIALCONN1_P (p) || PIPECONN1_P (p))
     pset_childp (p, Fplist_put (p->childp, QCfilter, filter));
   setup_process_coding_systems (process);
@@ -1108,7 +1159,9 @@ See `set-process-sentinel' for more info on sentinels.  */)
 
 DEFUN ("set-process-window-size", Fset_process_window_size,
        Sset_process_window_size, 3, 3, 0,
-       doc: /* Tell PROCESS that it has logical window size HEIGHT and WIDTH.  */)
+       doc: /* Tell PROCESS that it has logical window size WIDTH by HEIGHT.
+Value is t if PROCESS was successfully told about the window size,
+nil otherwise.  */)
   (Lisp_Object process, Lisp_Object height, Lisp_Object width)
 {
   CHECK_PROCESS (process);
@@ -1117,7 +1170,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))
@@ -1185,8 +1239,10 @@ SERVICE) for a network connection or (PORT SPEED) for a serial
 connection.  If KEY is t, the complete contact information for the
 connection is returned, else the specific value for the keyword KEY is
 returned.  See `make-network-process' or `make-serial-process' for a
-list of keywords.  */)
-  (register Lisp_Object process, Lisp_Object key)
+list of keywords.
+If PROCESS is a non-blocking network process that hasn't been fully
+set up yet, this function will block until socket setup has completed.  */)
+  (Lisp_Object process, Lisp_Object key)
 {
   Lisp_Object contact;
 
@@ -1194,6 +1250,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,
@@ -1228,8 +1288,8 @@ DEFUN ("process-plist", Fprocess_plist, Sprocess_plist,
 
 DEFUN ("set-process-plist", Fset_process_plist, Sset_process_plist,
        2, 2, 0,
-       doc: /* Replace the plist of PROCESS with PLIST.  Returns PLIST.  */)
-  (register Lisp_Object process, Lisp_Object plist)
+       doc: /* Replace the plist of PROCESS with PLIST.  Return PLIST.  */)
+  (Lisp_Object process, Lisp_Object plist)
 {
   CHECK_PROCESS (process);
   CHECK_LIST (plist);
@@ -1269,7 +1329,7 @@ A 4 or 5 element vector represents an IPv4 address (with port number).
 An 8 or 9 element vector represents an IPv6 address (with port number).
 If optional second argument OMIT-PORT is non-nil, don't include a port
 number in the string, even when present in ADDRESS.
-Returns nil if format of ADDRESS is invalid.  */)
+Return nil if format of ADDRESS is invalid.  */)
   (Lisp_Object address, Lisp_Object omit_port)
 {
   if (NILP (address))
@@ -2217,12 +2277,12 @@ usage:  (make-pipe-process &rest ARGS)  */)
    The address family of sa is not included in the result.  */
 
 Lisp_Object
-conv_sockaddr_to_lisp (struct sockaddr *sa, int len)
+conv_sockaddr_to_lisp (struct sockaddr *sa, ptrdiff_t len)
 {
   Lisp_Object address;
-  int i;
+  ptrdiff_t i;
   unsigned char *cp;
-  register struct Lisp_Vector *p;
+  struct Lisp_Vector *p;
 
   /* Workaround for a bug in getsockname on BSD: Names bound to
      sockets in the UNIX domain are inaccessible; getsockname returns
@@ -2294,13 +2354,23 @@ conv_sockaddr_to_lisp (struct sockaddr *sa, int len)
   return address;
 }
 
+/* Convert an internal struct addrinfo to a Lisp object.  */
+
+static Lisp_Object
+conv_addrinfo_to_lisp (struct addrinfo *res)
+{
+  Lisp_Object protocol = make_number (res->ai_protocol);
+  eassert (XINT (protocol) == res->ai_protocol);
+  return Fcons (protocol, conv_sockaddr_to_lisp (res->ai_addr, res->ai_addrlen));
+}
+
 
 /* Get family and required size for sockaddr structure to hold ADDRESS.  */
 
-static int
+static ptrdiff_t
 get_lisp_to_sockaddr_size (Lisp_Object address, int *familyp)
 {
-  register struct Lisp_Vector *p;
+  struct Lisp_Vector *p;
 
   if (VECTORP (address))
     {
@@ -2416,13 +2486,18 @@ conv_lisp_to_sockaddr (int family, Lisp_Object address, struct sockaddr *sa, int
 #ifdef DATAGRAM_SOCKETS
 DEFUN ("process-datagram-address", Fprocess_datagram_address, Sprocess_datagram_address,
        1, 1, 0,
-       doc: /* Get the current datagram address associated with PROCESS.  */)
+       doc: /* Get the current datagram address associated with PROCESS.
+If PROCESS is a non-blocking network process that hasn't been fully
+set up yet, this function will block until socket setup has completed.  */)
   (Lisp_Object process)
 {
   int channel;
 
   CHECK_PROCESS (process);
 
+  if (NETCONN_P (process))
+    wait_for_socket_fds (process, "process-datagram-address");
+
   if (!DATAGRAM_CONN_P (process))
     return Qnil;
 
@@ -2434,14 +2509,21 @@ DEFUN ("process-datagram-address", Fprocess_datagram_address, Sprocess_datagram_
 DEFUN ("set-process-datagram-address", Fset_process_datagram_address, Sset_process_datagram_address,
        2, 2, 0,
        doc: /* Set the datagram address for PROCESS to ADDRESS.
-Returns nil upon error setting address, ADDRESS otherwise.  */)
+Return nil upon error setting address, ADDRESS otherwise.
+
+If PROCESS is a non-blocking network process that hasn't been fully
+set up yet, this function will block until socket setup has completed.  */)
   (Lisp_Object process, Lisp_Object address)
 {
   int channel;
-  int family, len;
+  int family;
+  ptrdiff_t len;
 
   CHECK_PROCESS (process);
 
+  if (NETCONN_P (process))
+    wait_for_socket_fds (process, "set-process-datagram-address");
+
   if (!DATAGRAM_CONN_P (process))
     return Qnil;
 
@@ -2497,7 +2579,7 @@ static const struct socket_options {
 
 /* Set option OPT to value VAL on socket S.
 
-   Returns (1<<socket_options[OPT].optbit) if option is known, 0 otherwise.
+   Return (1<<socket_options[OPT].optbit) if option is known, 0 otherwise.
    Signals an error if setting a known option fails.
 */
 
@@ -2599,7 +2681,10 @@ DEFUN ("set-network-process-option",
        doc: /* For network process PROCESS set option OPTION to value VALUE.
 See `make-network-process' for a list of options and values.
 If optional fourth arg NO-ERROR is non-nil, don't signal an error if
-OPTION is not a supported option, return nil instead; otherwise return t.  */)
+OPTION is not a supported option, return nil instead; otherwise return t.
+
+If PROCESS is a non-blocking network process that hasn't been fully
+set up yet, this function will block until socket setup has completed. */)
   (Lisp_Object process, Lisp_Object option, Lisp_Object value, Lisp_Object no_error)
 {
   int s;
@@ -2610,6 +2695,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,851 +2991,995 @@ usage:  (make-serial-process &rest ARGS)  */)
   return proc;
 }
 
-/* Create a network stream/datagram client/server process.  Treated
-   exactly like a normal process when reading and writing.  Primary
-   differences are in status display and process deletion.  A network
-   connection has no PID; you cannot signal it.  All you can do is
-   stop/continue it and deactivate/close it via delete-process.  */
+static void
+set_network_socket_coding_system (Lisp_Object proc, Lisp_Object host,
+                                 Lisp_Object service, Lisp_Object name)
+{
+  Lisp_Object tem;
+  struct Lisp_Process *p = XPROCESS (proc);
+  Lisp_Object contact = p->childp;
+  Lisp_Object coding_systems = Qt;
+  Lisp_Object val;
 
-DEFUN ("make-network-process", Fmake_network_process, Smake_network_process,
-       0, MANY, 0,
-       doc: /* Create and return a network server or client process.
+  tem = Fplist_member (contact, QCcoding);
+  if (!NILP (tem) && (!CONSP (tem) || !CONSP (XCDR (tem))))
+    tem = Qnil;  /* No error message (too late!).  */
 
-In Emacs, network connections are represented by process objects, so
-input and output work as for subprocesses and `delete-process' closes
-a network connection.  However, a network process has no process id,
-it cannot be signaled, and the status codes are different from normal
-processes.
+  /* Setup coding systems for communicating with the network stream.  */
+  /* Qt denotes we have not yet called Ffind_operation_coding_system.  */
 
-Arguments are specified as keyword/argument pairs.  The following
-arguments are defined:
+  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);
 
-:name NAME -- NAME is name for process.  It is modified if necessary
-to make it unique.
+  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);
 
-:buffer BUFFER -- BUFFER is the buffer (or buffer-name) to associate
-with the process.  Process output goes at end of that buffer, unless
-you specify an output stream or filter function to handle the output.
-BUFFER may be also nil, meaning that this process is not associated
-with any buffer.
+  pset_decoding_buf (p, empty_unibyte_string);
+  p->decoding_carryover = 0;
+  pset_encoding_buf (p, empty_unibyte_string);
 
-:host HOST -- HOST is name of the host to connect to, or its IP
-address.  The symbol `local' specifies the local host.  If specified
-for a server process, it must be a valid name or address for the local
-host, and only clients connecting to that address will be accepted.
+  p->inherit_coding_system_flag
+    = !(!NILP (tem) || NILP (p->buffer) || !inherit_process_coding_system);
+}
 
-:service SERVICE -- SERVICE is name of the service desired, or an
-integer specifying a port number to connect to.  If SERVICE is t,
-a random port number is selected for the server.  (If Emacs was
-compiled with getaddrinfo, a port number can also be specified as a
-string, e.g. "80", as well as an integer.  This is not portable.)
+#ifdef HAVE_GNUTLS
+static void
+finish_after_tls_connection (Lisp_Object proc)
+{
+  struct Lisp_Process *p = XPROCESS (proc);
+  Lisp_Object contact = p->childp;
+  Lisp_Object result = Qt;
 
-:type TYPE -- TYPE is the type of connection.  The default (nil) is a
-stream type connection, `datagram' creates a datagram type connection,
-`seqpacket' creates a reliable datagram connection.
+  if (!NILP (Ffboundp (Qnsm_verify_connection)))
+    result = call3 (Qnsm_verify_connection,
+                   proc,
+                   Fplist_get (contact, QChost),
+                   Fplist_get (contact, QCservice));
 
-:family FAMILY -- FAMILY is the address (and protocol) family for the
-service specified by HOST and SERVICE.  The default (nil) is to use
-whatever address family (IPv4 or IPv6) that is defined for the host
-and port number specified by HOST and SERVICE.  Other address families
-supported are:
-  local -- for a local (i.e. UNIX) address specified by SERVICE.
-  ipv4  -- use IPv4 address family only.
-  ipv6  -- use IPv6 address family only.
+  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
 
-:local ADDRESS -- ADDRESS is the local address used for the connection.
-This parameter is ignored when opening a client process. When specified
-for a server process, the FAMILY, HOST and SERVICE args are ignored.
+static void
+connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos,
+                        Lisp_Object use_external_socket_p)
+{
+  ptrdiff_t count = SPECPDL_INDEX ();
+  ptrdiff_t count1;
+  int s = -1, outch, inch;
+  int xerrno = 0;
+  int family;
+  struct sockaddr *sa = NULL;
+  int ret;
+  ptrdiff_t addrlen;
+  struct Lisp_Process *p = XPROCESS (proc);
+  Lisp_Object contact = p->childp;
+  int optbits = 0;
+  int socket_to_use = -1;
 
-:remote ADDRESS -- ADDRESS is the remote partner's address for the
-connection.  This parameter is ignored when opening a stream server
-process.  For a datagram server process, it specifies the initial
-setting of the remote datagram address.  When specified for a client
-process, the FAMILY, HOST, and SERVICE args are ignored.
+  if (!NILP (use_external_socket_p))
+    {
+      socket_to_use = external_sock_fd;
 
-The format of ADDRESS depends on the address family:
-- An IPv4 address is represented as an vector of integers [A B C D P]
-corresponding to numeric IP address A.B.C.D and port number P.
-- A local address is represented as a string with the address in the
-local address space.
-- An "unsupported family" address is represented by a cons (F . AV)
-where F is the family number and AV is a vector containing the socket
-address data with one element per address data byte.  Do not rely on
-this format in portable code, as it may depend on implementation
-defined constants, data sizes, and data structure alignment.
+      /* Ensure we don't consume the external socket twice.  */
+      external_sock_fd = -1;
+    }
 
-:coding CODING -- If CODING is a symbol, it specifies the coding
-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.
+  /* Do this in case we never enter the while-loop below.  */
+  count1 = SPECPDL_INDEX ();
+  s = -1;
 
-: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.
+  while (!NILP (addrinfos))
+    {
+      Lisp_Object addrinfo = XCAR (addrinfos);
+      addrinfos = XCDR (addrinfos);
+      int protocol = XINT (XCAR (addrinfo));
+      Lisp_Object ip_address = XCDR (addrinfo);
 
-:noquery BOOL -- Query the user unless BOOL is non-nil, and process is
-running when Emacs is exited.
+#ifdef WINDOWSNT
+    retry_connect:
+#endif
 
-:stop BOOL -- Start process in the `stopped' state if BOOL non-nil.
-In the stopped state, a server process does not accept new
-connections, and a client process does not handle incoming traffic.
-The stopped state is cleared by `continue-process' and set by
-`stop-process'.
+      addrlen = get_lisp_to_sockaddr_size (ip_address, &family);
+      if (sa)
+       free (sa);
+      sa = xmalloc (addrlen);
+      conv_lisp_to_sockaddr (family, ip_address, sa, addrlen);
 
-:filter FILTER -- Install FILTER as the process filter.
+      s = socket_to_use;
+      if (s < 0)
+       {
+         int socktype = p->socktype | SOCK_CLOEXEC;
+         if (p->is_non_blocking_client)
+           socktype |= SOCK_NONBLOCK;
+         s = socket (family, socktype, protocol);
+         if (s < 0)
+           {
+             xerrno = errno;
+             continue;
+           }
+       }
 
-:filter-multibyte BOOL -- If BOOL is non-nil, strings given to the
-process filter are multibyte, otherwise they are unibyte.
-If this keyword is not specified, the strings are multibyte if
-the default value of `enable-multibyte-characters' is non-nil.
+      if (p->is_non_blocking_client && ! (SOCK_NONBLOCK && socket_to_use < 0))
+       {
+         ret = fcntl (s, F_SETFL, O_NONBLOCK);
+         if (ret < 0)
+           {
+             xerrno = errno;
+             emacs_close (s);
+             s = -1;
+             continue;
+           }
+       }
 
-:sentinel SENTINEL -- Install SENTINEL as the process sentinel.
+#ifdef DATAGRAM_SOCKETS
+      if (!p->is_server && p->socktype == SOCK_DGRAM)
+       break;
+#endif /* DATAGRAM_SOCKETS */
 
-:log LOG -- Install LOG as the server process log function.  This
-function is called when the server accepts a network connection from a
-client.  The arguments are SERVER, CLIENT, and MESSAGE, where SERVER
-is the server process, CLIENT is the new process for the connection,
-and MESSAGE is a string.
+      /* Make us close S if quit.  */
+      record_unwind_protect_int (close_file_unwind, s);
 
-:plist PLIST -- Install PLIST as the new process's initial plist.
+      /* Parse network options in the arg list.  We simply ignore anything
+        which isn't a known option (including other keywords).  An error
+        is signaled if setting a known option fails.  */
+      {
+       Lisp_Object params = contact, key, val;
 
-: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
-pending connection queue (also known as the backlog); the default
-queue length is 5.  Default is to create a client process.
+       while (!NILP (params))
+         {
+           key = XCAR (params);
+           params = XCDR (params);
+           val = XCAR (params);
+           params = XCDR (params);
+           optbits |= set_socket_option (s, key, val);
+         }
+      }
 
-The following network options can be specified for this connection:
+      if (p->is_server)
+       {
+         /* Configure as a server socket.  */
 
-:broadcast BOOL    -- Allow send and receive of datagram broadcasts.
-:dontroute BOOL    -- Only send to directly connected hosts.
-:keepalive BOOL    -- Send keep-alive messages on network stream.
-:linger BOOL or TIMEOUT -- Send queued messages before closing.
-:oobinline BOOL    -- Place out-of-band data in receive data stream.
-:priority INT      -- Set protocol defined priority for sent packets.
-:reuseaddr BOOL    -- Allow reusing a recently used local address
-                      (this is allowed by default for a server process).
-:bindtodevice NAME -- bind to interface NAME.  Using this may require
-                      special privileges on some systems.
+         /* SO_REUSEADDR = 1 is default for server sockets; must specify
+            explicit :reuseaddr key to override this.  */
+#ifdef HAVE_LOCAL_SOCKETS
+         if (family != AF_LOCAL)
+#endif
+           if (!(optbits & (1 << OPIX_REUSEADDR)))
+             {
+               int optval = 1;
+               if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval))
+                 report_file_error ("Cannot set reuse option on server socket", Qnil);
+             }
 
-Consult the relevant system programmer's manual pages for more
-information on using these options.
+          /* If passed a socket descriptor, it should be already bound. */
+         if (socket_to_use < 0 && bind (s, sa, addrlen) != 0)
+           report_file_error ("Cannot bind server socket", Qnil);
 
+#ifdef HAVE_GETSOCKNAME
+         if (p->port == 0)
+           {
+             struct sockaddr_in sa1;
+             socklen_t len1 = sizeof (sa1);
+             if (getsockname (s, (struct sockaddr *)&sa1, &len1) == 0)
+               {
+                 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
 
-A server process will listen for and accept connections from clients.
-When a client connection is accepted, a new network process is created
-for the connection with the following parameters:
+         if (p->socktype != SOCK_DGRAM && listen (s, p->backlog))
+           report_file_error ("Cannot listen on server socket", Qnil);
 
-- The client's process name is constructed by concatenating the server
-process's NAME and a client identification string.
-- If the FILTER argument is non-nil, the client process will not get a
-separate process buffer; otherwise, the client's process buffer is a newly
-created buffer named after the server process's BUFFER name or process
-NAME concatenated with the client identification string.
-- The connection type and the process filter and sentinel parameters are
-inherited from the server process's TYPE, FILTER and SENTINEL.
-- The client process's contact info is set according to the client's
-addressing information (typically an IP address and a port number).
-- The client process's plist is initialized from the server's plist.
+         break;
+       }
 
-Notice that the FILTER and SENTINEL args are never used directly by
-the server process.  Also, the BUFFER argument is not used directly by
-the server process, but via the optional :log function, accepted (and
-failed) connections may be logged in the server process's buffer.
+      immediate_quit = 1;
+      QUIT;
 
-The original argument list, modified with the actual connection
-information, is available via the `process-contact' function.
+      ret = connect (s, sa, addrlen);
+      xerrno = errno;
 
-usage: (make-network-process &rest ARGS)  */)
-  (ptrdiff_t nargs, Lisp_Object *args)
-{
-  Lisp_Object proc;
-  Lisp_Object contact;
-  struct Lisp_Process *p;
-#ifdef HAVE_GETADDRINFO
-  struct addrinfo ai, *res, *lres;
-  struct addrinfo hints;
-  const char *portstring;
-  char portbuf[128];
-#else /* HAVE_GETADDRINFO */
-  struct _emacs_addrinfo
-  {
-    int ai_family;
-    int ai_socktype;
-    int ai_protocol;
-    int ai_addrlen;
-    struct sockaddr *ai_addr;
-    struct _emacs_addrinfo *ai_next;
-  } ai, *res, *lres;
-#endif /* HAVE_GETADDRINFO */
-  struct sockaddr_in address_in;
-#ifdef HAVE_LOCAL_SOCKETS
-  struct sockaddr_un address_un;
-#endif
-  int port;
-  int ret = 0;
-  int xerrno = 0;
-  int s = -1, outch, inch;
-  ptrdiff_t count = SPECPDL_INDEX ();
-  ptrdiff_t count1;
-  Lisp_Object colon_address;  /* Either QClocal or QCremote.  */
-  Lisp_Object tem;
-  Lisp_Object name, buffer, host, service, address;
-  Lisp_Object filter, sentinel;
-  bool is_non_blocking_client = 0;
-  bool is_server = 0;
-  int backlog = 5;
-  int socktype;
-  int family = -1;
+      if (ret == 0 || xerrno == EISCONN)
+       {
+         /* The unwind-protect will be discarded afterwards.
+            Likewise for immediate_quit.  */
+         break;
+       }
 
-  if (nargs == 0)
-    return Qnil;
+      if (p->is_non_blocking_client && xerrno == EINPROGRESS)
+       break;
 
-  /* Save arguments for process-contact and clone-process.  */
-  contact = Flist (nargs, args);
+#ifndef WINDOWSNT
+      if (xerrno == EINTR)
+       {
+         /* Unlike most other syscalls connect() cannot be called
+            again.  (That would return EALREADY.)  The proper way to
+            wait for completion is pselect().  */
+         int sc;
+         socklen_t len;
+         fd_set fdset;
+       retry_select:
+         FD_ZERO (&fdset);
+         FD_SET (s, &fdset);
+         QUIT;
+         sc = pselect (s + 1, NULL, &fdset, NULL, NULL, NULL);
+         if (sc == -1)
+           {
+             if (errno == EINTR)
+               goto retry_select;
+             else
+               report_file_error ("Failed select", Qnil);
+           }
+         eassert (sc > 0);
+
+         len = sizeof xerrno;
+         eassert (FD_ISSET (s, &fdset));
+         if (getsockopt (s, SOL_SOCKET, SO_ERROR, &xerrno, &len) < 0)
+           report_file_error ("Failed getsockopt", Qnil);
+         if (xerrno == 0)
+           break;
+         if (NILP (addrinfos))
+           report_file_errno ("Failed connect", Qnil, xerrno);
+       }
+#endif /* !WINDOWSNT */
+
+      immediate_quit = 0;
+
+      /* Discard the unwind protect closing S.  */
+      specpdl_ptr = specpdl + count1;
+      emacs_close (s);
+      s = -1;
 
 #ifdef WINDOWSNT
-  /* Ensure socket support is loaded if available.  */
-  init_winsock (TRUE);
+      if (xerrno == EINTR)
+       goto retry_connect;
 #endif
+    }
 
-  /* :type TYPE  (nil: stream, datagram */
-  tem = Fplist_get (contact, QCtype);
-  if (NILP (tem))
-    socktype = SOCK_STREAM;
+  if (s >= 0)
+    {
 #ifdef DATAGRAM_SOCKETS
-  else if (EQ (tem, Qdatagram))
-    socktype = SOCK_DGRAM;
-#endif
-#ifdef HAVE_SEQPACKET
-  else if (EQ (tem, Qseqpacket))
-    socktype = SOCK_SEQPACKET;
+      if (p->socktype == SOCK_DGRAM)
+       {
+         if (datagram_address[s].sa)
+           emacs_abort ();
+
+         datagram_address[s].sa = xmalloc (addrlen);
+         datagram_address[s].len = addrlen;
+         if (p->is_server)
+           {
+             Lisp_Object remote;
+             memset (datagram_address[s].sa, 0, addrlen);
+             if (remote = Fplist_get (contact, QCremote), !NILP (remote))
+               {
+                 int rfamily;
+                 ptrdiff_t rlen = get_lisp_to_sockaddr_size (remote, &rfamily);
+                 if (rlen != 0 && rfamily == family
+                     && rlen == addrlen)
+                   conv_lisp_to_sockaddr (rfamily, remote,
+                                          datagram_address[s].sa, rlen);
+               }
+           }
+         else
+           memcpy (datagram_address[s].sa, sa, addrlen);
+       }
 #endif
-  else
-    error ("Unsupported connection type");
 
-  /* :server BOOL */
-  tem = Fplist_get (contact, QCserver);
-  if (!NILP (tem))
-    {
-      /* Don't support network sockets when non-blocking mode is
-        not available, since a blocked Emacs is not useful.  */
-      is_server = 1;
-      if (TYPE_RANGED_INTEGERP (int, tem))
-       backlog = XINT (tem);
+      contact = Fplist_put (contact, p->is_server? QClocal: QCremote,
+                           conv_sockaddr_to_lisp (sa, addrlen));
+#ifdef HAVE_GETSOCKNAME
+      if (!p->is_server)
+       {
+         struct sockaddr_in sa1;
+         socklen_t len1 = sizeof (sa1);
+         if (getsockname (s, (struct sockaddr *)&sa1, &len1) == 0)
+           contact = Fplist_put (contact, QClocal,
+                                 conv_sockaddr_to_lisp ((struct sockaddr *)&sa1, len1));
+       }
+#endif
     }
 
-  /* Make colon_address an alias for :local (server) or :remote (client).  */
-  colon_address = is_server ? QClocal : QCremote;
+  immediate_quit = 0;
 
-  /* :nowait BOOL */
-  if (!is_server && socktype != SOCK_DGRAM
-      && (tem = Fplist_get (contact, QCnowait), !NILP (tem)))
+  if (s < 0)
     {
-#ifndef NON_BLOCKING_CONNECT
-      error ("Non-blocking connect not supported");
-#else
-      is_non_blocking_client = 1;
-#endif
+      /* If non-blocking got this far - and failed - assume non-blocking is
+        not supported after all.  This is probably a wrong assumption, but
+        the normal blocking calls to open-network-stream handles this error
+        better.  */
+      if (p->is_non_blocking_client)
+       return;
+
+      report_file_errno ((p->is_server
+                         ? "make server process failed"
+                         : "make client process failed"),
+                        contact, xerrno);
     }
 
-  name = Fplist_get (contact, QCname);
-  buffer = Fplist_get (contact, QCbuffer);
-  filter = Fplist_get (contact, QCfilter);
-  sentinel = Fplist_get (contact, QCsentinel);
+  inch = s;
+  outch = s;
 
-  CHECK_STRING (name);
+  chan_process[inch] = proc;
 
-  /* Initialize addrinfo structure in case we don't use getaddrinfo.  */
-  ai.ai_socktype = socktype;
-  ai.ai_protocol = 0;
-  ai.ai_next = NULL;
-  res = &ai;
+  fcntl (inch, F_SETFL, O_NONBLOCK);
 
-  /* :local ADDRESS or :remote ADDRESS */
-  address = Fplist_get (contact, colon_address);
-  if (!NILP (address))
-    {
-      host = service = Qnil;
+  p = XPROCESS (proc);
+  p->open_fd[SUBPROCESS_STDIN] = inch;
+  p->infd  = inch;
+  p->outfd = outch;
 
-      if (!(ai.ai_addrlen = get_lisp_to_sockaddr_size (address, &family)))
-       error ("Malformed :address");
-      ai.ai_family = family;
-      ai.ai_addr = alloca (ai.ai_addrlen);
-      conv_lisp_to_sockaddr (family, address, ai.ai_addr, ai.ai_addrlen);
-      goto open_socket;
-    }
+  /* Discard the unwind protect for closing S, if any.  */
+  specpdl_ptr = specpdl + count1;
 
-  /* :family FAMILY -- nil (for Inet), local, or integer.  */
-  tem = Fplist_get (contact, QCfamily);
-  if (NILP (tem))
+  /* Unwind bind_polling_period and request_sigio.  */
+  unbind_to (count, Qnil);
+
+  if (p->is_server && p->socktype != SOCK_DGRAM)
+    pset_status (p, Qlisten);
+
+  /* Make the process marker point into the process buffer (if any).  */
+  if (BUFFERP (p->buffer))
+    set_marker_both (p->mark, p->buffer,
+                    BUF_ZV (XBUFFER (p->buffer)),
+                    BUF_ZV_BYTE (XBUFFER (p->buffer)));
+
+  if (p->is_non_blocking_client)
     {
-#if defined (HAVE_GETADDRINFO) && defined (AF_INET6)
-      family = AF_UNSPEC;
-#else
-      family = AF_INET;
-#endif
+      /* We may get here if connect did succeed immediately.  However,
+        in that case, we still need to signal this like a non-blocking
+        connection.  */
+      if (! (connecting_status (p->status)
+            && EQ (XCDR (p->status), addrinfos)))
+       pset_status (p, Fcons (Qconnect, addrinfos));
+      if (!FD_ISSET (inch, &connect_wait_mask))
+       {
+         FD_SET (inch, &connect_wait_mask);
+         FD_SET (inch, &write_mask);
+         num_pending_connects++;
+       }
     }
-#ifdef HAVE_LOCAL_SOCKETS
-  else if (EQ (tem, Qlocal))
-    family = AF_LOCAL;
-#endif
-#ifdef AF_INET6
-  else if (EQ (tem, Qipv6))
-    family = AF_INET6;
-#endif
-  else if (EQ (tem, Qipv4))
-    family = AF_INET;
-  else if (TYPE_RANGED_INTEGERP (int, tem))
-    family = XINT (tem);
   else
-    error ("Unknown address family");
+    /* A server may have a client filter setting of Qt, but it must
+       still listen for incoming connects unless it is stopped.  */
+    if ((!EQ (p->filter, Qt) && !EQ (p->command, Qt))
+       || (EQ (p->status, Qlisten) && NILP (p->command)))
+      {
+       FD_SET (inch, &input_wait_mask);
+       FD_SET (inch, &non_keyboard_wait_mask);
+      }
 
-  ai.ai_family = family;
+  if (inch > max_process_desc)
+    max_process_desc = inch;
 
-  /* :service SERVICE -- string, integer (port number), or t (random port).  */
-  service = Fplist_get (contact, QCservice);
+  /* Set up the masks based on the process filter. */
+  set_process_filter_masks (p);
 
-  /* :host HOST -- hostname, ip address, or 'local for localhost.  */
-  host = Fplist_get (contact, QChost);
-  if (!NILP (host))
-    {
-      if (EQ (host, Qlocal))
-       /* Depending on setup, "localhost" may map to different IPv4 and/or
-          IPv6 addresses, so it's better to be explicit (Bug#6781).  */
-       host = build_string ("127.0.0.1");
-      CHECK_STRING (host);
-    }
+  setup_process_coding_systems (proc);
 
-#ifdef HAVE_LOCAL_SOCKETS
-  if (family == AF_LOCAL)
+#ifdef HAVE_GNUTLS
+  /* Continue the asynchronous connection. */
+  if (!NILP (p->gnutls_boot_parameters))
     {
-      if (!NILP (host))
+      Lisp_Object boot, params = p->gnutls_boot_parameters;
+
+      boot = Fgnutls_boot (proc, XCAR (params), XCDR (params));
+      p->gnutls_boot_parameters = Qnil;
+
+      if (p->gnutls_initstage == GNUTLS_STAGE_READY)
+       /* Run sentinels, etc. */
+       finish_after_tls_connection (proc);
+      else if (p->gnutls_initstage != GNUTLS_STAGE_HANDSHAKE_TRIED)
        {
-         message (":family local ignores the :host property");
-         contact = Fplist_put (contact, QChost, Qnil);
-         host = Qnil;
+         deactivate_process (proc);
+         if (NILP (boot))
+           pset_status (p, list2 (Qfailed,
+                                  build_string ("TLS negotiation failed")));
+         else
+           pset_status (p, list2 (Qfailed, boot));
        }
-      CHECK_STRING (service);
-      memset (&address_un, 0, sizeof address_un);
-      address_un.sun_family = AF_LOCAL;
-      if (sizeof address_un.sun_path <= SBYTES (service))
-       error ("Service name too long");
-      lispstpcpy (address_un.sun_path, service);
-      ai.ai_addr = (struct sockaddr *) &address_un;
-      ai.ai_addrlen = sizeof address_un;
-      goto open_socket;
     }
 #endif
 
-  /* Slow down polling to every ten seconds.
-     Some kernels have a bug which causes retrying connect to fail
-     after a connect.  Polling can interfere with gethostbyname too.  */
-#ifdef POLL_FOR_INPUT
-  if (socktype != SOCK_DGRAM)
-    {
-      record_unwind_protect_void (run_all_atimers);
-      bind_polling_period (10);
-    }
-#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 (!NILP (host))
-    {
+/* Create a network stream/datagram client/server process.  Treated
+   exactly like a normal process when reading and writing.  Primary
+   differences are in status display and process deletion.  A network
+   connection has no PID; you cannot signal it.  All you can do is
+   stop/continue it and deactivate/close it via delete-process.  */
 
-      /* SERVICE can either be a string or int.
-        Convert to a C string for later use by getaddrinfo.  */
-      if (EQ (service, Qt))
-       portstring = "0";
-      else if (INTEGERP (service))
-       {
-         sprintf (portbuf, "%"pI"d", XINT (service));
-         portstring = portbuf;
-       }
-      else
-       {
-         CHECK_STRING (service);
-         portstring = SSDATA (service);
-       }
+DEFUN ("make-network-process", Fmake_network_process, Smake_network_process,
+       0, MANY, 0,
+       doc: /* Create and return a network server or client process.
 
-      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;
+In Emacs, network connections are represented by process objects, so
+input and output work as for subprocesses and `delete-process' closes
+a network connection.  However, a network process has no process id,
+it cannot be signaled, and the status codes are different from normal
+processes.
 
-#ifdef HAVE_RES_INIT
-      res_init ();
-#endif
+Arguments are specified as keyword/argument pairs.  The following
+arguments are defined:
 
-      ret = getaddrinfo (SSDATA (host), portstring, &hints, &res);
-      if (ret)
-#ifdef HAVE_GAI_STRERROR
-       error ("%s/%s %s", SSDATA (host), portstring, gai_strerror (ret));
-#else
-       error ("%s/%s getaddrinfo error %d", SSDATA (host), portstring, ret);
-#endif
-      immediate_quit = 0;
+:name NAME -- NAME is name for process.  It is modified if necessary
+to make it unique.
+
+:buffer BUFFER -- BUFFER is the buffer (or buffer-name) to associate
+with the process.  Process output goes at end of that buffer, unless
+you specify an output stream or filter function to handle the output.
+BUFFER may be also nil, meaning that this process is not associated
+with any buffer.
 
-      goto open_socket;
-    }
-#endif /* HAVE_GETADDRINFO */
+:host HOST -- HOST is name of the host to connect to, or its IP
+address.  The symbol `local' specifies the local host.  If specified
+for a server process, it must be a valid name or address for the local
+host, and only clients connecting to that address will be accepted.
 
-  /* We end up here if getaddrinfo is not defined, or in case no hostname
-     has been specified (e.g. for a local server process).  */
+:service SERVICE -- SERVICE is name of the service desired, or an
+integer specifying a port number to connect to.  If SERVICE is t,
+a random port number is selected for the server.  A port number can
+be specified as an integer string, e.g., "80", as well as an integer.
 
-  if (EQ (service, Qt))
-    port = 0;
-  else if (INTEGERP (service))
-    port = htons ((unsigned short) XINT (service));
-  else
-    {
-      struct servent *svc_info;
-      CHECK_STRING (service);
-      svc_info = getservbyname (SSDATA (service),
-                               (socktype == SOCK_DGRAM ? "udp" : "tcp"));
-      if (svc_info == 0)
-       error ("Unknown service: %s", SDATA (service));
-      port = svc_info->s_port;
-    }
+:type TYPE -- TYPE is the type of connection.  The default (nil) is a
+stream type connection, `datagram' creates a datagram type connection,
+`seqpacket' creates a reliable datagram connection.
 
-  memset (&address_in, 0, sizeof address_in);
-  address_in.sin_family = family;
-  address_in.sin_addr.s_addr = INADDR_ANY;
-  address_in.sin_port = port;
+:family FAMILY -- FAMILY is the address (and protocol) family for the
+service specified by HOST and SERVICE.  The default (nil) is to use
+whatever address family (IPv4 or IPv6) that is defined for the host
+and port number specified by HOST and SERVICE.  Other address families
+supported are:
+  local -- for a local (i.e. UNIX) address specified by SERVICE.
+  ipv4  -- use IPv4 address family only.
+  ipv6  -- use IPv6 address family only.
 
-#ifndef HAVE_GETADDRINFO
-  if (!NILP (host))
-    {
-      struct hostent *host_info_ptr;
+:local ADDRESS -- ADDRESS is the local address used for the connection.
+This parameter is ignored when opening a client process. When specified
+for a server process, the FAMILY, HOST and SERVICE args are ignored.
 
-      /* gethostbyname may fail with TRY_AGAIN, but we don't honor that,
-        as it may `hang' Emacs for a very long time.  */
-      immediate_quit = 1;
-      QUIT;
+:remote ADDRESS -- ADDRESS is the remote partner's address for the
+connection.  This parameter is ignored when opening a stream server
+process.  For a datagram server process, it specifies the initial
+setting of the remote datagram address.  When specified for a client
+process, the FAMILY, HOST, and SERVICE args are ignored.
 
-#ifdef HAVE_RES_INIT
-      res_init ();
-#endif
+The format of ADDRESS depends on the address family:
+- An IPv4 address is represented as an vector of integers [A B C D P]
+corresponding to numeric IP address A.B.C.D and port number P.
+- A local address is represented as a string with the address in the
+local address space.
+- An "unsupported family" address is represented by a cons (F . AV)
+where F is the family number and AV is a vector containing the socket
+address data with one element per address data byte.  Do not rely on
+this format in portable code, as it may depend on implementation
+defined constants, data sizes, and data structure alignment.
 
-      host_info_ptr = gethostbyname (SDATA (host));
-      immediate_quit = 0;
+:coding CODING -- If CODING is a symbol, it specifies the coding
+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.
 
-      if (host_info_ptr)
-       {
-         memcpy (&address_in.sin_addr, host_info_ptr->h_addr,
-                 host_info_ptr->h_length);
-         family = host_info_ptr->h_addrtype;
-         address_in.sin_family = family;
-       }
-      else
-       /* Attempt to interpret host as numeric inet address.  */
-       {
-         unsigned long numeric_addr;
-         numeric_addr = inet_addr (SSDATA (host));
-         if (numeric_addr == -1)
-           error ("Unknown host \"%s\"", SDATA (host));
+: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.
 
-         memcpy (&address_in.sin_addr, &numeric_addr,
-                 sizeof (address_in.sin_addr));
-       }
+:noquery BOOL -- Query the user unless BOOL is non-nil, and process is
+running when Emacs is exited.
 
-    }
-#endif /* not HAVE_GETADDRINFO */
+:stop BOOL -- Start process in the `stopped' state if BOOL non-nil.
+In the stopped state, a server process does not accept new
+connections, and a client process does not handle incoming traffic.
+The stopped state is cleared by `continue-process' and set by
+`stop-process'.
 
-  ai.ai_family = family;
-  ai.ai_addr = (struct sockaddr *) &address_in;
-  ai.ai_addrlen = sizeof address_in;
+:filter FILTER -- Install FILTER as the process filter.
 
- open_socket:
+:filter-multibyte BOOL -- If BOOL is non-nil, strings given to the
+process filter are multibyte, otherwise they are unibyte.
+If this keyword is not specified, the strings are multibyte if
+the default value of `enable-multibyte-characters' is non-nil.
 
-  /* Do this in case we never enter the for-loop below.  */
-  count1 = SPECPDL_INDEX ();
-  s = -1;
+:sentinel SENTINEL -- Install SENTINEL as the process sentinel.
 
-  for (lres = res; lres; lres = lres->ai_next)
-    {
-      ptrdiff_t optn;
-      int optbits;
+:log LOG -- Install LOG as the server process log function.  This
+function is called when the server accepts a network connection from a
+client.  The arguments are SERVER, CLIENT, and MESSAGE, where SERVER
+is the server process, CLIENT is the new process for the connection,
+and MESSAGE is a string.
 
-#ifdef WINDOWSNT
-    retry_connect:
-#endif
+:plist PLIST -- Install PLIST as the new process's initial plist.
 
-      s = socket (lres->ai_family, lres->ai_socktype | SOCK_CLOEXEC,
-                 lres->ai_protocol);
-      if (s < 0)
-       {
-         xerrno = errno;
-         continue;
-       }
+: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').
 
-#ifdef DATAGRAM_SOCKETS
-      if (!is_server && socktype == SOCK_DGRAM)
-       break;
-#endif /* DATAGRAM_SOCKETS */
+: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
+pending connection queue (also known as the backlog); the default
+queue length is 5.  Default is to create a client process.
 
-#ifdef NON_BLOCKING_CONNECT
-      if (is_non_blocking_client)
-       {
-         ret = fcntl (s, F_SETFL, O_NONBLOCK);
-         if (ret < 0)
-           {
-             xerrno = errno;
-             emacs_close (s);
-             s = -1;
-             continue;
-           }
-       }
-#endif
+The following network options can be specified for this connection:
 
-      /* Make us close S if quit.  */
-      record_unwind_protect_int (close_file_unwind, s);
+:broadcast BOOL    -- Allow send and receive of datagram broadcasts.
+:dontroute BOOL    -- Only send to directly connected hosts.
+:keepalive BOOL    -- Send keep-alive messages on network stream.
+:linger BOOL or TIMEOUT -- Send queued messages before closing.
+:oobinline BOOL    -- Place out-of-band data in receive data stream.
+:priority INT      -- Set protocol defined priority for sent packets.
+:reuseaddr BOOL    -- Allow reusing a recently used local address
+                      (this is allowed by default for a server process).
+:bindtodevice NAME -- bind to interface NAME.  Using this may require
+                      special privileges on some systems.
+:use-external-socket BOOL -- Use any pre-allocated sockets that have
+                             been passed to Emacs.  If Emacs wasn't
+                             passed a socket, this option is silently
+                             ignored.
 
-      /* Parse network options in the arg list.
-        We simply ignore anything which isn't a known option (including other keywords).
-        An error is signaled if setting a known option fails.  */
-      for (optn = optbits = 0; optn < nargs - 1; optn += 2)
-       optbits |= set_socket_option (s, args[optn], args[optn + 1]);
 
-      if (is_server)
-       {
-         /* Configure as a server socket.  */
+Consult the relevant system programmer's manual pages for more
+information on using these options.
 
-         /* SO_REUSEADDR = 1 is default for server sockets; must specify
-            explicit :reuseaddr key to override this.  */
-#ifdef HAVE_LOCAL_SOCKETS
-         if (family != AF_LOCAL)
-#endif
-           if (!(optbits & (1 << OPIX_REUSEADDR)))
-             {
-               int optval = 1;
-               if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval))
-                 report_file_error ("Cannot set reuse option on server socket", Qnil);
-             }
 
-         if (bind (s, lres->ai_addr, lres->ai_addrlen))
-           report_file_error ("Cannot bind server socket", Qnil);
+A server process will listen for and accept connections from clients.
+When a client connection is accepted, a new network process is created
+for the connection with the following parameters:
 
-#ifdef HAVE_GETSOCKNAME
-         if (EQ (service, Qt))
-           {
-             struct sockaddr_in sa1;
-             socklen_t len1 = sizeof (sa1);
-             if (getsockname (s, (struct sockaddr *)&sa1, &len1) == 0)
-               {
-                 ((struct sockaddr_in *)(lres->ai_addr))->sin_port = sa1.sin_port;
-                 service = make_number (ntohs (sa1.sin_port));
-                 contact = Fplist_put (contact, QCservice, service);
-               }
-           }
-#endif
+- The client's process name is constructed by concatenating the server
+process's NAME and a client identification string.
+- If the FILTER argument is non-nil, the client process will not get a
+separate process buffer; otherwise, the client's process buffer is a newly
+created buffer named after the server process's BUFFER name or process
+NAME concatenated with the client identification string.
+- The connection type and the process filter and sentinel parameters are
+inherited from the server process's TYPE, FILTER and SENTINEL.
+- The client process's contact info is set according to the client's
+addressing information (typically an IP address and a port number).
+- The client process's plist is initialized from the server's plist.
 
-         if (socktype != SOCK_DGRAM && listen (s, backlog))
-           report_file_error ("Cannot listen on server socket", Qnil);
+Notice that the FILTER and SENTINEL args are never used directly by
+the server process.  Also, the BUFFER argument is not used directly by
+the server process, but via the optional :log function, accepted (and
+failed) connections may be logged in the server process's buffer.
 
-         break;
-       }
+The original argument list, modified with the actual connection
+information, is available via the `process-contact' function.
 
-      immediate_quit = 1;
-      QUIT;
+usage: (make-network-process &rest ARGS)  */)
+  (ptrdiff_t nargs, Lisp_Object *args)
+{
+  Lisp_Object proc;
+  Lisp_Object contact;
+  struct Lisp_Process *p;
+  const char *portstring;
+  ptrdiff_t portstringlen ATTRIBUTE_UNUSED;
+  char portbuf[INT_BUFSIZE_BOUND (EMACS_INT)];
+#ifdef HAVE_LOCAL_SOCKETS
+  struct sockaddr_un address_un;
+#endif
+  EMACS_INT port = 0;
+  Lisp_Object tem;
+  Lisp_Object name, buffer, host, service, address;
+  Lisp_Object filter, sentinel, use_external_socket_p;
+  Lisp_Object addrinfos = Qnil;
+  int socktype;
+  int family = -1;
+  enum { any_protocol = 0 };
+#ifdef HAVE_GETADDRINFO_A
+  struct gaicb *dns_request = NULL;
+#endif
+  ptrdiff_t count = SPECPDL_INDEX ();
 
-      ret = connect (s, lres->ai_addr, lres->ai_addrlen);
-      xerrno = errno;
+  if (nargs == 0)
+    return Qnil;
 
-      if (ret == 0 || xerrno == EISCONN)
-       {
-         /* The unwind-protect will be discarded afterwards.
-            Likewise for immediate_quit.  */
-         break;
-       }
+  /* Save arguments for process-contact and clone-process.  */
+  contact = Flist (nargs, args);
 
-#ifdef NON_BLOCKING_CONNECT
-#ifdef EINPROGRESS
-      if (is_non_blocking_client && xerrno == EINPROGRESS)
-       break;
-#else
-#ifdef EWOULDBLOCK
-      if (is_non_blocking_client && xerrno == EWOULDBLOCK)
-       break;
+#ifdef WINDOWSNT
+  /* Ensure socket support is loaded if available.  */
+  init_winsock (TRUE);
 #endif
+
+  /* :type TYPE  (nil: stream, datagram */
+  tem = Fplist_get (contact, QCtype);
+  if (NILP (tem))
+    socktype = SOCK_STREAM;
+#ifdef DATAGRAM_SOCKETS
+  else if (EQ (tem, Qdatagram))
+    socktype = SOCK_DGRAM;
 #endif
+#ifdef HAVE_SEQPACKET
+  else if (EQ (tem, Qseqpacket))
+    socktype = SOCK_SEQPACKET;
 #endif
+  else
+    error ("Unsupported connection type");
 
-#ifndef WINDOWSNT
-      if (xerrno == EINTR)
-       {
-         /* Unlike most other syscalls connect() cannot be called
-            again.  (That would return EALREADY.)  The proper way to
-            wait for completion is pselect().  */
-         int sc;
-         socklen_t len;
-         fd_set fdset;
-       retry_select:
-         FD_ZERO (&fdset);
-         FD_SET (s, &fdset);
-         QUIT;
-         sc = pselect (s + 1, NULL, &fdset, NULL, NULL, NULL);
-         if (sc == -1)
-           {
-             if (errno == EINTR)
-               goto retry_select;
-             else
-               report_file_error ("Failed select", Qnil);
-           }
-         eassert (sc > 0);
-
-         len = sizeof xerrno;
-         eassert (FD_ISSET (s, &fdset));
-         if (getsockopt (s, SOL_SOCKET, SO_ERROR, &xerrno, &len) < 0)
-           report_file_error ("Failed getsockopt", Qnil);
-         if (xerrno)
-           report_file_errno ("Failed connect", Qnil, xerrno);
-         break;
-       }
-#endif /* !WINDOWSNT */
+  name = Fplist_get (contact, QCname);
+  buffer = Fplist_get (contact, QCbuffer);
+  filter = Fplist_get (contact, QCfilter);
+  sentinel = Fplist_get (contact, QCsentinel);
+  use_external_socket_p = Fplist_get (contact, QCuse_external_socket);
 
-      immediate_quit = 0;
+  CHECK_STRING (name);
 
-      /* Discard the unwind protect closing S.  */
-      specpdl_ptr = specpdl + count1;
-      emacs_close (s);
-      s = -1;
+  /* :local ADDRESS or :remote ADDRESS */
+  tem = Fplist_get (contact, QCserver);
+  if (NILP (tem))
+    address = Fplist_get (contact, QCremote);
+  else
+    address = Fplist_get (contact, QClocal);
+  if (!NILP (address))
+    {
+      host = service = Qnil;
 
-#ifdef WINDOWSNT
-      if (xerrno == EINTR)
-       goto retry_connect;
+      if (!get_lisp_to_sockaddr_size (address, &family))
+       error ("Malformed :address");
+
+      addrinfos = list1 (Fcons (make_number (any_protocol), address));
+      goto open_socket;
+    }
+
+  /* :family FAMILY -- nil (for Inet), local, or integer.  */
+  tem = Fplist_get (contact, QCfamily);
+  if (NILP (tem))
+    {
+#ifdef AF_INET6
+      family = AF_UNSPEC;
+#else
+      family = AF_INET;
 #endif
     }
+#ifdef HAVE_LOCAL_SOCKETS
+  else if (EQ (tem, Qlocal))
+    family = AF_LOCAL;
+#endif
+#ifdef AF_INET6
+  else if (EQ (tem, Qipv6))
+    family = AF_INET6;
+#endif
+  else if (EQ (tem, Qipv4))
+    family = AF_INET;
+  else if (TYPE_RANGED_INTEGERP (int, tem))
+    family = XINT (tem);
+  else
+    error ("Unknown address family");
 
-  if (s >= 0)
+  /* :service SERVICE -- string, integer (port number), or t (random port).  */
+  service = Fplist_get (contact, QCservice);
+
+  /* :host HOST -- hostname, ip address, or 'local for localhost.  */
+  host = Fplist_get (contact, QChost);
+  if (NILP (host))
     {
-#ifdef DATAGRAM_SOCKETS
-      if (socktype == SOCK_DGRAM)
-       {
-         if (datagram_address[s].sa)
-           emacs_abort ();
-         datagram_address[s].sa = xmalloc (lres->ai_addrlen);
-         datagram_address[s].len = lres->ai_addrlen;
-         if (is_server)
-           {
-             Lisp_Object remote;
-             memset (datagram_address[s].sa, 0, lres->ai_addrlen);
-             if (remote = Fplist_get (contact, QCremote), !NILP (remote))
-               {
-                 int rfamily, rlen;
-                 rlen = get_lisp_to_sockaddr_size (remote, &rfamily);
-                 if (rlen != 0 && rfamily == lres->ai_family
-                     && rlen == lres->ai_addrlen)
-                   conv_lisp_to_sockaddr (rfamily, remote,
-                                          datagram_address[s].sa, rlen);
-               }
-           }
-         else
-           memcpy (datagram_address[s].sa, lres->ai_addr, lres->ai_addrlen);
-       }
+      /* 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
-      contact = Fplist_put (contact, colon_address,
-                           conv_sockaddr_to_lisp (lres->ai_addr, lres->ai_addrlen));
-#ifdef HAVE_GETSOCKNAME
-      if (!is_server)
+       host = build_string ("127.0.0.1");
+    }
+  else
+    {
+      if (EQ (host, Qlocal))
+       /* Depending on setup, "localhost" may map to different IPv4 and/or
+          IPv6 addresses, so it's better to be explicit (Bug#6781).  */
+       host = build_string ("127.0.0.1");
+      CHECK_STRING (host);
+    }
+
+#ifdef HAVE_LOCAL_SOCKETS
+  if (family == AF_LOCAL)
+    {
+      if (!NILP (host))
        {
-         struct sockaddr_in sa1;
-         socklen_t len1 = sizeof (sa1);
-         if (getsockname (s, (struct sockaddr *)&sa1, &len1) == 0)
-           contact = Fplist_put (contact, QClocal,
-                                 conv_sockaddr_to_lisp ((struct sockaddr *)&sa1, len1));
+         message (":family local ignores the :host property");
+         contact = Fplist_put (contact, QChost, Qnil);
+         host = Qnil;
        }
-#endif
+      CHECK_STRING (service);
+      if (sizeof address_un.sun_path <= SBYTES (service))
+       error ("Service name too long");
+      addrinfos = list1 (Fcons (make_number (any_protocol), service));
+      goto open_socket;
     }
+#endif
 
-  immediate_quit = 0;
-
-#ifdef HAVE_GETADDRINFO
-  if (res != &ai)
+  /* Slow down polling to every ten seconds.
+     Some kernels have a bug which causes retrying connect to fail
+     after a connect.  Polling can interfere with gethostbyname too.  */
+#ifdef POLL_FOR_INPUT
+  if (socktype != SOCK_DGRAM)
     {
-      block_input ();
-      freeaddrinfo (res);
-      unblock_input ();
+      record_unwind_protect_void (run_all_atimers);
+      bind_polling_period (10);
     }
 #endif
 
-  if (s < 0)
+  if (!NILP (host))
     {
-      /* If non-blocking got this far - and failed - assume non-blocking is
-        not supported after all.  This is probably a wrong assumption, but
-        the normal blocking calls to open-network-stream handles this error
-        better.  */
-      if (is_non_blocking_client)
-         return Qnil;
-
-      report_file_errno ((is_server
-                         ? "make server process failed"
-                         : "make client process failed"),
-                        contact, xerrno);
+      /* SERVICE can either be a string or int.
+        Convert to a C string for later use by getaddrinfo.  */
+      if (EQ (service, Qt))
+       {
+         portstring = "0";
+         portstringlen = 1;
+       }
+      else if (INTEGERP (service))
+       {
+         portstring = portbuf;
+         portstringlen = sprintf (portbuf, "%"pI"d", XINT (service));
+       }
+      else
+       {
+         CHECK_STRING (service);
+         portstring = SSDATA (service);
+         portstringlen = SBYTES (service);
+       }
     }
 
-  inch = s;
-  outch = s;
+#ifdef HAVE_GETADDRINFO_A
+  if (!NILP (host) && !NILP (Fplist_get (contact, QCnowait)))
+    {
+      ptrdiff_t hostlen = SBYTES (host);
+      struct req
+      {
+       struct gaicb gaicb;
+       struct addrinfo hints;
+       char str[FLEXIBLE_ARRAY_MEMBER];
+      } *req = xmalloc (offsetof (struct req, str)
+                       + hostlen + 1 + portstringlen + 1);
+      dns_request = &req->gaicb;
+      dns_request->ar_name = req->str;
+      dns_request->ar_service = req->str + hostlen + 1;
+      dns_request->ar_request = &req->hints;
+      dns_request->ar_result = NULL;
+      memset (&req->hints, 0, sizeof req->hints);
+      req->hints.ai_family = family;
+      req->hints.ai_socktype = socktype;
+      strcpy (req->str, SSDATA (host));
+      strcpy (req->str + hostlen + 1, portstring);
+
+      int ret = getaddrinfo_a (GAI_NOWAIT, &dns_request, 1, NULL);
+      if (ret)
+       error ("%s/%s getaddrinfo_a error %d", SSDATA (host), portstring, ret);
 
-  if (!NILP (buffer))
-    buffer = Fget_buffer_create (buffer);
-  proc = make_process (name);
+      goto open_socket;
+    }
+#endif /* HAVE_GETADDRINFO_A */
 
-  chan_process[inch] = proc;
+  /* If we have a host, use getaddrinfo to resolve both host and service.
+     Otherwise, use getservbyname to lookup the service.  */
 
-  fcntl (inch, F_SETFL, O_NONBLOCK);
+  if (!NILP (host))
+    {
+      struct addrinfo *res, *lres;
+      int ret;
 
-  p = XPROCESS (proc);
+      immediate_quit = 1;
+      QUIT;
 
-  pset_childp (p, contact);
-  pset_plist (p, Fcopy_sequence (Fplist_get (contact, QCplist)));
-  pset_type (p, Qnetwork);
+      struct addrinfo hints;
+      memset (&hints, 0, sizeof hints);
+      hints.ai_family = family;
+      hints.ai_socktype = socktype;
 
-  pset_buffer (p, buffer);
-  pset_sentinel (p, sentinel);
-  pset_filter (p, filter);
-  pset_log (p, Fplist_get (contact, QClog));
-  if (tem = Fplist_get (contact, QCnoquery), !NILP (tem))
-    p->kill_without_query = 1;
-  if ((tem = Fplist_get (contact, QCstop), !NILP (tem)))
-    pset_command (p, Qt);
-  p->pid = 0;
+      ret = getaddrinfo (SSDATA (host), portstring, &hints, &res);
+      if (ret)
+#ifdef HAVE_GAI_STRERROR
+       {
+         synchronize_system_messages_locale ();
+         char const *str = gai_strerror (ret);
+         if (! NILP (Vlocale_coding_system))
+           str = SSDATA (code_convert_string_norecord
+                         (build_string (str), Vlocale_coding_system, 0));
+         error ("%s/%s %s", SSDATA (host), portstring, str);
+       }
+#else
+       error ("%s/%s getaddrinfo error %d", SSDATA (host), portstring, ret);
+#endif
+      immediate_quit = 0;
 
-  p->open_fd[SUBPROCESS_STDIN] = inch;
-  p->infd  = inch;
-  p->outfd = outch;
+      for (lres = res; lres; lres = lres->ai_next)
+       addrinfos = Fcons (conv_addrinfo_to_lisp (lres), addrinfos);
 
-  /* Discard the unwind protect for closing S, if any.  */
-  specpdl_ptr = specpdl + count1;
+      addrinfos = Fnreverse (addrinfos);
 
-  /* Unwind bind_polling_period and request_sigio.  */
-  unbind_to (count, Qnil);
+      freeaddrinfo (res);
 
-  if (is_server && socktype != SOCK_DGRAM)
-    pset_status (p, Qlisten);
+      goto open_socket;
+    }
 
-  /* Make the process marker point into the process buffer (if any).  */
-  if (BUFFERP (buffer))
-    set_marker_both (p->mark, buffer,
-                    BUF_ZV (XBUFFER (buffer)),
-                    BUF_ZV_BYTE (XBUFFER (buffer)));
+  /* No hostname has been specified (e.g., a local server process).  */
 
-#ifdef NON_BLOCKING_CONNECT
-  if (is_non_blocking_client)
+  if (EQ (service, Qt))
+    port = 0;
+  else if (INTEGERP (service))
+    port = XINT (service);
+  else
     {
-      /* We may get here if connect did succeed immediately.  However,
-        in that case, we still need to signal this like a non-blocking
-        connection.  */
-      pset_status (p, Qconnect);
-      if (!FD_ISSET (inch, &connect_wait_mask))
+      CHECK_STRING (service);
+
+      port = -1;
+      if (SBYTES (service) != 0)
        {
-         FD_SET (inch, &connect_wait_mask);
-         FD_SET (inch, &write_mask);
-         num_pending_connects++;
+         /* Allow the service to be a string containing the port number,
+            because that's allowed if you have getaddrbyname.  */
+         char *service_end;
+         long int lport = strtol (SSDATA (service), &service_end, 10);
+         if (service_end == SSDATA (service) + SBYTES (service))
+           port = lport;
+         else
+           {
+             struct servent *svc_info
+               = getservbyname (SSDATA (service),
+                                socktype == SOCK_DGRAM ? "udp" : "tcp");
+             if (svc_info)
+               port = ntohs (svc_info->s_port);
+           }
        }
     }
-  else
-#endif
-    /* A server may have a client filter setting of Qt, but it must
-       still listen for incoming connects unless it is stopped.  */
-    if ((!EQ (p->filter, Qt) && !EQ (p->command, Qt))
-       || (EQ (p->status, Qlisten) && NILP (p->command)))
-      {
-       FD_SET (inch, &input_wait_mask);
-       FD_SET (inch, &non_keyboard_wait_mask);
-      }
-
-  if (inch > max_process_desc)
-    max_process_desc = inch;
-
-  tem = Fplist_member (contact, QCcoding);
-  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;
 
-    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 (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
-            || (NILP (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, 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 (! (0 <= port && port < 1 << 16))
+    {
+      AUTO_STRING (unknown_service, "Unknown service: %s");
+      xsignal1 (Qerror, CALLN (Fformat, unknown_service, service));
+    }
 
-    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, 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);
+ open_socket:
 
-  pset_decoding_buf (p, empty_unibyte_string);
-  p->decoding_carryover = 0;
-  pset_encoding_buf (p, empty_unibyte_string);
+  if (!NILP (buffer))
+    buffer = Fget_buffer_create (buffer);
+  proc = make_process (name);
+  p = XPROCESS (proc);
+  pset_childp (p, contact);
+  pset_plist (p, Fcopy_sequence (Fplist_get (contact, QCplist)));
+  pset_type (p, Qnetwork);
 
-  p->inherit_coding_system_flag
-    = !(!NILP (tem) || NILP (buffer) || !inherit_process_coding_system);
+  pset_buffer (p, buffer);
+  pset_sentinel (p, sentinel);
+  pset_filter (p, filter);
+  pset_log (p, Fplist_get (contact, QClog));
+  if (tem = Fplist_get (contact, QCnoquery), !NILP (tem))
+    p->kill_without_query = 1;
+  if ((tem = Fplist_get (contact, QCstop), !NILP (tem)))
+    pset_command (p, Qt);
+  p->pid = 0;
+  p->backlog = 5;
+  p->is_non_blocking_client = false;
+  p->is_server = false;
+  p->port = port;
+  p->socktype = socktype;
+#ifdef HAVE_GETADDRINFO_A
+  p->dns_request = 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, host, service, name);
+
+  unbind_to (count, Qnil);
+
+  /* :server BOOL */
+  tem = Fplist_get (contact, QCserver);
+  if (!NILP (tem))
+    {
+      /* Don't support network sockets when non-blocking mode is
+        not available, since a blocked Emacs is not useful.  */
+      p->is_server = true;
+      if (TYPE_RANGED_INTEGERP (int, tem))
+       p->backlog = XINT (tem);
+    }
+
+  /* :nowait BOOL */
+  if (!p->is_server && socktype != SOCK_DGRAM
+      && !NILP (Fplist_get (contact, QCnowait)))
+    p->is_non_blocking_client = true;
+
+#ifdef HAVE_GETADDRINFO_A
+  /* With async address resolution, the list of addresses is empty, so
+     postpone connecting to the server. */
+  if (!p->is_server && NILP (addrinfos))
+    {
+      p->dns_request = dns_request;
+      p->status = list1 (Qconnect);
+      return proc;
+    }
+#endif
 
+  connect_network_socket (proc, addrinfos, use_external_socket_p);
   return proc;
 }
 
@@ -4085,19 +4316,6 @@ Data that is unavailable is returned as nil.  */)
 #endif
 }
 
-/* If program file NAME starts with /: for quoting a magic
-   name, remove that, preserving the multibyteness of NAME.  */
-
-Lisp_Object
-remove_slash_colon (Lisp_Object name)
-{
-  return
-    ((SBYTES (name) > 2 && SREF (name, 0) == '/' && SREF (name, 1) == ':')
-     ? make_specified_string (SSDATA (name) + 2, SCHARS (name) - 2,
-                             SBYTES (name) - 2, STRING_MULTIBYTE (name))
-     : name);
-}
-
 /* Turn off input and output for process PROC.  */
 
 static void
@@ -4141,7 +4359,6 @@ deactivate_process (Lisp_Object proc)
       chan_process[inchannel] = Qnil;
       FD_CLR (inchannel, &input_wait_mask);
       FD_CLR (inchannel, &non_keyboard_wait_mask);
-#ifdef NON_BLOCKING_CONNECT
       if (FD_ISSET (inchannel, &connect_wait_mask))
        {
          FD_CLR (inchannel, &connect_wait_mask);
@@ -4149,7 +4366,6 @@ deactivate_process (Lisp_Object proc)
          if (--num_pending_connects < 0)
            emacs_abort ();
        }
-#endif
       if (inchannel == max_process_desc)
        {
          /* We just closed the highest-numbered process input descriptor,
@@ -4273,15 +4489,7 @@ server_accept_connection (Lisp_Object server, int channel)
   if (s < 0)
     {
       int code = errno;
-
-      if (code == EAGAIN)
-       return;
-#ifdef EWOULDBLOCK
-      if (code == EWOULDBLOCK)
-       return;
-#endif
-
-      if (!NILP (ps->log))
+      if (!would_block (code) && !NILP (ps->log))
        call3 (ps->log, server, Qnil,
               concat3 (build_string ("accept failed with code"),
                        Fnumber_to_string (make_number (code)),
@@ -4453,6 +4661,87 @@ 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 addrinfos = Qnil;
+
+  /* Sanity check. */
+  if (! p->dns_request)
+    return Qnil;
+
+  int ret = gai_error (p->dns_request);
+  if (ret == EAI_INPROGRESS)
+    return Qt;
+
+  /* We got a response. */
+  if (ret == 0)
+    {
+      struct addrinfo *res;
+
+      for (res = p->dns_request->ar_result; res; res = res->ai_next)
+       addrinfos = Fcons (conv_addrinfo_to_lisp (res), addrinfos);
+
+      addrinfos = Fnreverse (addrinfos);
+    }
+  /* The DNS lookup failed. */
+  else if (connecting_status (p->status))
+    {
+      deactivate_process (proc);
+      pset_status (p, (list2
+                      (Qfailed,
+                       concat3 (build_string ("Name lookup of "),
+                                build_string (p->dns_request->ar_name),
+                                build_string (" failed")))));
+    }
+
+  free_dns_request (proc);
+
+  /* This process should not already be connected (or killed). */
+  if (! connecting_status (p->status))
+    return Qnil;
+
+  return addrinfos;
+}
+
+#endif /* HAVE_GETADDRINFO_A */
+
+static void
+wait_for_socket_fds (Lisp_Object process, char const *name)
+{
+  while (XPROCESS (process)->infd < 0
+        && connecting_status (XPROCESS (process)->status))
+    {
+      add_to_log ("Waiting for socket from %s...", build_string (name));
+      wait_reading_process_output (0, 20 * 1000 * 1000, 0, 0, Qnil, NULL, 0);
+    }
+}
+
+static void
+wait_while_connecting (Lisp_Object process)
+{
+  while (connecting_status (XPROCESS (process)->status))
+    {
+      add_to_log ("Waiting for connection...");
+      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 (XPROCESS (process)->gnutls_p
+        && XPROCESS (process)->gnutls_initstage != GNUTLS_STAGE_READY)
+    {
+      add_to_log ("Waiting for TLS...");
+      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
@@ -4531,6 +4820,9 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
   struct timespec got_output_end_time = invalid_timespec ();
   enum { MINIMUM = -1, TIMEOUT, INFINITY } wait;
   int got_some_output = -1;
+#if defined HAVE_GETADDRINFO_A || defined HAVE_GNUTLS
+  bool retry_for_async;
+#endif
   ptrdiff_t count = SPECPDL_INDEX ();
 
   /* Close to the current time if known, an invalid timespec otherwise.  */
@@ -4578,6 +4870,60 @@ 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
+      {
+       Lisp_Object process_list_head, aproc;
+       struct Lisp_Process *p;
+
+       retry_for_async = false;
+       FOR_EACH_PROCESS(process_list_head, aproc)
+         {
+           p = XPROCESS (aproc);
+
+           if (! wait_proc || p == wait_proc)
+             {
+#ifdef HAVE_GETADDRINFO_A
+               /* Check for pending DNS requests. */
+               if (p->dns_request)
+                 {
+                   Lisp_Object addrinfos = check_for_dns (aproc);
+                   if (!NILP (addrinfos) && !EQ (addrinfos, Qt))
+                     connect_network_socket (aproc, addrinfos, Qnil);
+                   else
+                     retry_for_async = true;
+                 }
+#endif
+#ifdef HAVE_GNUTLS
+               /* Continue TLS negotiation. */
+               if (p->gnutls_initstage == GNUTLS_STAGE_HANDSHAKE_TRIED
+                   && p->is_non_blocking_client)
+                 {
+                   gnutls_try_handshake (p);
+                   p->gnutls_handshakes_tried++;
+
+                   if (p->gnutls_initstage == GNUTLS_STAGE_READY)
+                     {
+                       gnutls_verify_boot (aproc, Qnil);
+                       finish_after_tls_connection (aproc);
+                     }
+                   else
+                     {
+                       retry_for_async = true;
+                       if (p->gnutls_handshakes_tried
+                           > GNUTLS_EMACS_HANDSHAKES_LIMIT)
+                         {
+                           deactivate_process (aproc);
+                           pset_status (p, list2 (Qfailed,
+                                                  build_string ("TLS negotiation failed")));
+                         }
+                     }
+                 }
+#endif
+             }
+         }
+      }
+#endif /* GETADDRINFO_A or GNUTLS */
+
       /* Compute time from now till when time limit is up.  */
       /* Exit if already run out.  */
       if (wait == TIMEOUT)
@@ -4660,11 +5006,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
          timeout = make_timespec (0, 0);
          if ((pselect (max (max_process_desc, max_input_desc) + 1,
                        &Atemp,
-#ifdef NON_BLOCKING_CONNECT
                        (num_pending_connects > 0 ? &Ctemp : NULL),
-#else
-                       NULL,
-#endif
                        NULL, &timeout, NULL)
               <= 0))
            {
@@ -4682,7 +5024,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
        update_status (wait_proc);
       if (wait_proc
          && ! EQ (wait_proc->status, Qrun)
-         && ! EQ (wait_proc->status, Qconnect))
+         && ! connecting_status (wait_proc->status))
        {
          bool read_some_bytes = false;
 
@@ -4698,12 +5040,8 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
                  int nread = read_process_output (proc, wait_proc->infd);
                  if (nread < 0)
                    {
-                   if (errno == EIO || errno == EAGAIN)
-                     break;
-#ifdef EWOULDBLOCK
-                   if (errno == EWOULDBLOCK)
-                     break;
-#endif
+                     if (errno == EIO || would_block (errno))
+                       break;
                    }
                  else
                    {
@@ -4836,6 +5174,15 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
          if (timeout.tv_sec > 0 || timeout.tv_nsec > 0)
            now = invalid_timespec ();
 
+#if defined HAVE_GETADDRINFO_A || defined HAVE_GNUTLS
+         if (retry_for_async
+             && (timeout.tv_sec > 0 || timeout.tv_nsec > ASYNC_RETRY_NSEC))
+           {
+             timeout.tv_sec = 0;
+             timeout.tv_nsec = ASYNC_RETRY_NSEC;
+           }
+#endif
+
 #if defined (HAVE_NS)
           nfds = ns_select
 #elif defined (HAVE_GLIB)
@@ -4922,16 +5269,22 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
              haven't lowered our timeout due to timers or SIGIO and
              have waited a long amount of time due to repeated
              timers.  */
+         struct timespec huge_timespec
+           = make_timespec (TYPE_MAXIMUM (time_t), 2 * TIMESPEC_RESOLUTION);
+         struct timespec cmp_time = huge_timespec;
          if (wait < TIMEOUT)
            break;
-         struct timespec cmp_time
-           = (wait == TIMEOUT
-              ? end_time
-              : (!process_skipped && got_some_output > 0
-                 && (timeout.tv_sec > 0 || timeout.tv_nsec > 0))
-              ? got_output_end_time
-              : invalid_timespec ());
-         if (timespec_valid_p (cmp_time))
+         if (wait == TIMEOUT)
+           cmp_time = end_time;
+         if (!process_skipped && got_some_output > 0
+             && (timeout.tv_sec > 0 || timeout.tv_nsec > 0))
+           {
+             if (!timespec_valid_p (got_output_end_time))
+               break;
+             if (timespec_cmp (got_output_end_time, cmp_time) < 0)
+               cmp_time = got_output_end_time;
+           }
+         if (timespec_cmp (cmp_time, huge_timespec) < 0)
            {
              now = current_timespec ();
              if (timespec_cmp (cmp_time, now) <= 0)
@@ -5078,11 +5431,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
                  if (do_display)
                    redisplay_preserve_echo_area (12);
                }
-#ifdef EWOULDBLOCK
-             else if (nread == -1 && errno == EWOULDBLOCK)
-               ;
-#endif
-             else if (nread == -1 && errno == EAGAIN)
+             else if (nread == -1 && would_block (errno))
                ;
 #ifdef WINDOWSNT
              /* FIXME: Is this special case still needed?  */
@@ -5147,7 +5496,6 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
                                 list2 (Qexit, make_number (256)));
                }
            }
-#ifdef NON_BLOCKING_CONNECT
          if (FD_ISSET (channel, &Writeok)
              && FD_ISSET (channel, &connect_wait_mask))
            {
@@ -5164,15 +5512,16 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
 
              p = XPROCESS (proc);
 
-#ifdef GNU_LINUX
-             /* getsockopt(,,SO_ERROR,,) is said to hang on some systems.
-                So only use it on systems where it is known to work.  */
+#ifndef WINDOWSNT
              {
                socklen_t xlen = sizeof (xerrno);
                if (getsockopt (channel, SOL_SOCKET, SO_ERROR, &xerrno, &xlen))
                  xerrno = errno;
              }
 #else
+             /* On MS-Windows, getsockopt clears the error for the
+                entire process, which may not be the right thing; see
+                w32.c.  Use getpeername instead.  */
              {
                struct sockaddr pname;
                socklen_t pnamelen = sizeof (pname);
@@ -5191,17 +5540,36 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
 #endif
              if (xerrno)
                {
-                 p->tick = ++process_tick;
-                 pset_status (p, list2 (Qfailed, make_number (xerrno)));
+                 Lisp_Object addrinfos
+                   = connecting_status (p->status) ? XCDR (p->status) : Qnil;
+                 if (!NILP (addrinfos))
+                   XSETCDR (p->status, XCDR (addrinfos));
+                 else
+                   {
+                     p->tick = ++process_tick;
+                     pset_status (p, list2 (Qfailed, make_number (xerrno)));
+                   }
                  deactivate_process (proc);
+                 if (!NILP (addrinfos))
+                   connect_network_socket (proc, addrinfos, Qnil);
                }
              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"));
+#ifdef HAVE_GNUTLS
+                 /* If we have an incompletely set up TLS connection,
+                    then defer the sentinel signaling until
+                    later. */
+                 if (NILP (p->gnutls_boot_parameters)
+                     && !p->gnutls_p)
+#endif
+                   {
+                     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))
                    {
@@ -5210,7 +5578,6 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
                    }
                }
            }
-#endif /* NON_BLOCKING_CONNECT */
        }                       /* End for each file descriptor.  */
     }                          /* End while exit conditions not met.  */
 
@@ -5658,6 +6025,12 @@ 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))
@@ -5806,11 +6179,7 @@ send_process (Lisp_Object proc, const char *buf, ptrdiff_t len,
 
          if (rv < 0)
            {
-             if (errno == EAGAIN
-#ifdef EWOULDBLOCK
-                 || errno == EWOULDBLOCK
-#endif
-                 )
+             if (would_block (errno))
                /* Buffer is full.  Wait, accepting input;
                   that may allow the program
                   to finish doing output and read more.  */
@@ -5875,7 +6244,10 @@ nil, indicating the current buffer's process.
 Called from program, takes three arguments, PROCESS, START and END.
 If the region is more than 500 characters long,
 it is sent in several bunches.  This may happen even for shorter regions.
-Output from processes can arrive in between bunches.  */)
+Output from processes can arrive in between bunches.
+
+If PROCESS is a non-blocking network process that hasn't been fully
+set up yet, this function will block until socket setup has completed.  */)
   (Lisp_Object process, Lisp_Object start, Lisp_Object end)
 {
   Lisp_Object proc = get_process (process);
@@ -5889,6 +6261,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 ());
 
@@ -5902,12 +6277,14 @@ PROCESS may be a process, a buffer, the name of a process or buffer, or
 nil, indicating the current buffer's process.
 If STRING is more than 500 characters long,
 it is sent in several bunches.  This may happen even for shorter strings.
-Output from processes can arrive in between bunches.  */)
+Output from processes can arrive in between bunches.
+
+If PROCESS is a non-blocking network process that hasn't been fully
+set up yet, this function will block until socket setup has completed.  */)
   (Lisp_Object process, Lisp_Object string)
 {
-  Lisp_Object proc;
   CHECK_STRING (string);
-  proc = get_process (process);
+  Lisp_Object proc = get_process (process);
   send_process (proc, SSDATA (string),
                SBYTES (string), string);
   return Qnil;
@@ -5949,12 +6326,8 @@ process group.  */)
 {
   /* Initialize in case ioctl doesn't exist or gives an error,
      in a way that will cause returning t.  */
-  pid_t gid;
-  Lisp_Object proc;
-  struct Lisp_Process *p;
-
-  proc = get_process (process);
-  p = XPROCESS (proc);
+  Lisp_Object proc = get_process (process);
+  struct Lisp_Process *p = XPROCESS (proc);
 
   if (!EQ (p->type, Qreal))
     error ("Process %s is not a subprocess",
@@ -5963,7 +6336,7 @@ process group.  */)
     error ("Process %s is not active",
           SDATA (p->name));
 
-  gid = emacs_get_tty_pgrp (p);
+  pid_t gid = emacs_get_tty_pgrp (p);
 
   if (gid == p->pid)
     return Qnil;
@@ -6034,7 +6407,7 @@ process_send_signal (Lisp_Object process, int signo, Lisp_Object current_group,
          break;
 
        case SIGTSTP:
-#if defined (VSWTCH) && !defined (PREFER_VSUSP)
+#ifdef VSWTCH
          sig_char = &t.c_cc[VSWTCH];
 #else
          sig_char = &t.c_cc[VSUSP];
@@ -6322,10 +6695,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];
@@ -6650,7 +7028,7 @@ status_notify (struct Lisp_Process *deleting_process,
 
          /* If process is still active, read any output that remains.  */
          while (! EQ (p->filter, Qt)
-                && ! EQ (p->status, Qconnect)
+                && ! connecting_status (p->status)
                 && ! EQ (p->status, Qlisten)
                 /* Network or serial process not stopped:  */
                 && ! EQ (p->command, Qt)
@@ -6770,22 +7148,24 @@ DEFUN ("set-process-coding-system", Fset_process_coding_system,
        Sset_process_coding_system, 1, 3, 0,
        doc: /* Set coding systems of PROCESS to DECODING and ENCODING.
 DECODING will be used to decode subprocess output and ENCODING to
-encode subprocess input.  */)
-  (register Lisp_Object process, Lisp_Object decoding, Lisp_Object encoding)
+encode subprocess input. */)
+  (Lisp_Object process, Lisp_Object decoding, Lisp_Object encoding)
 {
-  register struct Lisp_Process *p;
-
   CHECK_PROCESS (process);
-  p = XPROCESS (process);
-  if (p->infd < 0)
-    error ("Input file descriptor of %s closed", SDATA (p->name));
-  if (p->outfd < 0)
-    error ("Output file descriptor of %s closed", SDATA (p->name));
+
+  struct Lisp_Process *p = XPROCESS (process);
+
   Fcheck_coding_system (decoding);
   Fcheck_coding_system (encoding);
   encoding = coding_inherit_eol_type (encoding, Qnil);
   pset_decode_coding_system (p, decoding);
   pset_encode_coding_system (p, encoding);
+
+  /* If the sockets haven't been set up yet, the final setup part of
+     this will be called asynchronously. */
+  if (p->infd < 0 || p->outfd < 0)
+    return Qnil;
+
   setup_process_coding_systems (process);
 
   return Qnil;
@@ -6810,13 +7190,18 @@ all character code conversion except for end-of-line conversion is
 suppressed.  */)
   (Lisp_Object process, Lisp_Object flag)
 {
-  register struct Lisp_Process *p;
-
   CHECK_PROCESS (process);
-  p = XPROCESS (process);
+
+  struct Lisp_Process *p = XPROCESS (process);
   if (NILP (flag))
     pset_decode_coding_system
       (p, raw_text_coding_system (p->decode_coding_system));
+
+  /* If the sockets haven't been set up yet, the final setup part of
+     this will be called asynchronously. */
+  if (p->infd < 0 || p->outfd < 0)
+    return Qnil;
+
   setup_process_coding_systems (process);
 
   return Qnil;
@@ -6827,14 +7212,11 @@ DEFUN ("process-filter-multibyte-p", Fprocess_filter_multibyte_p,
        doc: /* Return t if a multibyte string is given to PROCESS's filter.*/)
   (Lisp_Object process)
 {
-  register struct Lisp_Process *p;
-  struct coding_system *coding;
-
   CHECK_PROCESS (process);
-  p = XPROCESS (process);
+  struct Lisp_Process *p = XPROCESS (process);
   if (p->infd < 0)
     return Qnil;
-  coding = proc_decode_coding_system[p->infd];
+  struct coding_system *coding = proc_decode_coding_system[p->infd];
   return (CODING_FOR_UNIBYTE (coding) ? Qnil : Qt);
 }
 
@@ -7118,6 +7500,19 @@ add_timer_wait_descriptor (int fd)
 
 #endif /* HAVE_TIMERFD */
 
+/* If program file NAME starts with /: for quoting a magic
+   name, remove that, preserving the multibyteness of NAME.  */
+
+Lisp_Object
+remove_slash_colon (Lisp_Object name)
+{
+  return
+    ((SBYTES (name) > 2 && SREF (name, 0) == '/' && SREF (name, 1) == ':')
+     ? make_specified_string (SSDATA (name) + 2, SCHARS (name) - 2,
+                             SBYTES (name) - 2, STRING_MULTIBYTE (name))
+     : name);
+}
+
 /* Add DESC to the set of keyboard input descriptors.  */
 
 void
@@ -7375,10 +7770,10 @@ catch_child_signal (void)
 /* This is not called "init_process" because that is the name of a
    Mach system call, so it would cause problems on Darwin systems.  */
 void
-init_process_emacs (void)
+init_process_emacs (int sockfd)
 {
 #ifdef subprocesses
-  register int i;
+  int i;
 
   inhibit_sentinels = 0;
 
@@ -7401,12 +7796,11 @@ init_process_emacs (void)
   FD_ZERO (&non_process_wait_mask);
   FD_ZERO (&write_mask);
   max_process_desc = max_input_desc = -1;
+  external_sock_fd = sockfd;
   memset (fd_callback_info, 0, sizeof (fd_callback_info));
 
-#ifdef NON_BLOCKING_CONNECT
   FD_ZERO (&connect_wait_mask);
   num_pending_connects = 0;
-#endif
 
   process_output_delay_count = 0;
   process_output_skip = 0;
@@ -7501,6 +7895,9 @@ syms_of_process (void)
   DEFSYM (QCserver, ":server");
   DEFSYM (QCnowait, ":nowait");
   DEFSYM (QCsentinel, ":sentinel");
+  DEFSYM (QCuse_external_socket, ":use-external-socket");
+  DEFSYM (QCtls_parameters, ":tls-parameters");
+  DEFSYM (Qnsm_verify_connection, "nsm-verify-connection");
   DEFSYM (QClog, ":log");
   DEFSYM (QCnoquery, ":noquery");
   DEFSYM (QCstop, ":stop");
@@ -7643,13 +8040,6 @@ The variable takes effect when `start-process' is called.  */);
   defsubr (&Sset_process_filter_multibyte);
   defsubr (&Sprocess_filter_multibyte_p);
 
-#endif /* subprocesses */
-
-  defsubr (&Sget_buffer_process);
-  defsubr (&Sprocess_inherit_coding_system_flag);
-  defsubr (&Slist_system_processes);
-  defsubr (&Sprocess_attributes);
-
  {
    Lisp_Object subfeatures = Qnil;
    const struct socket_options *sopt;
@@ -7657,9 +8047,7 @@ The variable takes effect when `start-process' is called.  */);
 #define ADD_SUBFEATURE(key, val) \
   subfeatures = pure_cons (pure_cons (key, pure_cons (val, Qnil)), subfeatures)
 
-#ifdef NON_BLOCKING_CONNECT
    ADD_SUBFEATURE (QCnowait, Qt);
-#endif
 #ifdef DATAGRAM_SOCKETS
    ADD_SUBFEATURE (QCtype, Qdatagram);
 #endif
@@ -7684,4 +8072,10 @@ The variable takes effect when `start-process' is called.  */);
    Fprovide (intern_c_string ("make-network-process"), subfeatures);
  }
 
+#endif /* subprocesses */
+
+  defsubr (&Sget_buffer_process);
+  defsubr (&Sprocess_inherit_coding_system_flag);
+  defsubr (&Slist_system_processes);
+  defsubr (&Sprocess_attributes);
 }