1 /* 2 * Copyright (c) 1996,1999 by Internet Software Consortium. 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 9 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 10 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 11 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 13 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 14 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 15 * SOFTWARE. 16 */ 17 18 #include <sys/cdefs.h> 19 #if defined(LIBC_SCCS) && !defined(lint) 20 #if 0 21 static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.1 2002/08/02 02:17:21 marka Exp "; 22 #else 23 __RCSID("$NetBSD: inet_net_pton.c,v 1.4 2012/03/20 17:08:13 matt Exp $"); 24 #endif 25 #endif 26 27 #include "port_before.h" 28 29 #include <sys/types.h> 30 #include <sys/socket.h> 31 #include <netinet/in.h> 32 #include <arpa/nameser.h> 33 #include <arpa/inet.h> 34 35 #include <isc/assertions.h> 36 #include <stddef.h> 37 #include <ctype.h> 38 #include <errno.h> 39 #include <stdio.h> 40 #include <string.h> 41 #include <stdlib.h> 42 43 #include "port_after.h" 44 45 #ifdef __weak_alias 46 __weak_alias(inet_net_pton,_inet_net_pton) 47 #endif 48 49 #ifdef SPRINTF_CHAR 50 # define SPRINTF(x) strlen(sprintf/**/x) 51 #else 52 # define SPRINTF(x) ((size_t)sprintf x) 53 #endif 54 55 56 /* 57 * static int 58 * inet_net_pton_ipv4(src, dst, size) 59 * convert IPv4 network number from presentation to network format. 60 * accepts hex octets, hex strings, decimal octets, and /CIDR. 61 * "size" is in bytes and describes "dst". 62 * return: 63 * number of bits, either imputed classfully or specified with /CIDR, 64 * or -1 if some failure occurred (check errno). ENOENT means it was 65 * not an IPv4 network specification. 66 * note: 67 * network byte order assumed. this means 192.5.5.240/28 has 68 * 0b11110000 in its fourth octet. 69 * author: 70 * Paul Vixie (ISC), June 1996 71 */ 72 static int 73 inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) 74 { 75 static const char xdigits[] = "0123456789abcdef"; 76 static const char digits[] = "0123456789"; 77 int ch, dirty, bits; 78 ptrdiff_t n, tmp; 79 const u_char *odst = dst; 80 81 tmp = 0; 82 ch = *src++; 83 if (ch == '0' && (src[0] == 'x' || src[0] == 'X') 84 && isascii((u_char)(src[1])) 85 && isxdigit((u_char)(src[1]))) { 86 /* Hexadecimal: Eat nybble string. */ 87 if (size == 0) 88 goto emsgsize; 89 dirty = 0; 90 src++; /* skip x or X. */ 91 while ((ch = *src++) != '\0' && isascii((u_char)ch) 92 && isxdigit((u_char)ch)) { 93 if (isupper((u_char)ch)) 94 ch = tolower((u_char)ch); 95 n = strchr(xdigits, ch) - xdigits; 96 INSIST(n >= 0 && n <= 15); 97 if (dirty == 0) 98 tmp = n; 99 else 100 tmp = (tmp << 4) | n; 101 if (++dirty == 2) { 102 if (size-- == 0) 103 goto emsgsize; 104 *dst++ = (u_char) tmp; 105 dirty = 0; 106 } 107 } 108 if (dirty) { /* Odd trailing nybble? */ 109 if (size-- == 0) 110 goto emsgsize; 111 *dst++ = (u_char) (tmp << 4); 112 } 113 } else if (isascii((u_char)ch) && isdigit((u_char)ch)) { 114 /* Decimal: eat dotted digit string. */ 115 for (;;) { 116 tmp = 0; 117 do { 118 n = strchr(digits, ch) - digits; 119 INSIST(n >= 0 && n <= 9); 120 tmp *= 10; 121 tmp += n; 122 if (tmp > 255) 123 goto enoent; 124 } while ((ch = *src++) != '\0' && 125 isascii((u_char)ch) && isdigit((u_char)ch)); 126 if (size-- == 0) 127 goto emsgsize; 128 *dst++ = (u_char) tmp; 129 if (ch == '\0' || ch == '/') 130 break; 131 if (ch != '.') 132 goto enoent; 133 ch = *src++; 134 if (!isascii((u_char)ch) || !isdigit((u_char)ch)) 135 goto enoent; 136 } 137 } else 138 goto enoent; 139 140 bits = -1; 141 if (ch == '/' && isascii((u_char)(src[0])) && 142 isdigit((u_char)(src[0])) && dst > odst) { 143 /* CIDR width specifier. Nothing can follow it. */ 144 ch = *src++; /* Skip over the /. */ 145 bits = 0; 146 do { 147 n = strchr(digits, ch) - digits; 148 INSIST(n >= 0 && n <= 9); 149 bits *= 10; 150 bits += (int)n; 151 if (bits > 32) 152 goto emsgsize; 153 } while ((ch = *src++) != '\0' && isascii((u_char)ch) 154 && isdigit((u_char)ch)); 155 if (ch != '\0') 156 goto enoent; 157 } 158 159 /* Firey death and destruction unless we prefetched EOS. */ 160 if (ch != '\0') 161 goto enoent; 162 163 /* If nothing was written to the destination, we found no address. */ 164 if (dst == odst) 165 goto enoent; 166 /* If no CIDR spec was given, infer width from net class. */ 167 if (bits == -1) { 168 if (*odst >= 240) /* Class E */ 169 bits = 32; 170 else if (*odst >= 224) /* Class D */ 171 bits = 4; 172 else if (*odst >= 192) /* Class C */ 173 bits = 24; 174 else if (*odst >= 128) /* Class B */ 175 bits = 16; 176 else /* Class A */ 177 bits = 8; 178 /* If imputed mask is narrower than specified octets, widen. */ 179 if (bits >= 8 && bits < ((dst - odst) * 8)) 180 bits = (int)(dst - odst) * 8; 181 } 182 /* Extend network to cover the actual mask. */ 183 while (bits > ((dst - odst) * 8)) { 184 if (size-- == 0) 185 goto emsgsize; 186 *dst++ = '\0'; 187 } 188 return (bits); 189 190 enoent: 191 errno = ENOENT; 192 return (-1); 193 194 emsgsize: 195 errno = EMSGSIZE; 196 return (-1); 197 } 198 199 static int 200 getbits(const char *src, int *bitsp) 201 { 202 static const char digits[] = "0123456789"; 203 int n; 204 int val; 205 char ch; 206 207 val = 0; 208 n = 0; 209 while ((ch = *src++) != '\0') { 210 const char *pch; 211 212 pch = strchr(digits, ch); 213 if (pch != NULL) { 214 if (n++ != 0 && val == 0) /* no leading zeros */ 215 return (0); 216 val *= 10; 217 val += (int)(pch - digits); 218 if (val > 128) /* range */ 219 return (0); 220 continue; 221 } 222 return (0); 223 } 224 if (n == 0) 225 return (0); 226 *bitsp = val; 227 return (1); 228 } 229 230 static int 231 getv4(const char *src, u_char *dst, int *bitsp) 232 { 233 static const char digits[] = "0123456789"; 234 u_char *odst = dst; 235 int n; 236 u_int val; 237 char ch; 238 239 val = 0; 240 n = 0; 241 while ((ch = *src++) != '\0') { 242 const char *pch; 243 244 pch = strchr(digits, ch); 245 if (pch != NULL) { 246 if (n++ != 0 && val == 0) /* no leading zeros */ 247 return (0); 248 val *= 10; 249 val += (int)(pch - digits); 250 if (val > 255) /* range */ 251 return (0); 252 continue; 253 } 254 if (ch == '.' || ch == '/') { 255 if (dst - odst > 3) /* too many octets? */ 256 return (0); 257 *dst++ = val; 258 if (ch == '/') 259 return (getbits(src, bitsp)); 260 val = 0; 261 n = 0; 262 continue; 263 } 264 return (0); 265 } 266 if (n == 0) 267 return (0); 268 if (dst - odst > 3) /* too many octets? */ 269 return (0); 270 *dst++ = val; 271 return (1); 272 } 273 274 static int 275 inet_net_pton_ipv6(const char *src, u_char *dst, size_t size) 276 { 277 static const char xdigits_l[] = "0123456789abcdef", 278 xdigits_u[] = "0123456789ABCDEF"; 279 u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; 280 const char *xdigits, *curtok; 281 int ch, saw_xdigit; 282 u_int val; 283 int digits; 284 int bits; 285 size_t bytes; 286 int words; 287 int ipv4; 288 289 memset((tp = tmp), '\0', NS_IN6ADDRSZ); 290 endp = tp + NS_IN6ADDRSZ; 291 colonp = NULL; 292 /* Leading :: requires some special handling. */ 293 if (*src == ':') 294 if (*++src != ':') 295 goto enoent; 296 curtok = src; 297 saw_xdigit = 0; 298 val = 0; 299 digits = 0; 300 bits = -1; 301 ipv4 = 0; 302 while ((ch = *src++) != '\0') { 303 const char *pch; 304 305 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) 306 pch = strchr((xdigits = xdigits_u), ch); 307 if (pch != NULL) { 308 val <<= 4; 309 val |= (int)(pch - xdigits); 310 if (++digits > 4) 311 goto enoent; 312 saw_xdigit = 1; 313 continue; 314 } 315 if (ch == ':') { 316 curtok = src; 317 if (!saw_xdigit) { 318 if (colonp) 319 goto enoent; 320 colonp = tp; 321 continue; 322 } else if (*src == '\0') 323 goto enoent; 324 if (tp + NS_INT16SZ > endp) 325 return (0); 326 *tp++ = (u_char) (val >> 8) & 0xff; 327 *tp++ = (u_char) val & 0xff; 328 saw_xdigit = 0; 329 digits = 0; 330 val = 0; 331 continue; 332 } 333 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && 334 getv4(curtok, tp, &bits) > 0) { 335 tp += NS_INADDRSZ; 336 saw_xdigit = 0; 337 ipv4 = 1; 338 break; /* '\0' was seen by inet_pton4(). */ 339 } 340 if (ch == '/' && getbits(src, &bits) > 0) 341 break; 342 goto enoent; 343 } 344 if (saw_xdigit) { 345 if (tp + NS_INT16SZ > endp) 346 goto enoent; 347 *tp++ = (u_char) (val >> 8) & 0xff; 348 *tp++ = (u_char) val & 0xff; 349 } 350 if (bits == -1) 351 bits = 128; 352 353 words = (bits + 15) / 16; 354 if (words < 2) 355 words = 2; 356 if (ipv4) 357 words = 8; 358 endp = tmp + 2 * words; 359 360 if (colonp != NULL) { 361 /* 362 * Since some memmove()'s erroneously fail to handle 363 * overlapping regions, we'll do the shift by hand. 364 */ 365 const ptrdiff_t n = tp - colonp; 366 int i; 367 368 if (tp == endp) 369 goto enoent; 370 for (i = 1; i <= n; i++) { 371 endp[- i] = colonp[n - i]; 372 colonp[n - i] = 0; 373 } 374 tp = endp; 375 } 376 if (tp != endp) 377 goto enoent; 378 379 bytes = (bits + 7) / 8; 380 if (bytes > size) 381 goto emsgsize; 382 memcpy(dst, tmp, bytes); 383 return (bits); 384 385 enoent: 386 errno = ENOENT; 387 return (-1); 388 389 emsgsize: 390 errno = EMSGSIZE; 391 return (-1); 392 } 393 394 /* 395 * int 396 * inet_net_pton(af, src, dst, size) 397 * convert network number from presentation to network format. 398 * accepts hex octets, hex strings, decimal octets, and /CIDR. 399 * "size" is in bytes and describes "dst". 400 * return: 401 * number of bits, either imputed classfully or specified with /CIDR, 402 * or -1 if some failure occurred (check errno). ENOENT means it was 403 * not a valid network specification. 404 * author: 405 * Paul Vixie (ISC), June 1996 406 */ 407 int 408 inet_net_pton(int af, const char *src, void *dst, size_t size) 409 { 410 switch (af) { 411 case AF_INET: 412 return (inet_net_pton_ipv4(src, dst, size)); 413 case AF_INET6: 414 return (inet_net_pton_ipv6(src, dst, size)); 415 default: 416 errno = EAFNOSUPPORT; 417 return (-1); 418 } 419 } 420 421 #undef inet_net_pton 422 #pragma weak inet_net_pton = __inet_net_pton 423