1 /*- 2 * SPDX-License-Identifier: ISC 3 * 4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (c) 1998,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 ISC DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include "port_before.h" 21 22 #include <sys/types.h> 23 #include <sys/socket.h> 24 #include <netinet/in.h> 25 #include <arpa/nameser.h> 26 #include <arpa/inet.h> 27 28 #include <assert.h> 29 #include <ctype.h> 30 #include <errno.h> 31 #include <stdio.h> 32 #include <string.h> 33 #include <stdlib.h> 34 35 #include "port_after.h" 36 37 #ifdef SPRINTF_CHAR 38 # define SPRINTF(x) strlen(sprintf/**/x) 39 #else 40 # define SPRINTF(x) ((size_t)sprintf x) 41 #endif 42 43 static int inet_cidr_pton_ipv4 (const char *src, u_char *dst, int *bits, 44 int ipv6); 45 static int inet_cidr_pton_ipv6 (const char *src, u_char *dst, int *bits); 46 47 static int getbits(const char *, int ipv6); 48 49 /*% 50 * int 51 * inet_cidr_pton(af, src, dst, *bits) 52 * convert network address from presentation to network format. 53 * accepts inet_pton()'s input for this "af" plus trailing "/CIDR". 54 * "dst" is assumed large enough for its "af". "bits" is set to the 55 * /CIDR prefix length, which can have defaults (like /32 for IPv4). 56 * return: 57 * -1 if an error occurred (inspect errno; ENOENT means bad format). 58 * 0 if successful conversion occurred. 59 * note: 60 * 192.5.5.1/28 has a nonzero host part, which means it isn't a network 61 * as called for by inet_net_pton() but it can be a host address with 62 * an included netmask. 63 * author: 64 * Paul Vixie (ISC), October 1998 65 */ 66 int 67 inet_cidr_pton(int af, const char *src, void *dst, int *bits) { 68 switch (af) { 69 case AF_INET: 70 return (inet_cidr_pton_ipv4(src, dst, bits, 0)); 71 case AF_INET6: 72 return (inet_cidr_pton_ipv6(src, dst, bits)); 73 default: 74 errno = EAFNOSUPPORT; 75 return (-1); 76 } 77 } 78 79 static const char digits[] = "0123456789"; 80 81 static int 82 inet_cidr_pton_ipv4(const char *src, u_char *dst, int *pbits, int ipv6) { 83 const u_char *odst = dst; 84 int n, ch, tmp, bits; 85 size_t size = 4; 86 87 /* Get the mantissa. */ 88 while (ch = *src++, (isascii(ch) && isdigit(ch))) { 89 tmp = 0; 90 do { 91 n = strchr(digits, ch) - digits; 92 assert(n >= 0 && n <= 9); 93 tmp *= 10; 94 tmp += n; 95 if (tmp > 255) 96 goto enoent; 97 } while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch)); 98 if (size-- == 0U) 99 goto emsgsize; 100 *dst++ = (u_char) tmp; 101 if (ch == '\0' || ch == '/') 102 break; 103 if (ch != '.') 104 goto enoent; 105 } 106 107 /* Get the prefix length if any. */ 108 bits = -1; 109 if (ch == '/' && dst > odst) { 110 bits = getbits(src, ipv6); 111 if (bits == -2) 112 goto enoent; 113 } else if (ch != '\0') 114 goto enoent; 115 116 /* Prefix length can default to /32 only if all four octets spec'd. */ 117 if (bits == -1) { 118 if (dst - odst == 4) 119 bits = ipv6 ? 128 : 32; 120 else 121 goto enoent; 122 } 123 124 /* If nothing was written to the destination, we found no address. */ 125 if (dst == odst) 126 goto enoent; 127 128 /* If prefix length overspecifies mantissa, life is bad. */ 129 if (((bits - (ipv6 ? 96 : 0)) / 8) > (dst - odst)) 130 goto enoent; 131 132 /* Extend address to four octets. */ 133 while (size-- > 0U) 134 *dst++ = 0; 135 136 *pbits = bits; 137 return (0); 138 139 enoent: 140 errno = ENOENT; 141 return (-1); 142 143 emsgsize: 144 errno = EMSGSIZE; 145 return (-1); 146 } 147 148 static int 149 inet_cidr_pton_ipv6(const char *src, u_char *dst, int *pbits) { 150 static const char xdigits_l[] = "0123456789abcdef", 151 xdigits_u[] = "0123456789ABCDEF"; 152 u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; 153 const char *xdigits, *curtok; 154 int ch, saw_xdigit; 155 u_int val; 156 int bits; 157 158 memset((tp = tmp), '\0', NS_IN6ADDRSZ); 159 endp = tp + NS_IN6ADDRSZ; 160 colonp = NULL; 161 /* Leading :: requires some special handling. */ 162 if (*src == ':') 163 if (*++src != ':') 164 return (0); 165 curtok = src; 166 saw_xdigit = 0; 167 val = 0; 168 bits = -1; 169 while ((ch = *src++) != '\0') { 170 const char *pch; 171 172 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) 173 pch = strchr((xdigits = xdigits_u), ch); 174 if (pch != NULL) { 175 val <<= 4; 176 val |= (pch - xdigits); 177 if (val > 0xffff) 178 return (0); 179 saw_xdigit = 1; 180 continue; 181 } 182 if (ch == ':') { 183 curtok = src; 184 if (!saw_xdigit) { 185 if (colonp) 186 return (0); 187 colonp = tp; 188 continue; 189 } else if (*src == '\0') { 190 return (0); 191 } 192 if (tp + NS_INT16SZ > endp) 193 return (0); 194 *tp++ = (u_char) (val >> 8) & 0xff; 195 *tp++ = (u_char) val & 0xff; 196 saw_xdigit = 0; 197 val = 0; 198 continue; 199 } 200 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && 201 inet_cidr_pton_ipv4(curtok, tp, &bits, 1) == 0) { 202 tp += NS_INADDRSZ; 203 saw_xdigit = 0; 204 break; /*%< '\\0' was seen by inet_pton4(). */ 205 } 206 if (ch == '/') { 207 bits = getbits(src, 1); 208 if (bits == -2) 209 goto enoent; 210 break; 211 } 212 goto enoent; 213 } 214 if (saw_xdigit) { 215 if (tp + NS_INT16SZ > endp) 216 goto emsgsize; 217 *tp++ = (u_char) (val >> 8) & 0xff; 218 *tp++ = (u_char) val & 0xff; 219 } 220 if (colonp != NULL) { 221 /* 222 * Since some memmove()'s erroneously fail to handle 223 * overlapping regions, we'll do the shift by hand. 224 */ 225 const int n = tp - colonp; 226 int i; 227 228 if (tp == endp) 229 goto enoent; 230 for (i = 1; i <= n; i++) { 231 endp[- i] = colonp[n - i]; 232 colonp[n - i] = 0; 233 } 234 tp = endp; 235 } 236 237 memcpy(dst, tmp, NS_IN6ADDRSZ); 238 239 *pbits = bits; 240 return (0); 241 242 enoent: 243 errno = ENOENT; 244 return (-1); 245 246 emsgsize: 247 errno = EMSGSIZE; 248 return (-1); 249 } 250 251 static int 252 getbits(const char *src, int ipv6) { 253 int bits = 0; 254 char *cp, ch; 255 256 if (*src == '\0') /*%< syntax */ 257 return (-2); 258 do { 259 ch = *src++; 260 cp = strchr(digits, ch); 261 if (cp == NULL) /*%< syntax */ 262 return (-2); 263 bits *= 10; 264 bits += cp - digits; 265 if (bits == 0 && *src != '\0') /*%< no leading zeros */ 266 return (-2); 267 if (bits > (ipv6 ? 128 : 32)) /*%< range error */ 268 return (-2); 269 } while (*src != '\0'); 270 271 return (bits); 272 } 273 274 /*! \file */ 275