5359edc160de518d8e43fdd3448365c15b912c3c
galt
  Mon Jul 22 11:48:10 2019 -0700
Added ipv6 support. Listening processes us hybrid dual stack feature of OS to simplify implementation and use a single listening socket. Works with both TCP and UDP. Parasol working. geoIp also updated and ready for IPv6. Should be invisible to most users, while providing connections via ipv6 where available. Supports both ipv4 and ipv6.

diff --git src/lib/rudp.c src/lib/rudp.c
index 2ada0cf..653c7e4 100644
--- src/lib/rudp.c
+++ src/lib/rudp.c
@@ -139,66 +139,75 @@
 	}
     freeDlList(&ru->recvList);
     hashFree(&ru->recvHash);
     }
 freez(pRu);
 }
 
 struct rudp *rudpOpen() 
 /* Open up an unbound rudp.   This is suitable for
  * writing to and for reading responses.  However 
  * you'll want to rudpBind if you want to listen for
  * incoming messages.   Call rudpClose() when done 
  * with this one.  Warns and returns NULL if there is
  * a problem. */
 {
-int sd = socket(AF_INET,  SOCK_DGRAM, IPPROTO_UDP);
+int sd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
 if (sd < 0)
     {
     warn("Couldn't open socket in rudpOpen %s", strerror(errno));
     return NULL;
     }
 return rudpNew(sd);
 }
 
 struct rudp *rudpMustOpen()
 /* Open up unbound rudp.  Warn and die if there is a problem.  */
 {
 struct rudp *ru = rudpOpen();
 if (ru == NULL)
     noWarnAbort();
 return ru;
 }
 
-struct rudp *rudpOpenBound(struct sockaddr_in *sai)
+struct rudp *rudpOpenBound(struct sockaddr_storage *sai)
 /* Open up a rudp socket bound to a particular port and address.
  * Use this rather than rudpOpen if you want to wait for
  * messages at a specific address in a server or the like. */
 {
+// we should always bind listeners to IPV6 for dual stack
 struct rudp *ru = rudpOpen();  
 if (ru != NULL)
     {
-    if (bind(ru->socket, (struct sockaddr *)sai, sizeof(*sai)) < 0)
+    // Explicitly turn off IPV6_V6ONLY which is needed on non-Linux platforms like NetBSD and Darwin.
+    // This means we allow ipv4 socket connections that can have ipv4-mapped ipv6 IPs.
+    int off = 0;
+    if (setsockopt(ru->socket, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&off, sizeof(off)) < 0)
+	{
+	errAbort("setsockopt IPV6_V6ONLY off failed.");
+	}
+
+    if (bind(ru->socket, (struct sockaddr *)sai, getSockSize6n4(sai)) < 0)
 	{
 	warn("Couldn't bind rudp socket: %s", strerror(errno));
 	rudpClose(&ru);
 	}
     }
 return ru;
 }
 
-struct rudp *rudpMustOpenBound(struct sockaddr_in *sai)
+struct rudp *rudpMustOpenBound(struct sockaddr_storage *sai)
 /* Open up a rudp socket bound to a particular port and address
  * or die trying. */
 {
 struct rudp *ru = rudpOpenBound(sai);
 if (ru == NULL)
     noWarnAbort();
 return ru;
 }
 
 void rudpClose(struct rudp **pRu)
 /* Close socket and free memory. */
 {
 struct rudp *ru = *pRu;
 if (ru != NULL)
     {
@@ -255,89 +264,93 @@
     readyCount = select(sd+1, &set, NULL, NULL, &tv);
     if (readyCount < 0) 
 	{
 	if (errno == EINTR)	/* Select interrupted, not timed out. */
 	    continue;
     	else 
     	    warn("select failure in rudp: %s", strerror(errno));
     	}
     else
 	{
     	return readyCount > 0;	/* Zero readyCount indicates time out */
 	}
     }
 }
 
