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