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