Subject: bug#17976: 24.3; url-retrieve-synchronously doesn't fallback to IPv4

From: Paul Eggert <[email-masked]>
Date: Sun, 12 Jun 2016 11:36:43 -0700

I reproduced this bug in a test Fedora 23 environment where attempting to 
connect to one address failed (connection refused). The attached patch to Emacs 
master causes Emacs to try addresses addresses until one succeeds. Does this fix 
your problem? I suspect the code should check for error codes other than 
ECONNREFUSED, but I don't know what the list of such codes should be.
From 958a23698c9f1b9b804aa79d410b11b07998b223 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Sun, 12 Jun 2016 11:08:41 -0700
Subject: [PATCH] Try other addresses when connecting to multihomed

* src/process.c (decode_status, Fmake_network_process):
Support (connect . ADDRINFOS) status.
(connecting): New function, to support (connect . ADDRINFOS).
(connect_network_socket, check_for_dns, wait_for_socket_fds)
(wait_while_connecting, wait_reading_process_output, status_notify):
Use it.
(connect_network_socket) [!WINDOWSNT]:
If the connection failed with ECONNREFUSED and there are other
addresses to try, do not signal an error; instead, loop around
to try the next address.
(wait_reading_process_output): Advance to the next address
if there are multiple addresses and the first remaining address
failed.
* src/process.h (struct Lisp_Process.status): Adjust comment
to describe (connect . ADDRINFOS).
---
 src/process.c | 45 ++++++++++++++++++++++++++++++++-------------
 src/process.h |  4 +++-
 2 files changed, 35 insertions(+), 14 deletions(-)

diff --git a/src/process.c b/src/process.c
index e669278..a72bf20 100644
--- a/src/process.c
+++ b/src/process.c
@@ -542,6 +542,9 @@ decode_status (Lisp_Object l, Lisp_Object *symbol, Lisp_Object *code,
 {
   Lisp_Object tem;
 
+  if (CONSP (l) && EQ (XCAR (l), Qconnect))
+    l = XCAR (l);
+
   if (SYMBOLP (l))
     {
       *symbol = l;
@@ -3106,6 +3109,12 @@ finish_after_tls_connection (Lisp_Object proc)
 }
 #endif
 
+static bool
+connecting (struct Lisp_Process *p)
+{
+  return CONSP (p->status) && EQ (XCAR (p->status), Qconnect);
+}
+
 static void
 connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos,
                         Lisp_Object use_external_socket_p)
@@ -3288,9 +3297,10 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos,
 	  eassert (FD_ISSET (s, &fdset));
 	  if (getsockopt (s, SOL_SOCKET, SO_ERROR, &xerrno, &len) < 0)
 	    report_file_error ("Failed getsockopt", Qnil);
-	  if (xerrno)
+	  if (xerrno == 0)
+	    break;
+	  if (xerrno != ECONNREFUSED || NILP (addrinfos))
 	    report_file_errno ("Failed connect", Qnil, xerrno);
-	  break;
 	}
 #endif /* !WINDOWSNT */
 
@@ -3399,7 +3409,8 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos,
       /* 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 (! (connecting (p) && EQ (XCDR (p->status), addrinfos)))
+	pset_status (p, Fcons (Qconnect, addrinfos));
       if (!FD_ISSET (inch, &connect_wait_mask))
 	{
 	  FD_SET (inch, &connect_wait_mask);
@@ -3960,7 +3971,7 @@ usage: (make-network-process &rest ARGS)  */)
   if (!p->is_server && NILP (addrinfos))
     {
       p->dns_request = dns_request;
-      p->status = Qconnect;
+      p->status = list1 (Qconnect);
       return proc;
     }
 #endif
@@ -4673,7 +4684,7 @@ check_for_dns (Lisp_Object proc)
       addrinfos = Fnreverse (addrinfos);
     }
   /* The DNS lookup failed. */
-  else if (EQ (p->status, Qconnect))
+  else if (connecting (p))
     {
       deactivate_process (proc);
       pset_status (p, (list2
@@ -4686,7 +4697,7 @@ check_for_dns (Lisp_Object proc)
   free_dns_request (proc);
 
   /* This process should not already be connected (or killed). */
-  if (!EQ (p->status, Qconnect))
+  if (! connecting (p))
     return Qnil;
 
   return addrinfos;
@@ -4697,8 +4708,7 @@ check_for_dns (Lisp_Object proc)
 static void
 wait_for_socket_fds (Lisp_Object process, char const *name)
 {
-  while (XPROCESS (process)->infd < 0
-	 && EQ (XPROCESS (process)->status, Qconnect))
+  while (XPROCESS (process)->infd < 0 && connecting (XPROCESS (process)))
     {
       add_to_log ("Waiting for socket from %s...", build_string (name));
       wait_reading_process_output (0, 20 * 1000 * 1000, 0, 0, Qnil, NULL, 0);
@@ -4708,7 +4718,7 @@ wait_for_socket_fds (Lisp_Object process, char const *name)
 static void
 wait_while_connecting (Lisp_Object process)
 {
-  while (EQ (XPROCESS (process)->status, Qconnect))
+  while (connecting (XPROCESS (process)))
     {
       add_to_log ("Waiting for connection...");
       wait_reading_process_output (0, 20 * 1000 * 1000, 0, 0, Qnil, NULL, 0);
@@ -5010,7 +5020,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 (wait_proc))
 	{
 	  bool read_some_bytes = false;
 
@@ -5520,9 +5530,18 @@ 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 (p) ? 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
 		{
@@ -6999,7 +7018,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 (p)
 		 && ! EQ (p->status, Qlisten)
 		 /* Network or serial process not stopped:  */
 		 && ! EQ (p->command, Qt)
diff --git a/src/process.h b/src/process.h
index 4430377..6c227bc 100644
--- a/src/process.h
+++ b/src/process.h
@@ -83,7 +83,9 @@ struct Lisp_Process
     Lisp_Object mark;
 
     /* Symbol indicating status of process.
-       This may be a symbol: run, open, closed, listen, connect, or failed.
+       This may be a symbol: run, open, closed, listen, or failed.
+       Or it may be a pair (connect . ADDRINFOS) where ADDRINFOS is
+       a list of remaining (PROTOCOL . ADDRINFO) pairs to try.
        Or it may be (failed ERR) where ERR is an integer, string or symbol.
        Or it may be a list, whose car is stop, exit or signal
        and whose cdr is a pair (EXIT_CODE . COREDUMP_FLAG)
-- 
2.5.5