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