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