From e64dc10ea1d5be38681191d9f4d2aa9018ab59dc Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Sun, 4 Oct 2020 03:50:26 +0900 Subject: [PATCH] [linux] handle ffff:ffff in ipv6 addr correctly Close #102 Close #109 The listen address and port of an AF_INET6 socket were not display if the socket listened at an ipv6 address including ffff:ffff. Here is a command session demonstrating the bug: # ip -6 addr add abcd:ef10:ffff:ffff:ffff:ffff:ffff:ff62 dev lo # nc -6 -l abcd:ef10:ffff:ffff:ffff:ffff:ffff:ff62 8888 & [1] 6762 # ./lsof -p 6762 -a -d fd -P -n COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME nc 6762 yamato 0u CHR 136,6 0t0 9 /dev/pts/6 nc 6762 yamato 1u CHR 136,6 0t0 9 /dev/pts/6 nc 6762 yamato 2u CHR 136,6 0t0 9 /dev/pts/6 nc 6762 yamato 3u sock 0,9 0t0 5833594 protocol: TCPv6 The last line should be: nc 6762 yamato 3u IPv6 5833594 0t0 TCP [abcd:ef10:ffff:ffff:ffff:ffff:ffff:ff62]:8888 (LISTEN) The original code decoding an ipv6 address uses UINT32_MAX constant incorrect way. @zhrf2020 reported this bug in #102. The test case is based on the report. @zhrf2020 provided the initial version of fix, #109. This change is based on the version. --- dialects/linux/dsock.c | 33 ++++++++++++- 1 files changed, 31 insertions(+), 2 deletions(-) diff --git a/dialects/linux/dsock.c b/dialects/linux/dsock.c index 4b71460..0aaaf6d 100644 --- a/dialects/linux/dsock.c +++ b/dialects/linux/dsock.c @@ -3632,9 +3632,38 @@ net6a2in6(as, ad) (void) strncpy(buf, as, 8); buf[8] = '\0'; ep = (char *)NULL; - if ((ad->s6_addr32[i] = (uint32_t)strtoul(buf, &ep, 16)) - == (uint32_t)UINT32_MAX || !ep || *ep) + + errno = 0; + unsigned long ul_addr = strtoul(buf, &ep, 16); + if (!ep || *ep) + break; + else if (ul_addr == ULONG_MAX && errno == ERANGE) + { + /* Quoted from strtoul(3) + --------------------------------------------------- + The strtoul() function returns either the result of + the conversion or, if there was a leading minus + sign, the negation of the result of the conversion + represented as an unsigned value, unless the + original (nonnegated) value would overflow; in the + latter case, strtoul() returns ULONG_MAX and sets + errno to ERANGE. + --------------------------------------------------- + NOTE: even if the value doesn't overflow, a + negative is not acceptable. */ break; + } + else if (ul_addr > (unsigned long)UINT32_MAX) + { + /* This will never happen: + The maximum length of BUF is 8 characters. + The possible maximum value represented by BUF is + "FFFFFFFF". This is UINT32_MAX. + If you agree with what I write here, make a pull + request for removing this block. */ + break; + } + ad->s6_addr32[i] = (uint32_t)ul_addr; } return((*as || (i != 4) || len) ? 1 : 0); }