86351e141ea3ce1d6465d0293a5247164b0494eb
galt
  Mon Jan 6 11:13:33 2020 -0800
If ipv6 is disabled, retry with ipv4-only listening socket. Tweak packet ip address filtering routine to tolerate the ipv4 address format. fixes #24730.

diff --git src/lib/net.c src/lib/net.c
index 2e896be..4f8a5a5 100644
--- src/lib/net.c
+++ src/lib/net.c
@@ -6,30 +6,31 @@
 #include "common.h"
 #include <signal.h>
 #include <errno.h>
 #include <string.h>
 #include <sys/time.h>
 #include <pthread.h>
 #include "errAbort.h"
 #include "hash.h"
 #include "net.h"
 #include "linefile.h"
 #include "base64.h"
 #include "cheapcgi.h"
 #include "https.h"
 #include "sqlNum.h"
 #include "obscure.h"
+#include "errCatch.h"
 
 /* Brought errno in to get more useful error messages */
 extern int errno;
 
 static int netStreamSocketFromAddrInfo(struct addrinfo *address)
 /* Create a socket from addrinfo structure.  
  * Complain and return something negative if can't. */
 {
 int sd = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
 if (sd < 0)
     warn("Couldn't make %s socket.", familyToString(address->ai_family));
 
 return sd;
 }
 
@@ -259,32 +260,72 @@
 {
 int sd = netConnect(hostName, port);
 if (sd < 0)
    noWarnAbort();
 return sd;
 }
 
 int netMustConnectTo(char *hostName, char *portName)
 /* Start connection with a server and a port that needs to be converted to integer */
 {
 if (!isdigit(portName[0]))
     errAbort("netConnectTo: ports must be numerical, not %s", portName);
 return netMustConnect(hostName, atoi(portName));
 }
 
+int netAcceptingSocket4Only(int port, int queueSize)
+/* Create an IPV4 socket that can accept connections from 
+ * only IPV4 clients on the current machine. Useful for systems with ipv6 disabled. */
+{
+struct sockaddr_in serverAddr;
+int sd;
 
-int netAcceptingSocket(int port, int queueSize)
+netBlockBrokenPipes();
+
+// ipv4 listening socket accepts ipv4 connections.
+if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+    {
+    errAbort("socket() failed");
+    }
+
+// Allow local address reuse when server is restarted without waiting.
+int on = -1;
+if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on,sizeof(on)) < 0)
+    {
+    errAbort("setsockopt(SO_REUSEADDR) failed");
+    }
+
+ZeroVar(&serverAddr);
+serverAddr.sin_family = AF_INET;
+serverAddr.sin_port = htons(port);
+serverAddr.sin_addr.s_addr = INADDR_ANY;
+
+if (bind(sd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
+    {
+    errAbort("Couldn't bind socket to %d: %s", port, strerror(errno));
+    }
+
+if (listen(sd, queueSize) < 0)
+    {
+    errAbort("listen() failed");
+    }
+
+return sd;
+}
+
+
+int netAcceptingSocket6n4(int port, int queueSize)
 /* Create an IPV6 socket that can accept connections from 
  * both IPV4 and IPV6 clients on the current machine. */
 {
 struct sockaddr_in6 serverAddr;
 int sd;
 
 netBlockBrokenPipes();
 
 // Hybrid dual stack ipv6 listening socket accepts ipv6 and ipv4 mapped connections.
 if ((sd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
     {
     errAbort("socket() failed");
     }
 
 // Allow local address reuse when server is restarted without waiting.
@@ -307,57 +348,99 @@
 serverAddr.sin6_port   = htons(port);
 serverAddr.sin6_addr   = in6addr_any;
 if (bind(sd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
     {
     errAbort("Couldn't bind socket to %d: %s", port, strerror(errno));
     }
 
 if (listen(sd, queueSize) < 0)
     {
     errAbort("listen() failed");
     }
 
 return sd;
 }
 
+
+int netAcceptingSocket(int port, int queueSize)
+/* Create an IPV6 socket that can accept connections from 
+ * both IPV4 and IPV6 clients on the current machine.
+ * OR Failover to making IPv4 Only socket if IPv6 is disabled. */
+{
+int sd = -1;
+struct errCatch *errCatch = errCatchNew();
+if (errCatchStart(errCatch))
+    {
+    sd = netAcceptingSocket6n4(port, queueSize);
+    }
+errCatchEnd(errCatch);
+if (errCatch->gotError)
+    {
+    // if ipv6 is disabled, fall back to trying ipv4 only listen socket
+    warn("%s", errCatch->message->string);
+    warn("Retrying listen socket using ipv4 only.");
+    sd = netAcceptingSocket4Only(port, queueSize);
+    }
+errCatchFree(&errCatch);
+if (sd == -1)
+  errAbort("unable to open listening socket");
+return sd;
+}
+
 int netAccept(int sd)
 /* Accept incoming connection from socket descriptor. */
 {
 socklen_t fromLen;
 return accept(sd, NULL, &fromLen);
 }
 
 int netAcceptFrom(int acceptor, struct cidr *subnet)
 /* Wait for incoming connection from socket descriptor
  * from IP address in subnet.  Subnet is something
  * returned from internetParseSubnetCidr. 
  * Subnet may be NULL. */
 {
-struct sockaddr_storage theirAddr;
-ZeroVar(&theirAddr);
 for (;;)
     {
     int sd = accept(acceptor, NULL, NULL);
     if (sd >= 0)
 	{
 	if (subnet == NULL)
 	    return sd;
 	else
 	    {
+	    socklen_t len;
+	    struct sockaddr_storage addr;
+	    char ipStr[INET6_ADDRSTRLEN];
+
+	    len = sizeof addr;
+	    getpeername(sd, (struct sockaddr*)&addr, &len);
+
+            getAddrAsString6n4(&addr, ipStr, sizeof ipStr);
+
+	    if (!strchr(ipStr, ':'))   // convert ipv4 to ipv6-mapped
+		{
+		// prepend "::ffff:" to ipStr
+	        char temp[INET6_ADDRSTRLEN];
+		safef(temp, sizeof temp, "::ffff:%s", ipStr);
+		safecpy(ipStr, sizeof ipStr, temp);
+		}
+	    // convert back to ipv6 address.
 	    struct sockaddr_in6 clientAddr;
-	    unsigned int addrLen=sizeof(clientAddr);
-	    getpeername(sd, (struct sockaddr *)&clientAddr, &addrLen);
+	    internetIpStringToIp6(ipStr, &clientAddr.sin6_addr);
+
+            // see if it is in the allowed subnet
 	    if (internetIpInSubnetCidr(&clientAddr.sin6_addr, subnet))
 		{
 		return sd;
 		}
 	    else
 		{
 		close(sd);
 		}
 	    }
 	}
     }
 }
 
 FILE *netFileFromSocket(int socket)
 /* Wrap a FILE around socket.  This should be fclose'd