154359Sroberto/* 254359Sroberto * decodenetnum - return a net number (this is crude, but careful) 354359Sroberto */ 4280849Scy#include <config.h> 554359Sroberto#include <sys/types.h> 654359Sroberto#include <ctype.h> 7280849Scy#ifdef HAVE_SYS_SOCKET_H 854359Sroberto#include <sys/socket.h> 9280849Scy#endif 10280849Scy#ifdef HAVE_NETINET_IN_H 1154359Sroberto#include <netinet/in.h> 12280849Scy#endif 1354359Sroberto 14280849Scy#include "ntp.h" 1554359Sroberto#include "ntp_stdlib.h" 1654359Sroberto 17358659Scy 18362716Scy/* If the given string position points to a decimal digit, parse the 19362716Scy * number. If this is not possible, or the parsing did not consume the 20362716Scy * whole string, or if the result exceeds the maximum value, return the 21362716Scy * default value. 22362716Scy */ 23362716Scystatic unsigned long 24362716Scy_num_or_dflt( 25362716Scy char * sval, 26362716Scy unsigned long maxval, 27362716Scy unsigned long defval 28358659Scy ) 29358659Scy{ 30362716Scy char * ep; 31362716Scy unsigned long num; 32362716Scy 33362716Scy if (!(sval && isdigit(*(unsigned char*)sval))) 34362716Scy return defval; 35362716Scy 36362716Scy num = strtoul(sval, &ep, 10); 37362716Scy if (!*ep && num <= maxval) 38362716Scy return num; 39362716Scy 40362716Scy return defval; 41358659Scy} 42358659Scy 43362716Scy/* If the given string position is not NULL and does not point to the 44362716Scy * terminator, replace the character with NUL and advance the pointer. 45362716Scy * Return the resulting position. 46362716Scy */ 47362716Scystatic inline char* 48362716Scy_chop( 49362716Scy char * sp) 50362716Scy{ 51362716Scy if (sp && *sp) 52362716Scy *sp++ = '\0'; 53362716Scy return sp; 54362716Scy} 55362716Scy 56362716Scy/* If the given string position points to the given char, advance the 57362716Scy * pointer and return the result. Otherwise, return NULL. 58362716Scy */ 59362716Scystatic inline char* 60362716Scy_skip( 61362716Scy char * sp, 62362716Scy int ch) 63362716Scy{ 64362716Scy if (sp && *(unsigned char*)sp == ch) 65362716Scy return (sp + 1); 66362716Scy return NULL; 67362716Scy} 68362716Scy 69280849Scy/* 70280849Scy * decodenetnum convert text IP address and port to sockaddr_u 71280849Scy * 72362716Scy * Returns FALSE (->0) for failure, TRUE (->1) for success. 73280849Scy */ 7454359Srobertoint 7554359Srobertodecodenetnum( 7654359Sroberto const char *num, 77362716Scy sockaddr_u *net 7854359Sroberto ) 7954359Sroberto{ 80362716Scy /* Building a parser is more fun in Haskell, but here we go... 81362716Scy * 82362716Scy * This works through 'inet_pton()' taking the brunt of the 83362716Scy * work, after some string manipulations to split off URI 84362716Scy * brackets, ports and scope identifiers. The heuristics are 85362716Scy * simple but must hold for all _VALID_ addresses. inet_pton() 86362716Scy * will croak on bad ones later, but replicating the whole 87362716Scy * parser logic to detect errors is wasteful. 88362716Scy */ 89358659Scy 90362716Scy sockaddr_u netnum; 91362716Scy char buf[64]; /* working copy of input */ 92362716Scy char *haddr=buf; 93362716Scy unsigned int port=NTP_PORT, scope=0; 94362716Scy unsigned short afam=AF_UNSPEC; 95362716Scy 96362716Scy /* copy input to working buffer with length check */ 97362716Scy if (strlcpy(buf, num, sizeof(buf)) >= sizeof(buf)) 98358659Scy return FALSE; 99289764Sglebius 100362716Scy /* Identify address family and possibly the port, if given. If 101362716Scy * this results in AF_UNSPEC, we will fail in the next step. 102362716Scy */ 103362716Scy if (*haddr == '[') { 104362716Scy char * endp = strchr(++haddr, ']'); 105362716Scy if (endp) { 106362716Scy port = _num_or_dflt(_skip(_chop(endp), ':'), 107362716Scy 0xFFFFu, port); 108362716Scy afam = strchr(haddr, ':') ? AF_INET6 : AF_INET; 109280849Scy } 110280849Scy } else { 111362716Scy char *col = strchr(haddr, ':'); 112362716Scy char *dot = strchr(haddr, '.'); 113362716Scy if (col == dot) { 114362716Scy /* no dot, no colon: bad! */ 115362716Scy afam = AF_UNSPEC; 116362716Scy } else if (!col) { 117362716Scy /* no colon, only dot: IPv4! */ 118362716Scy afam = AF_INET; 119362716Scy } else if (!dot || col < dot) { 120362716Scy /* no dot or 1st colon before 1st dot: IPv6! */ 121362716Scy afam = AF_INET6; 122362716Scy } else { 123362716Scy /* 1st dot before 1st colon: must be IPv4 with port */ 124362716Scy afam = AF_INET; 125362716Scy port = _num_or_dflt(_chop(col), 0xFFFFu, port); 126362716Scy } 12754359Sroberto } 128362716Scy 129362716Scy /* Since we don't know about additional members in the address 130362716Scy * structures, we wipe the result buffer thoroughly: 131362716Scy */ 132362716Scy memset(&netnum, 0, sizeof(netnum)); 133362716Scy 134362716Scy /* For AF_INET6, evaluate and remove any scope suffix. Have 135362716Scy * inet_pton() do the real work for AF_INET and AF_INET6, bail 136362716Scy * out otherwise: 137362716Scy */ 138362716Scy switch (afam) { 139362716Scy case AF_INET: 140362716Scy if (inet_pton(afam, haddr, &netnum.sa4.sin_addr) <= 0) 141362716Scy return FALSE; 142362716Scy netnum.sa4.sin_port = htons((unsigned short)port); 143362716Scy break; 144362716Scy 145362716Scy case AF_INET6: 146362716Scy scope = _num_or_dflt(_chop(strchr(haddr, '%')), 0xFFFFFFFFu, scope); 147362716Scy if (inet_pton(afam, haddr, &netnum.sa6.sin6_addr) <= 0) 148362716Scy return FALSE; 149362716Scy netnum.sa6.sin6_port = htons((unsigned short)port); 150362716Scy netnum.sa6.sin6_scope_id = scope; 151362716Scy break; 152362716Scy 153362716Scy case AF_UNSPEC: 154362716Scy default: 155358659Scy return FALSE; 156358659Scy } 157358659Scy 158362716Scy /* Collect the remaining pieces and feed the output, which was 159362716Scy * not touched so far: 160362716Scy */ 161362716Scy netnum.sa.sa_family = afam; 162362716Scy memcpy(net, &netnum, sizeof(netnum)); 163358659Scy return TRUE; 16454359Sroberto} 165