decodenetnum.c (ntp-4.2.8p14) | : | decodenetnum.c (ntp-4.2.8p15) | ||
---|---|---|---|---|
skipping to change at line 16 | skipping to change at line 16 | |||
#include <ctype.h> | #include <ctype.h> | |||
#ifdef HAVE_SYS_SOCKET_H | #ifdef HAVE_SYS_SOCKET_H | |||
#include <sys/socket.h> | #include <sys/socket.h> | |||
#endif | #endif | |||
#ifdef HAVE_NETINET_IN_H | #ifdef HAVE_NETINET_IN_H | |||
#include <netinet/in.h> | #include <netinet/in.h> | |||
#endif | #endif | |||
#include "ntp.h" | #include "ntp.h" | |||
#include "ntp_stdlib.h" | #include "ntp_stdlib.h" | |||
#include "ntp_assert.h" | ||||
#define PORTSTR(x) _PORTSTR(x) | /* If the given string position points to a decimal digit, parse the | |||
#define _PORTSTR(x) #x | * number. If this is not possible, or the parsing did not consume the | |||
* whole string, or if the result exceeds the maximum value, return the | ||||
static int | * default value. | |||
isnumstr( | */ | |||
const char *s | static unsigned long | |||
_num_or_dflt( | ||||
char * sval, | ||||
unsigned long maxval, | ||||
unsigned long defval | ||||
) | ) | |||
{ | { | |||
while (*s >= '0' && *s <= '9') | char * ep; | |||
++s; | unsigned long num; | |||
return !*s; | ||||
if (!(sval && isdigit(*(unsigned char*)sval))) | ||||
return defval; | ||||
num = strtoul(sval, &ep, 10); | ||||
if (!*ep && num <= maxval) | ||||
return num; | ||||
return defval; | ||||
} | ||||
/* If the given string position is not NULL and does not point to the | ||||
* terminator, replace the character with NUL and advance the pointer. | ||||
* Return the resulting position. | ||||
*/ | ||||
static inline char* | ||||
_chop( | ||||
char * sp) | ||||
{ | ||||
if (sp && *sp) | ||||
*sp++ = '\0'; | ||||
return sp; | ||||
} | ||||
/* If the given string position points to the given char, advance the | ||||
* pointer and return the result. Otherwise, return NULL. | ||||
*/ | ||||
static inline char* | ||||
_skip( | ||||
char * sp, | ||||
int ch) | ||||
{ | ||||
if (sp && *(unsigned char*)sp == ch) | ||||
return (sp + 1); | ||||
return NULL; | ||||
} | } | |||
/* | /* | |||
* decodenetnum convert text IP address and port to sockaddr_u | * decodenetnum convert text IP address and port to sockaddr_u | |||
* | * | |||
* Returns 0 for failure, 1 for success. | * Returns FALSE (->0) for failure, TRUE (->1) for success. | |||
*/ | */ | |||
int | int | |||
decodenetnum( | decodenetnum( | |||
const char *num, | const char *num, | |||
sockaddr_u *netnum | sockaddr_u *net | |||
) | ) | |||
{ | { | |||
static const char * const servicename = "ntp"; | /* Building a parser is more fun in Haskell, but here we go... | |||
static const char * const serviceport = PORTSTR(NTP_PORT); | * | |||
* This works through 'inet_pton()' taking the brunt of the | ||||
struct addrinfo hints, *ai = NULL; | * work, after some string manipulations to split off URI | |||
int err; | * brackets, ports and scope identifiers. The heuristics are | |||
const char *host_str; | * simple but must hold for all _VALID_ addresses. inet_pton() | |||
const char *port_str; | * will croak on bad ones later, but replicating the whole | |||
char *pp; | * parser logic to detect errors is wasteful. | |||
char *np; | */ | |||
char nbuf[80]; | ||||
sockaddr_u netnum; | ||||
REQUIRE(num != NULL); | char buf[64]; /* working copy of input */ | |||
char *haddr=buf; | ||||
unsigned int port=NTP_PORT, scope=0; | ||||
unsigned short afam=AF_UNSPEC; | ||||
if (strlen(num) >= sizeof(nbuf)) { | /* copy input to working buffer with length check */ | |||
printf("length error\n"); | if (strlcpy(buf, num, sizeof(buf)) >= sizeof(buf)) | |||
return FALSE; | return FALSE; | |||
} | ||||
port_str = servicename; | /* Identify address family and possibly the port, if given. If | |||
if ('[' != num[0]) { | * this results in AF_UNSPEC, we will fail in the next step. | |||
/* | */ | |||
* to distinguish IPv6 embedded colons from a port | if (*haddr == '[') { | |||
* specification on an IPv4 address, assume all | char * endp = strchr(++haddr, ']'); | |||
* legal IPv6 addresses have at least two colons. | if (endp) { | |||
*/ | port = _num_or_dflt(_skip(_chop(endp), ':'), | |||
pp = strchr(num, ':'); | 0xFFFFu, port); | |||
if (NULL == pp) | afam = strchr(haddr, ':') ? AF_INET6 : AF_INET; | |||
host_str = num; /* no colons */ | ||||
else if (NULL != strchr(pp + 1, ':')) | ||||
host_str = num; /* two or more colons */ | ||||
else { /* one colon */ | ||||
strlcpy(nbuf, num, sizeof(nbuf)); | ||||
host_str = nbuf; | ||||
pp = strchr(nbuf, ':'); | ||||
*pp = '\0'; | ||||
port_str = pp + 1; | ||||
} | } | |||
} else { | } else { | |||
host_str = np = nbuf; | char *col = strchr(haddr, ':'); | |||
while (*++num && ']' != *num) | char *dot = strchr(haddr, '.'); | |||
*np++ = *num; | if (col == dot) { | |||
*np = 0; | /* no dot, no colon: bad! */ | |||
if (']' == num[0] && ':' == num[1] && '\0' != num[2]) | afam = AF_UNSPEC; | |||
port_str = &num[2]; | } else if (!col) { | |||
/* no colon, only dot: IPv4! */ | ||||
afam = AF_INET; | ||||
} else if (!dot || col < dot) { | ||||
/* no dot or 1st colon before 1st dot: IPv6! */ | ||||
afam = AF_INET6; | ||||
} else { | ||||
/* 1st dot before 1st colon: must be IPv4 with port */ | ||||
afam = AF_INET; | ||||
port = _num_or_dflt(_chop(col), 0xFFFFu, port); | ||||
} | ||||
} | } | |||
if ( ! *host_str) | ||||
return FALSE; | ||||
if ( ! *port_str) | ||||
port_str = servicename; | ||||
ZERO(hints); | /* Since we don't know about additional members in the address | |||
hints.ai_flags |= Z_AI_NUMERICHOST; | * structures, we wipe the result buffer thoroughly: | |||
if (isnumstr(port_str)) | */ | |||
hints.ai_flags |= Z_AI_NUMERICSERV; | memset(&netnum, 0, sizeof(netnum)); | |||
err = getaddrinfo(host_str, port_str, &hints, &ai); | ||||
/* retry with default service name if the service lookup failed */ | /* For AF_INET6, evaluate and remove any scope suffix. Have | |||
if (err == EAI_SERVICE && strcmp(port_str, servicename)) { | * inet_pton() do the real work for AF_INET and AF_INET6, bail | |||
hints.ai_flags &= ~Z_AI_NUMERICSERV; | * out otherwise: | |||
port_str = servicename; | */ | |||
err = getaddrinfo(host_str, port_str, &hints, &ai); | switch (afam) { | |||
} | case AF_INET: | |||
/* retry another time with default service port if the service lookup fai | if (inet_pton(afam, haddr, &netnum.sa4.sin_addr) <= 0) | |||
led */ | return FALSE; | |||
if (err == EAI_SERVICE && strcmp(port_str, serviceport)) { | netnum.sa4.sin_port = htons((unsigned short)port); | |||
hints.ai_flags |= Z_AI_NUMERICSERV; | break; | |||
port_str = serviceport; | ||||
err = getaddrinfo(host_str, port_str, &hints, &ai); | case AF_INET6: | |||
} | scope = _num_or_dflt(_chop(strchr(haddr, '%')), 0xFFFFFFFFu, scop | |||
if (err != 0) | e); | |||
return FALSE; | if (inet_pton(afam, haddr, &netnum.sa6.sin6_addr) <= 0) | |||
return FALSE; | ||||
netnum.sa6.sin6_port = htons((unsigned short)port); | ||||
netnum.sa6.sin6_scope_id = scope; | ||||
break; | ||||
INSIST(ai->ai_addrlen <= sizeof(*netnum)); | case AF_UNSPEC: | |||
ZERO(*netnum); | default: | |||
memcpy(netnum, ai->ai_addr, ai->ai_addrlen); | return FALSE; | |||
freeaddrinfo(ai); | } | |||
/* Collect the remaining pieces and feed the output, which was | ||||
* not touched so far: | ||||
*/ | ||||
netnum.sa.sa_family = afam; | ||||
memcpy(net, &netnum, sizeof(netnum)); | ||||
return TRUE; | return TRUE; | |||
} | } | |||
End of changes. 14 change blocks. | ||||
79 lines changed or deleted | 124 lines changed or added |