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.1 2004/05/20 23:13:02 christos Exp $"); 24 #endif 25 #endif 26 27 #include "port_before.h" 28 29 #include "namespace.h" 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 <isc/assertions.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 static const char xdigits[] = "0123456789abcdef"; 75 static const char digits[] = "0123456789"; 76 int n, ch, tmp = 0, dirty, bits; 77 const u_char *odst = dst; 78 79 ch = *src++; 80 if (ch == '0' && (src[0] == 'x' || src[0] == 'X') 81 && isascii((u_char)(src[1])) 82 && isxdigit((u_char)(src[1]))) { 83 /* Hexadecimal: Eat nybble string. */ 84 if (size == 0) 85 goto emsgsize; 86 dirty = 0; 87 src++; /* skip x or X. */ 88 while ((ch = *src++) != '\0' && isascii((u_char)ch) 89 && isxdigit((u_char)ch)) { 90 if (isupper((u_char)ch)) 91 ch = tolower((u_char)ch); 92 n = strchr(xdigits, ch) - xdigits; 93 INSIST(n >= 0 && n <= 15); 94 if (dirty == 0) 95 tmp = n; 96 else 97 tmp = (tmp << 4) | n; 98 if (++dirty == 2) { 99 if (size-- == 0) 100 goto emsgsize; 101 *dst++ = (u_char) tmp; 102 dirty = 0; 103 } 104 } 105 if (dirty) { /* Odd trailing nybble? */ 106 if (size-- == 0) 107 goto emsgsize; 108 *dst++ = (u_char) (tmp << 4); 109 } 110 } else if (isascii((u_char)ch) && isdigit((u_char)ch)) { 111 /* Decimal: eat dotted digit string. */ 112 for (;;) { 113 tmp = 0; 114 do { 115 n = strchr(digits, ch) - digits; 116 INSIST(n >= 0 && n <= 9); 117 tmp *= 10; 118 tmp += n; 119 if (tmp > 255) 120 goto enoent; 121 } while ((ch = *src++) != '\0' && 122 isascii((u_char)ch) && isdigit((u_char)ch)); 123 if (size-- == 0) 124 goto emsgsize; 125 *dst++ = (u_char) tmp; 126 if (ch == '\0' || ch == '/') 127 break; 128 if (ch != '.') 129 goto enoent; 130 ch = *src++; 131 if (!isascii((u_char)ch) || !isdigit((u_char)ch)) 132 goto enoent; 133 } 134 } else 135 goto enoent; 136 137 bits = -1; 138 if (ch == '/' && isascii((u_char)(src[0])) && 139 isdigit((u_char)(src[0])) && dst > odst) { 140 /* CIDR width specifier. Nothing can follow it. */ 141 ch = *src++; /* Skip over the /. */ 142 bits = 0; 143 do { 144 n = strchr(digits, ch) - digits; 145 INSIST(n >= 0 && n <= 9); 146 bits *= 10; 147 bits += n; 148 } while ((ch = *src++) != '\0' && isascii((u_char)ch) 149 && isdigit((u_char)ch)); 150 if (ch != '\0') 151 goto enoent; 152 if (bits > 32) 153 goto emsgsize; 154 } 155 156 /* Firey death and destruction unless we prefetched EOS. */ 157 if (ch != '\0') 158 goto enoent; 159 160 /* If nothing was written to the destination, we found no address. */ 161 if (dst == odst) 162 goto enoent; 163 /* If no CIDR spec was given, infer width from net class. */ 164 if (bits == -1) { 165 if (*odst >= 240) /* Class E */ 166 bits = 32; 167 else if (*odst >= 224) /* Class D */ 168 bits = 4; 169 else if (*odst >= 192) /* Class C */ 170 bits = 24; 171 else if (*odst >= 128) /* Class B */ 172 bits = 16; 173 else /* Class A */ 174 bits = 8; 175 /* If imputed mask is narrower than specified octets, widen. */ 176 if (bits >= 8 && bits < ((dst - odst) * 8)) 177 bits = (dst - odst) * 8; 178 } 179 /* Extend network to cover the actual mask. */ 180 while (bits > ((dst - odst) * 8)) { 181 if (size-- == 0) 182 goto emsgsize; 183 *dst++ = '\0'; 184 } 185 return (bits); 186 187 enoent: 188 errno = ENOENT; 189 return (-1); 190 191 emsgsize: 192 errno = EMSGSIZE; 193 return (-1); 194 } 195 196 static int 197 getbits(const char *src, int *bitsp) { 198 static const char digits[] = "0123456789"; 199 int n; 200 int val; 201 char ch; 202 203 val = 0; 204 n = 0; 205 while ((ch = *src++) != '\0') { 206 const char *pch; 207 208 pch = strchr(digits, ch); 209 if (pch != NULL) { 210 if (n++ != 0 && val == 0) /* no leading zeros */ 211 return (0); 212 val *= 10; 213 val += (pch - digits); 214 if (val > 128) /* range */ 215 return (0); 216 continue; 217 } 218 return (0); 219 } 220 if (n == 0) 221 return (0); 222 *bitsp = val; 223 return (1); 224 } 225 226 static int 227 getv4(const char *src, u_char *dst, int *bitsp) { 228 static const char digits[] = "0123456789"; 229 u_char *odst = dst; 230 int n; 231 u_int val; 232 char ch; 233 234 val = 0; 235 n = 0; 236 while ((ch = *src++) != '\0') { 237 const char *pch; 238 239 pch = strchr(digits, ch); 240 if (pch != NULL) { 241 if (n++ != 0 && val == 0) /* no leading zeros */ 242 return (0); 243 val *= 10; 244 val += (pch - digits); 245 if (val > 255) /* range */ 246 return (0); 247 continue; 248 } 249 if (ch == '.' || ch == '/') { 250 if (dst - odst > 3) /* too many octets? */ 251 return (0); 252 *dst++ = val; 253 if (ch == '/') 254 return (getbits(src, bitsp)); 255 val = 0; 256 n = 0; 257 continue; 258 } 259 return (0); 260 } 261 if (n == 0) 262 return (0); 263 if (dst - odst > 3) /* too many octets? */ 264 return (0); 265 *dst++ = val; 266 return (1); 267 } 268 269 static int 270 inet_net_pton_ipv6(const char *src, u_char *dst, size_t size) { 271 static const char xdigits_l[] = "0123456789abcdef", 272 xdigits_u[] = "0123456789ABCDEF"; 273 u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; 274 const char *xdigits, *curtok; 275 int ch, saw_xdigit; 276 u_int val; 277 int digits; 278 int bits; 279 size_t bytes; 280 int words; 281 int ipv4; 282 283 memset((tp = tmp), '\0', NS_IN6ADDRSZ); 284 endp = tp + NS_IN6ADDRSZ; 285 colonp = NULL; 286 /* Leading :: requires some special handling. */ 287 if (*src == ':') 288 if (*++src != ':') 289 goto enoent; 290 curtok = src; 291 saw_xdigit = 0; 292 val = 0; 293 digits = 0; 294 bits = -1; 295 ipv4 = 0; 296 while ((ch = *src++) != '\0') { 297 const char *pch; 298 299 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) 300 pch = strchr((xdigits = xdigits_u), ch); 301 if (pch != NULL) { 302 val <<= 4; 303 val |= (pch - xdigits); 304 if (++digits > 4) 305 goto enoent; 306 saw_xdigit = 1; 307 continue; 308 } 309 if (ch == ':') { 310 curtok = src; 311 if (!saw_xdigit) { 312 if (colonp) 313 goto enoent; 314 colonp = tp; 315 continue; 316 } else if (*src == '\0') 317 goto enoent; 318 if (tp + NS_INT16SZ > endp) 319 return (0); 320 *tp++ = (u_char) (val >> 8) & 0xff; 321 *tp++ = (u_char) val & 0xff; 322 saw_xdigit = 0; 323 digits = 0; 324 val = 0; 325 continue; 326 } 327 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && 328 getv4(curtok, tp, &bits) > 0) { 329 tp += NS_INADDRSZ; 330 saw_xdigit = 0; 331 ipv4 = 1; 332 break; /* '\0' was seen by inet_pton4(). */ 333 } 334 if (ch == '/' && getbits(src, &bits) > 0) 335 break; 336 goto enoent; 337 } 338 if (saw_xdigit) { 339 if (tp + NS_INT16SZ > endp) 340 goto enoent; 341 *tp++ = (u_char) (val >> 8) & 0xff; 342 *tp++ = (u_char) val & 0xff; 343 } 344 if (bits == -1) 345 bits = 128; 346 347 words = (bits + 15) / 16; 348 if (words < 2) 349 words = 2; 350 if (ipv4) 351 words = 8; 352 endp = tmp + 2 * words; 353 354 if (colonp != NULL) { 355 /* 356 * Since some memmove()'s erroneously fail to handle 357 * overlapping regions, we'll do the shift by hand. 358 */ 359 const int n = tp - colonp; 360 int i; 361 362 if (tp == endp) 363 goto enoent; 364 for (i = 1; i <= n; i++) { 365 endp[- i] = colonp[n - i]; 366 colonp[n - i] = 0; 367 } 368 tp = endp; 369 } 370 if (tp != endp) 371 goto enoent; 372 373 bytes = (bits + 7) / 8; 374 if (bytes > size) 375 goto emsgsize; 376 memcpy(dst, tmp, bytes); 377 return (bits); 378 379 enoent: 380 errno = ENOENT; 381 return (-1); 382 383 emsgsize: 384 errno = EMSGSIZE; 385 return (-1); 386 } 387 388 /* 389 * int 390 * inet_net_pton(af, src, dst, size) 391 * convert network number from presentation to network format. 392 * accepts hex octets, hex strings, decimal octets, and /CIDR. 393 * "size" is in bytes and describes "dst". 394 * return: 395 * number of bits, either imputed classfully or specified with /CIDR, 396 * or -1 if some failure occurred (check errno). ENOENT means it was 397 * not a valid network specification. 398 * author: 399 * Paul Vixie (ISC), June 1996 400 */ 401 int 402 inet_net_pton(int af, const char *src, void *dst, size_t size) { 403 switch (af) { 404 case AF_INET: 405 return (inet_net_pton_ipv4(src, dst, size)); 406 case AF_INET6: 407 return (inet_net_pton_ipv6(src, dst, size)); 408 default: 409 errno = EAFNOSUPPORT; 410 return (-1); 411 } 412 } 413