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