1 /* 2 * decodenetnum - return a net number (this is crude, but careful) 3 */ 4 #include <config.h> 5 #include <sys/types.h> 6 #include <ctype.h> 7 #ifdef HAVE_SYS_SOCKET_H 8 #include <sys/socket.h> 9 #endif 10 #ifdef HAVE_NETINET_IN_H 11 #include <netinet/in.h> 12 #endif 13 14 #include "ntp.h" 15 #include "ntp_stdlib.h" 16 17 18 /* If the given string position points to a decimal digit, parse the 19 * number. If this is not possible, or the parsing did not consume the 20 * whole string, or if the result exceeds the maximum value, return the 21 * default value. 22 */ 23 static unsigned long 24 _num_or_dflt( 25 char * sval, 26 unsigned long maxval, 27 unsigned long defval 28 ) 29 { 30 char * ep; 31 unsigned long num; 32 33 if (!(sval && isdigit(*(unsigned char*)sval))) 34 return defval; 35 36 num = strtoul(sval, &ep, 10); 37 if (!*ep && num <= maxval) 38 return num; 39 40 return defval; 41 } 42 43 /* If the given string position is not NULL and does not point to the 44 * terminator, replace the character with NUL and advance the pointer. 45 * Return the resulting position. 46 */ 47 static inline char* 48 _chop( 49 char * sp) 50 { 51 if (sp && *sp) 52 *sp++ = '\0'; 53 return sp; 54 } 55 56 /* If the given string position points to the given char, advance the 57 * pointer and return the result. Otherwise, return NULL. 58 */ 59 static inline char* 60 _skip( 61 char * sp, 62 int ch) 63 { 64 if (sp && *(unsigned char*)sp == ch) 65 return (sp + 1); 66 return NULL; 67 } 68 69 /* 70 * decodenetnum convert text IP address and port to sockaddr_u 71 * 72 * Returns FALSE (->0) for failure, TRUE (->1) for success. 73 */ 74 int 75 decodenetnum( 76 const char *num, 77 sockaddr_u *net 78 ) 79 { 80 /* Building a parser is more fun in Haskell, but here we go... 81 * 82 * This works through 'inet_pton()' taking the brunt of the 83 * work, after some string manipulations to split off URI 84 * brackets, ports and scope identifiers. The heuristics are 85 * simple but must hold for all _VALID_ addresses. inet_pton() 86 * will croak on bad ones later, but replicating the whole 87 * parser logic to detect errors is wasteful. 88 */ 89 90 sockaddr_u netnum; 91 char buf[64]; /* working copy of input */ 92 char *haddr=buf; 93 unsigned int port=NTP_PORT, scope=0; 94 unsigned short afam=AF_UNSPEC; 95 96 /* copy input to working buffer with length check */ 97 if (strlcpy(buf, num, sizeof(buf)) >= sizeof(buf)) 98 return FALSE; 99 100 /* Identify address family and possibly the port, if given. If 101 * this results in AF_UNSPEC, we will fail in the next step. 102 */ 103 if (*haddr == '[') { 104 char * endp = strchr(++haddr, ']'); 105 if (endp) { 106 port = _num_or_dflt(_skip(_chop(endp), ':'), 107 0xFFFFu, port); 108 afam = strchr(haddr, ':') ? AF_INET6 : AF_INET; 109 } 110 } else { 111 char *col = strchr(haddr, ':'); 112 char *dot = strchr(haddr, '.'); 113 if (col == dot) { 114 /* no dot, no colon: bad! */ 115 afam = AF_UNSPEC; 116 } else if (!col) { 117 /* no colon, only dot: IPv4! */ 118 afam = AF_INET; 119 } else if (!dot || col < dot) { 120 /* no dot or 1st colon before 1st dot: IPv6! */ 121 afam = AF_INET6; 122 } else { 123 /* 1st dot before 1st colon: must be IPv4 with port */ 124 afam = AF_INET; 125 port = _num_or_dflt(_chop(col), 0xFFFFu, port); 126 } 127 } 128 129 /* Since we don't know about additional members in the address 130 * structures, we wipe the result buffer thoroughly: 131 */ 132 memset(&netnum, 0, sizeof(netnum)); 133 134 /* For AF_INET6, evaluate and remove any scope suffix. Have 135 * inet_pton() do the real work for AF_INET and AF_INET6, bail 136 * out otherwise: 137 */ 138 switch (afam) { 139 case AF_INET: 140 if (inet_pton(afam, haddr, &netnum.sa4.sin_addr) <= 0) 141 return FALSE; 142 netnum.sa4.sin_port = htons((unsigned short)port); 143 break; 144 145 case AF_INET6: 146 scope = _num_or_dflt(_chop(strchr(haddr, '%')), 0xFFFFFFFFu, scope); 147 if (inet_pton(afam, haddr, &netnum.sa6.sin6_addr) <= 0) 148 return FALSE; 149 netnum.sa6.sin6_port = htons((unsigned short)port); 150 netnum.sa6.sin6_scope_id = scope; 151 break; 152 153 case AF_UNSPEC: 154 default: 155 return FALSE; 156 } 157 158 /* Collect the remaining pieces and feed the output, which was 159 * not touched so far: 160 */ 161 netnum.sa.sa_family = afam; 162 memcpy(net, &netnum, sizeof(netnum)); 163 return TRUE; 164 } 165