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