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