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