1 /* $OpenBSD: inet_net_pton.c,v 1.3 2003/04/25 19:40:25 henning Exp $ */ 2 3 /* 4 * Copyright (c) 1996 by Internet Software Consortium. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 11 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 12 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 13 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 16 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 17 * SOFTWARE. 18 */ 19 20 #if defined(LIBC_SCCS) && !defined(lint) 21 #if 0 22 static const char rcsid[] = "$From: inet_net_pton.c,v 8.3 1996/11/11 06:36:52 vixie Exp $"; 23 #else 24 static const char rcsid[] = "$OpenBSD: inet_net_pton.c,v 1.3 2003/04/25 19:40:25 henning Exp $"; 25 #endif 26 #endif 27 28 #include <sys/types.h> 29 #include <sys/socket.h> 30 #include <netinet/in.h> 31 #include <arpa/inet.h> 32 33 #include <assert.h> 34 #include <ctype.h> 35 #include <errno.h> 36 #include <stdio.h> 37 #include <string.h> 38 #include <stdlib.h> 39 40 static int inet_net_pton_ipv4(const char *, u_char *, size_t); 41 42 /* 43 * static int 44 * inet_net_pton(af, src, dst, size) 45 * convert network number from presentation to network format. 46 * accepts hex octets, hex strings, decimal octets, and /CIDR. 47 * "size" is in bytes and describes "dst". 48 * return: 49 * number of bits, either imputed classfully or specified with /CIDR, 50 * or -1 if some failure occurred (check errno). ENOENT means it was 51 * not a valid network specification. 52 * author: 53 * Paul Vixie (ISC), June 1996 54 */ 55 int 56 inet_net_pton(af, src, dst, size) 57 int af; 58 const char *src; 59 void *dst; 60 size_t size; 61 { 62 switch (af) { 63 case AF_INET: 64 return (inet_net_pton_ipv4(src, dst, size)); 65 default: 66 errno = EAFNOSUPPORT; 67 return (-1); 68 } 69 } 70 71 /* 72 * static int 73 * inet_net_pton_ipv4(src, dst, size) 74 * convert IPv4 network number from presentation to network format. 75 * accepts hex octets, hex strings, decimal octets, and /CIDR. 76 * "size" is in bytes and describes "dst". 77 * return: 78 * number of bits, either imputed classfully or specified with /CIDR, 79 * or -1 if some failure occurred (check errno). ENOENT means it was 80 * not an IPv4 network specification. 81 * note: 82 * network byte order assumed. this means 192.5.5.240/28 has 83 * 0x11110000 in its fourth octet. 84 * author: 85 * Paul Vixie (ISC), June 1996 86 */ 87 static int 88 inet_net_pton_ipv4(src, dst, size) 89 const char *src; 90 u_char *dst; 91 size_t size; 92 { 93 static const char 94 xdigits[] = "0123456789abcdef", 95 digits[] = "0123456789"; 96 int n, ch, tmp, dirty, bits; 97 const u_char *odst = dst; 98 99 ch = *src++; 100 if (ch == '0' && (src[0] == 'x' || src[0] == 'X') 101 && isascii(src[1]) && isxdigit(src[1])) { 102 /* Hexadecimal: Eat nybble string. */ 103 if (size <= 0) 104 goto emsgsize; 105 *dst = 0, dirty = 0; 106 src++; /* skip x or X. */ 107 while ((ch = *src++) != '\0' && 108 isascii(ch) && isxdigit(ch)) { 109 if (isupper(ch)) 110 ch = tolower(ch); 111 n = strchr(xdigits, ch) - xdigits; 112 assert(n >= 0 && n <= 15); 113 *dst |= n; 114 if (!dirty++) 115 *dst <<= 4; 116 else if (size-- > 0) 117 *++dst = 0, dirty = 0; 118 else 119 goto emsgsize; 120 } 121 if (dirty) 122 size--; 123 } else if (isascii(ch) && isdigit(ch)) { 124 /* Decimal: eat dotted digit string. */ 125 for (;;) { 126 tmp = 0; 127 do { 128 n = strchr(digits, ch) - digits; 129 assert(n >= 0 && n <= 9); 130 tmp *= 10; 131 tmp += n; 132 if (tmp > 255) 133 goto enoent; 134 } while ((ch = *src++) != '\0' && 135 isascii(ch) && isdigit(ch)); 136 if (size-- <= 0) 137 goto emsgsize; 138 *dst++ = (u_char) tmp; 139 if (ch == '\0' || ch == '/') 140 break; 141 if (ch != '.') 142 goto enoent; 143 ch = *src++; 144 if (!isascii(ch) || !isdigit(ch)) 145 goto enoent; 146 } 147 } else 148 goto enoent; 149 150 bits = -1; 151 if (ch == '/' && isascii(src[0]) && isdigit(src[0]) && dst > odst) { 152 /* CIDR width specifier. Nothing can follow it. */ 153 ch = *src++; /* Skip over the /. */ 154 bits = 0; 155 do { 156 n = strchr(digits, ch) - digits; 157 assert(n >= 0 && n <= 9); 158 bits *= 10; 159 bits += n; 160 } while ((ch = *src++) != '\0' && 161 isascii(ch) && isdigit(ch)); 162 if (ch != '\0') 163 goto enoent; 164 if (bits > 32) 165 goto emsgsize; 166 } 167 168 /* Firey death and destruction unless we prefetched EOS. */ 169 if (ch != '\0') 170 goto enoent; 171 172 /* If nothing was written to the destination, we found no address. */ 173 if (dst == odst) 174 goto enoent; 175 /* If no CIDR spec was given, infer width from net class. */ 176 if (bits == -1) { 177 if (*odst >= 240) /* Class E */ 178 bits = 32; 179 else if (*odst >= 224) /* Class D */ 180 bits = 4; 181 else if (*odst >= 192) /* Class C */ 182 bits = 24; 183 else if (*odst >= 128) /* Class B */ 184 bits = 16; 185 else /* Class A */ 186 bits = 8; 187 /* If imputed mask is narrower than specified octets, widen. */ 188 if (bits < ((dst - odst) * 8)) 189 bits = (dst - odst) * 8; 190 } 191 /* Extend network to cover the actual mask. */ 192 while (bits > ((dst - odst) * 8)) { 193 if (size-- <= 0) 194 goto emsgsize; 195 *dst++ = '\0'; 196 } 197 return (bits); 198 199 enoent: 200 errno = ENOENT; 201 return (-1); 202 203 emsgsize: 204 errno = EMSGSIZE; 205 return (-1); 206 } 207