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