inet_cidr_pton.c revision 270838
1147883Sscottl/* 2147883Sscottl * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 3147883Sscottl * Copyright (c) 1998,1999 by Internet Software Consortium. 4147883Sscottl * 5147883Sscottl * Permission to use, copy, modify, and distribute this software for any 6147883Sscottl * purpose with or without fee is hereby granted, provided that the above 7147883Sscottl * copyright notice and this permission notice appear in all copies. 8147883Sscottl * 9147883Sscottl * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 10147883Sscottl * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11147883Sscottl * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 12147883Sscottl * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13147883Sscottl * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14147883Sscottl * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 15147883Sscottl * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16147883Sscottl */ 17147883Sscottl 18147883Sscottl#if defined(LIBC_SCCS) && !defined(lint) 19148679Sgibbsstatic const char rcsid[] = "$Id: inet_cidr_pton.c,v 1.6 2005/04/27 04:56:19 sra Exp $"; 20148679Sgibbs#endif 21148679Sgibbs#include <sys/cdefs.h> 22147883Sscottl__FBSDID("$FreeBSD: stable/10/lib/libc/inet/inet_cidr_pton.c 270838 2014-08-30 10:16:25Z ume $"); 23147883Sscottl 24147883Sscottl#include "port_before.h" 25147883Sscottl 26147883Sscottl#include <sys/types.h> 27147883Sscottl#include <sys/socket.h> 28147883Sscottl#include <netinet/in.h> 29147883Sscottl#include <arpa/nameser.h> 30147883Sscottl#include <arpa/inet.h> 31147883Sscottl 32147883Sscottl#include <assert.h> 33147883Sscottl#include <ctype.h> 34147883Sscottl#include <errno.h> 35159052Smjacob#include <stdio.h> 36159052Smjacob#include <string.h> 37159052Smjacob#include <stdlib.h> 38159052Smjacob 39159052Smjacob#include "port_after.h" 40159052Smjacob 41159052Smjacob#ifdef SPRINTF_CHAR 42159052Smjacob# define SPRINTF(x) strlen(sprintf/**/x) 43147883Sscottl#else 44147883Sscottl# define SPRINTF(x) ((size_t)sprintf x) 45147883Sscottl#endif 46147883Sscottl 47147883Sscottlstatic int inet_cidr_pton_ipv4 __P((const char *src, u_char *dst, 48147883Sscottl int *bits, int ipv6)); 49147883Sscottlstatic int inet_cidr_pton_ipv6 __P((const char *src, u_char *dst, 50147883Sscottl int *bits)); 51147883Sscottl 52147883Sscottlstatic int getbits(const char *, int ipv6); 53147883Sscottl 54147883Sscottl/*% 55147883Sscottl * int 56158934Smjacob * inet_cidr_pton(af, src, dst, *bits) 57158934Smjacob * convert network address from presentation to network format. 58158934Smjacob * accepts inet_pton()'s input for this "af" plus trailing "/CIDR". 59158934Smjacob * "dst" is assumed large enough for its "af". "bits" is set to the 60158934Smjacob * /CIDR prefix length, which can have defaults (like /32 for IPv4). 61158934Smjacob * return: 62158934Smjacob * -1 if an error occurred (inspect errno; ENOENT means bad format). 63147883Sscottl * 0 if successful conversion occurred. 64158934Smjacob * note: 65158934Smjacob * 192.5.5.1/28 has a nonzero host part, which means it isn't a network 66158934Smjacob * as called for by inet_net_pton() but it can be a host address with 67158934Smjacob * an included netmask. 68158934Smjacob * author: 69147883Sscottl * Paul Vixie (ISC), October 1998 70158982Smjacob */ 71159050Smjacobint 72160395Smjacobinet_cidr_pton(int af, const char *src, void *dst, int *bits) { 73147883Sscottl switch (af) { 74158934Smjacob case AF_INET: 75160395Smjacob return (inet_cidr_pton_ipv4(src, dst, bits, 0)); 76147883Sscottl case AF_INET6: 77158934Smjacob return (inet_cidr_pton_ipv6(src, dst, bits)); 78158934Smjacob default: 79158982Smjacob errno = EAFNOSUPPORT; 80158934Smjacob return (-1); 81147883Sscottl } 82147883Sscottl} 83147883Sscottl 84147883Sscottlstatic const char digits[] = "0123456789"; 85147883Sscottl 86147883Sscottlstatic int 87147883Sscottlinet_cidr_pton_ipv4(const char *src, u_char *dst, int *pbits, int ipv6) { 88147883Sscottl const u_char *odst = dst; 89147883Sscottl int n, ch, tmp, bits; 90147883Sscottl size_t size = 4; 91147883Sscottl 92147883Sscottl /* Get the mantissa. */ 93147883Sscottl while (ch = *src++, (isascii(ch) && isdigit(ch))) { 94147883Sscottl tmp = 0; 95147883Sscottl do { 96147883Sscottl n = strchr(digits, ch) - digits; 97147883Sscottl assert(n >= 0 && n <= 9); 98147883Sscottl tmp *= 10; 99147883Sscottl tmp += n; 100147883Sscottl if (tmp > 255) 101147883Sscottl goto enoent; 102147883Sscottl } while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch)); 103147883Sscottl if (size-- == 0U) 104 goto emsgsize; 105 *dst++ = (u_char) tmp; 106 if (ch == '\0' || ch == '/') 107 break; 108 if (ch != '.') 109 goto enoent; 110 } 111 112 /* Get the prefix length if any. */ 113 bits = -1; 114 if (ch == '/' && dst > odst) { 115 bits = getbits(src, ipv6); 116 if (bits == -2) 117 goto enoent; 118 } else if (ch != '\0') 119 goto enoent; 120 121 /* Prefix length can default to /32 only if all four octets spec'd. */ 122 if (bits == -1) { 123 if (dst - odst == 4) 124 bits = ipv6 ? 128 : 32; 125 else 126 goto enoent; 127 } 128 129 /* If nothing was written to the destination, we found no address. */ 130 if (dst == odst) 131 goto enoent; 132 133 /* If prefix length overspecifies mantissa, life is bad. */ 134 if (((bits - (ipv6 ? 96 : 0)) / 8) > (dst - odst)) 135 goto enoent; 136 137 /* Extend address to four octets. */ 138 while (size-- > 0U) 139 *dst++ = 0; 140 141 *pbits = bits; 142 return (0); 143 144 enoent: 145 errno = ENOENT; 146 return (-1); 147 148 emsgsize: 149 errno = EMSGSIZE; 150 return (-1); 151} 152 153static int 154inet_cidr_pton_ipv6(const char *src, u_char *dst, int *pbits) { 155 static const char xdigits_l[] = "0123456789abcdef", 156 xdigits_u[] = "0123456789ABCDEF"; 157 u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; 158 const char *xdigits, *curtok; 159 int ch, saw_xdigit; 160 u_int val; 161 int bits; 162 163 memset((tp = tmp), '\0', NS_IN6ADDRSZ); 164 endp = tp + NS_IN6ADDRSZ; 165 colonp = NULL; 166 /* Leading :: requires some special handling. */ 167 if (*src == ':') 168 if (*++src != ':') 169 return (0); 170 curtok = src; 171 saw_xdigit = 0; 172 val = 0; 173 bits = -1; 174 while ((ch = *src++) != '\0') { 175 const char *pch; 176 177 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) 178 pch = strchr((xdigits = xdigits_u), ch); 179 if (pch != NULL) { 180 val <<= 4; 181 val |= (pch - xdigits); 182 if (val > 0xffff) 183 return (0); 184 saw_xdigit = 1; 185 continue; 186 } 187 if (ch == ':') { 188 curtok = src; 189 if (!saw_xdigit) { 190 if (colonp) 191 return (0); 192 colonp = tp; 193 continue; 194 } else if (*src == '\0') { 195 return (0); 196 } 197 if (tp + NS_INT16SZ > endp) 198 return (0); 199 *tp++ = (u_char) (val >> 8) & 0xff; 200 *tp++ = (u_char) val & 0xff; 201 saw_xdigit = 0; 202 val = 0; 203 continue; 204 } 205 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && 206 inet_cidr_pton_ipv4(curtok, tp, &bits, 1) == 0) { 207 tp += NS_INADDRSZ; 208 saw_xdigit = 0; 209 break; /*%< '\\0' was seen by inet_pton4(). */ 210 } 211 if (ch == '/') { 212 bits = getbits(src, 1); 213 if (bits == -2) 214 goto enoent; 215 break; 216 } 217 goto enoent; 218 } 219 if (saw_xdigit) { 220 if (tp + NS_INT16SZ > endp) 221 goto emsgsize; 222 *tp++ = (u_char) (val >> 8) & 0xff; 223 *tp++ = (u_char) val & 0xff; 224 } 225 if (colonp != NULL) { 226 /* 227 * Since some memmove()'s erroneously fail to handle 228 * overlapping regions, we'll do the shift by hand. 229 */ 230 const int n = tp - colonp; 231 int i; 232 233 if (tp == endp) 234 goto enoent; 235 for (i = 1; i <= n; i++) { 236 endp[- i] = colonp[n - i]; 237 colonp[n - i] = 0; 238 } 239 tp = endp; 240 } 241 242 memcpy(dst, tmp, NS_IN6ADDRSZ); 243 244 *pbits = bits; 245 return (0); 246 247 enoent: 248 errno = ENOENT; 249 return (-1); 250 251 emsgsize: 252 errno = EMSGSIZE; 253 return (-1); 254} 255 256static int 257getbits(const char *src, int ipv6) { 258 int bits = 0; 259 char *cp, ch; 260 261 if (*src == '\0') /*%< syntax */ 262 return (-2); 263 do { 264 ch = *src++; 265 cp = strchr(digits, ch); 266 if (cp == NULL) /*%< syntax */ 267 return (-2); 268 bits *= 10; 269 bits += cp - digits; 270 if (bits == 0 && *src != '\0') /*%< no leading zeros */ 271 return (-2); 272 if (bits > (ipv6 ? 128 : 32)) /*%< range error */ 273 return (-2); 274 } while (*src != '\0'); 275 276 return (bits); 277} 278 279/*! \file */ 280