-static boolean getOurAck(struct rudp *ru, struct timeval *startTv, struct sockaddr_in *sai)
+static boolean getOurAck(struct rudp *ru, struct timeval *startTv, struct sockaddr_storage *sai)
 /* Wait for acknowledgement to the message we just sent.
  * The set should be zeroed out. Only wait for up to
  * ru->timeOut microseconds.   Prints a message and returns FALSE
  * if there's a problem.   */
 {
 struct rudpHeader head;
 int readSize;
 int timeOut = ru->timeOut;
-struct sockaddr_in retFrom;
+struct sockaddr_storage retFrom;
 unsigned int retFromSize = sizeof(retFrom);
 
 for (;;)
     {
     /* Set up select with our time out. */
     int dt;
     struct timeval tv;
 
     if (readReadyWait(ru->socket, timeOut))
 	{
 	/* Read message and if it's our ack return true.   */
 	readSize = recvfrom(ru->socket, &head, sizeof(head), 0, 
 		    (struct sockaddr*)&retFrom, &retFromSize);
 	if (readSize >= sizeof(head) && head.type == rudpAck && head.id == ru->lastId)
 	    {
-	    if ((sai->sin_addr.s_addr==retFrom.sin_addr.s_addr) &&
-		(sai->sin_port==retFrom.sin_port))
+	    char saiIpStr[NI_MAXHOST];
+	    char retFromIpStr[NI_MAXHOST];
+	    char saiPortStr[NI_MAXSERV];
+	    char retFromPortStr[NI_MAXSERV];
+	    getAddrAndPortAsString6n4(sai, saiIpStr, sizeof saiIpStr, saiPortStr, sizeof saiPortStr);
+	    getAddrAndPortAsString6n4(&retFrom, retFromIpStr, sizeof retFromIpStr, 
+		retFromPortStr, sizeof retFromPortStr);
+
+	    if (sameString(saiIpStr, retFromIpStr) && 
+		sameString(saiPortStr, retFromPortStr))
 		{
 		gettimeofday(&tv, NULL);
 		dt = timeDiff(startTv, &tv);
 		rudpAddRoundTripTime(ru, dt);
 		return TRUE;
 		}
 	    else
 		{
-		char retFromDottedQuad[17];
-		char saiDottedQuad[17];
-		internetIpToDottedQuad(ntohl(retFrom.sin_addr.s_addr), retFromDottedQuad);
-		internetIpToDottedQuad(ntohl(sai->sin_addr.s_addr), saiDottedQuad);
-		warn("rudp: discarding mistaken ack from %s:%d by confirming recipient ip:port %s:%d"
-		    , retFromDottedQuad, retFrom.sin_port
-		    , saiDottedQuad, sai->sin_port
+		warn("rudp: discarding mistaken ack from %s:%s by confirming recipient ip:port %s:%s"
+		    , retFromIpStr, retFromPortStr
+		    , saiIpStr, saiPortStr
 		    );
 		}
 	    }
 	}
 
     /* If we got to here then we did get a message, but it's not our
      * ack.  We ignore the message and loop around again,  but update
      * our timeout so that we won't keep getting other people's messages
      * forever. */
     gettimeofday(&tv, NULL);
     timeOut = ru->timeOut - timeDiff(startTv, &tv);
     if (timeOut <= 0)
 	return FALSE;
     }
 }
 
-int rudpSend(struct rudp *ru, struct sockaddr_in *sai, void *message, int size)
+int rudpSend(struct rudp *ru, struct sockaddr_storage *sai, void *message, int size)
 /* Send message of given size to port at host via rudp.  Prints a warning and
  * sets errno and returns -1 if there's a problem. */
 {
 struct timeval sendTv;	/* Current time. */
 
 char outBuf[udpEthMaxSize];
 struct rudpHeader *head;
 int fullSize = size + sizeof(*head);
 int i, err = 0, maxRetry = ru->maxRetries;
 
 
 /* Make buffer with header in front of message. 
  * At some point we might replace this with a scatter/gather
  * iovector. */
 ru->sendCount += 1;
@@ -345,31 +358,31 @@
 assert(size <= rudpMaxSize);
 head = (struct rudpHeader *)outBuf;
 memcpy(head+1, message, size);
 head->pid = ru->pid;
 head->connId = ru->connId;
 head->id = ++ru->lastId;
 head->type = rudpData;
 
 /* Go into send/wait for ack/retry loop. */
 for (i=0; i<maxRetry; ++i)
     {
     gettimeofday(&sendTv, NULL);
     head->sendSec = sendTv.tv_sec;
     head->sendMicro = sendTv.tv_usec;
     err = sendto(ru->socket, outBuf, fullSize, 0, 
-	(struct sockaddr *)sai, sizeof(*sai));
+	(struct sockaddr *)sai, getSockSize6n4(sai));
     if (err < 0) 
 	{
 	/* Warn, wait, and retry. */
 	struct timeval tv;
 	warn("sendto problem %s", strerror(errno));
 	tv.tv_sec = 0;
 	tv.tv_usec = ru->timeOut;
 	select(0, NULL, NULL, NULL, &tv);
 	rudpTimedOut(ru);
 	ru->resendCount += 1;
 	ru->resend = TRUE;
 	continue;
 	}
     if (getOurAck(ru, &sendTv, sai))
 	{
@@ -399,60 +412,63 @@
     if (dlEmpty(ru->recvList))
         break;
     p = ru->recvList->head->val;
     if (now - p->lastChecked < period)
         break;
     --ru->recvCount;
     node = dlPopHead(ru->recvList);
     freeMem(node);
     hashRemove(ru->recvHash, p->recvHashKey);
     freePacketSeen(&p);
     }
 }
 
 
 int rudpReceiveTimeOut(struct rudp *ru, void *messageBuf, int bufSize, 
-	struct sockaddr_in *retFrom, int timeOut)
+	struct sockaddr_storage *retFrom, int timeOut)
 /* Read message into buffer of given size.  Returns actual size read on
  * success. On failure prints a warning, sets errno, and returns -1. 
  * Also returns ip address of message source. If timeOut is nonzero,
  * it represents the timeout in milliseconds.  It will set errno to
  * ETIMEDOUT in this case.*/
 {
 char inBuf[udpEthMaxSize];
 struct rudpHeader *head = (struct rudpHeader *)inBuf;
 struct rudpHeader ackHead;
-struct sockaddr_in sai;
+struct sockaddr_storage sai;
 socklen_t saiSize = sizeof(sai);
 int readSize, err;
 assert(bufSize <= rudpMaxSize);
 ru->receiveCount += 1;
 for (;;)
     {
     if (timeOut != 0)
 	{
 	if (!readReadyWait(ru->socket, timeOut))
 	    {
 	    warn("rudpReceive timed out\n");
 	    errno = ETIMEDOUT;
 	    return -1;
 	    }
 	}
     readSize = recvfrom(ru->socket, inBuf, sizeof(inBuf), 0, 
 	(struct sockaddr*)&sai, &saiSize);
+
+    // return the entire structure
     if (retFrom != NULL)
 	*retFrom = sai; 
+
     if (readSize < 0)
 	{
 	if (errno == EINTR)
 	    continue;
 	warn("recvfrom error: %s", strerror(errno));
 	ru->failCount += 1;
 	return readSize;
 	}
     if (readSize < sizeof(*head))
 	{
 	warn("rudpRecieve truncated message");
 	continue;
 	}
     if (head->type != rudpData)
 	{
@@ -472,64 +488,64 @@
     if (readSize > bufSize)
 	{
 	warn("read more bytes than have room for in rudpReceive");
 	readSize = bufSize;
 	}
     memcpy(messageBuf, head+1, readSize);
 
     /* check for duplicate packet */
     if (!ru->recvHash)
 	{ /* take advantage of new auto-expanding hashes */
 	ru->recvHash = newHashExt(4, FALSE);  /* do not use local mem in hash */
 	ru->recvList = newDlList();
 	ru->recvCount = 0;
 	}
     char hashKey[64];
-    char saiDottedQuad[17];
-    internetIpToDottedQuad(ntohl(sai.sin_addr.s_addr), saiDottedQuad);
+    char saiStr[NI_MAXHOST];
+    getAddrAsString6n4(&sai, saiStr, sizeof saiStr);
     safef(hashKey, sizeof(hashKey), "%s-%d-%d-%d" 
-    	, saiDottedQuad 
+    	, saiStr
         , head->pid              
         , head->connId
         , head->id
       );		    
     if (hashLookup(ru->recvHash, hashKey))
 	{
 	warn("duplicate packet filtered out: %s", hashKey);
 	continue;
 	}
     long now = time(NULL);
     struct packetSeen *p;
     AllocVar(p);
     AllocVar(p->node);
     p->node->val = p;
     p->recvHashKey = hashStoreName(ru->recvHash, hashKey);
     p->lastChecked = now;
     dlAddTail(ru->recvList, p->node);
     ++ru->recvCount;
    
     sweepOutOldPacketsSeen(ru, now);
 
     ru->lastIdReceived = head->id;
     break;
     }
 return readSize;
 }
 
 
 int rudpReceiveFrom(struct rudp *ru, void *messageBuf, int bufSize, 
-	struct sockaddr_in *retFrom)
+	struct sockaddr_storage *retFrom)
 /* Read message into buffer of given size.  Returns actual size read on
  * success. On failure prints a warning, sets errno, and returns -1. 
  * Also returns ip address of message source. */
 {
 return rudpReceiveTimeOut(ru, messageBuf, bufSize, retFrom, 0);
 }
 
 int rudpReceive(struct rudp *ru, void *messageBuf, int bufSize)
 /* Read message into buffer of given size.  Returns actual size read on
  * success. On failure prints a warning, sets errno, and returns -1. */
 {
 return rudpReceiveFrom(ru, messageBuf, bufSize, NULL);
 }
 
 void rudpPrintStatus(struct rudp *ru)