1156952Sume/* 2156952Sume * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 3156952Sume * Copyright (c) 1998,1999 by Internet Software Consortium. 4156952Sume * 5156952Sume * Permission to use, copy, modify, and distribute this software for any 6156952Sume * purpose with or without fee is hereby granted, provided that the above 7156952Sume * copyright notice and this permission notice appear in all copies. 8156952Sume * 9156952Sume * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 10156952Sume * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11156952Sume * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 12156952Sume * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13156952Sume * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14156952Sume * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 15156952Sume * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16156952Sume */ 17156952Sume 18156952Sume#if defined(LIBC_SCCS) && !defined(lint) 19270838Sumestatic const char rcsid[] = "$Id: inet_cidr_pton.c,v 1.6 2005/04/27 04:56:19 sra Exp $"; 20156952Sume#endif 21156956Sume#include <sys/cdefs.h> 22156956Sume__FBSDID("$FreeBSD: releng/10.3/lib/libc/inet/inet_cidr_pton.c 270838 2014-08-30 10:16:25Z ume $"); 23156952Sume 24156952Sume#include "port_before.h" 25156952Sume 26156952Sume#include <sys/types.h> 27156952Sume#include <sys/socket.h> 28156952Sume#include <netinet/in.h> 29156952Sume#include <arpa/nameser.h> 30156952Sume#include <arpa/inet.h> 31156952Sume 32156956Sume#include <assert.h> 33156952Sume#include <ctype.h> 34156952Sume#include <errno.h> 35156952Sume#include <stdio.h> 36156952Sume#include <string.h> 37156952Sume#include <stdlib.h> 38156952Sume 39156952Sume#include "port_after.h" 40156952Sume 41156952Sume#ifdef SPRINTF_CHAR 42156952Sume# define SPRINTF(x) strlen(sprintf/**/x) 43156952Sume#else 44156952Sume# define SPRINTF(x) ((size_t)sprintf x) 45156952Sume#endif 46156952Sume 47156952Sumestatic int inet_cidr_pton_ipv4 __P((const char *src, u_char *dst, 48156952Sume int *bits, int ipv6)); 49156952Sumestatic int inet_cidr_pton_ipv6 __P((const char *src, u_char *dst, 50156952Sume int *bits)); 51156952Sume 52156952Sumestatic int getbits(const char *, int ipv6); 53156952Sume 54170244Sume/*% 55156952Sume * int 56156952Sume * inet_cidr_pton(af, src, dst, *bits) 57156952Sume * convert network address from presentation to network format. 58156952Sume * accepts inet_pton()'s input for this "af" plus trailing "/CIDR". 59156952Sume * "dst" is assumed large enough for its "af". "bits" is set to the 60156952Sume * /CIDR prefix length, which can have defaults (like /32 for IPv4). 61156952Sume * return: 62156952Sume * -1 if an error occurred (inspect errno; ENOENT means bad format). 63156952Sume * 0 if successful conversion occurred. 64156952Sume * note: 65156952Sume * 192.5.5.1/28 has a nonzero host part, which means it isn't a network 66156952Sume * as called for by inet_net_pton() but it can be a host address with 67156952Sume * an included netmask. 68156952Sume * author: 69156952Sume * Paul Vixie (ISC), October 1998 70156952Sume */ 71156952Sumeint 72156952Sumeinet_cidr_pton(int af, const char *src, void *dst, int *bits) { 73156952Sume switch (af) { 74156952Sume case AF_INET: 75156952Sume return (inet_cidr_pton_ipv4(src, dst, bits, 0)); 76156952Sume case AF_INET6: 77156952Sume return (inet_cidr_pton_ipv6(src, dst, bits)); 78156952Sume default: 79156952Sume errno = EAFNOSUPPORT; 80156952Sume return (-1); 81156952Sume } 82156952Sume} 83156952Sume 84156952Sumestatic const char digits[] = "0123456789"; 85156952Sume 86156952Sumestatic int 87156952Sumeinet_cidr_pton_ipv4(const char *src, u_char *dst, int *pbits, int ipv6) { 88156952Sume const u_char *odst = dst; 89156952Sume int n, ch, tmp, bits; 90156952Sume size_t size = 4; 91156952Sume 92156952Sume /* Get the mantissa. */ 93156952Sume while (ch = *src++, (isascii(ch) && isdigit(ch))) { 94156952Sume tmp = 0; 95156952Sume do { 96156952Sume n = strchr(digits, ch) - digits; 97156956Sume assert(n >= 0 && n <= 9); 98156952Sume tmp *= 10; 99156952Sume tmp += n; 100156952Sume if (tmp > 255) 101156952Sume goto enoent; 102156952Sume } while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch)); 103156952Sume if (size-- == 0U) 104156952Sume goto emsgsize; 105156952Sume *dst++ = (u_char) tmp; 106156952Sume if (ch == '\0' || ch == '/') 107156952Sume break; 108156952Sume if (ch != '.') 109156952Sume goto enoent; 110156952Sume } 111156952Sume 112156952Sume /* Get the prefix length if any. */ 113156952Sume bits = -1; 114156952Sume if (ch == '/' && dst > odst) { 115156952Sume bits = getbits(src, ipv6); 116156952Sume if (bits == -2) 117156952Sume goto enoent; 118156952Sume } else if (ch != '\0') 119156952Sume goto enoent; 120156952Sume 121156952Sume /* Prefix length can default to /32 only if all four octets spec'd. */ 122156952Sume if (bits == -1) { 123156952Sume if (dst - odst == 4) 124156952Sume bits = ipv6 ? 128 : 32; 125156952Sume else 126156952Sume goto enoent; 127156952Sume } 128156952Sume 129156952Sume /* If nothing was written to the destination, we found no address. */ 130156952Sume if (dst == odst) 131156952Sume goto enoent; 132156952Sume 133156952Sume /* If prefix length overspecifies mantissa, life is bad. */ 134156952Sume if (((bits - (ipv6 ? 96 : 0)) / 8) > (dst - odst)) 135156952Sume goto enoent; 136156952Sume 137156952Sume /* Extend address to four octets. */ 138156952Sume while (size-- > 0U) 139156952Sume *dst++ = 0; 140156952Sume 141156952Sume *pbits = bits; 142156952Sume return (0); 143156952Sume 144156952Sume enoent: 145156952Sume errno = ENOENT; 146156952Sume return (-1); 147156952Sume 148156952Sume emsgsize: 149156952Sume errno = EMSGSIZE; 150156952Sume return (-1); 151156952Sume} 152156952Sume 153156952Sumestatic int 154156952Sumeinet_cidr_pton_ipv6(const char *src, u_char *dst, int *pbits) { 155156952Sume static const char xdigits_l[] = "0123456789abcdef", 156156952Sume xdigits_u[] = "0123456789ABCDEF"; 157156952Sume u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; 158156952Sume const char *xdigits, *curtok; 159156952Sume int ch, saw_xdigit; 160156952Sume u_int val; 161156952Sume int bits; 162156952Sume 163156952Sume memset((tp = tmp), '\0', NS_IN6ADDRSZ); 164156952Sume endp = tp + NS_IN6ADDRSZ; 165156952Sume colonp = NULL; 166156952Sume /* Leading :: requires some special handling. */ 167156952Sume if (*src == ':') 168156952Sume if (*++src != ':') 169156952Sume return (0); 170156952Sume curtok = src; 171156952Sume saw_xdigit = 0; 172156952Sume val = 0; 173156952Sume bits = -1; 174156952Sume while ((ch = *src++) != '\0') { 175156952Sume const char *pch; 176156952Sume 177156952Sume if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) 178156952Sume pch = strchr((xdigits = xdigits_u), ch); 179156952Sume if (pch != NULL) { 180156952Sume val <<= 4; 181156952Sume val |= (pch - xdigits); 182156952Sume if (val > 0xffff) 183156952Sume return (0); 184156952Sume saw_xdigit = 1; 185156952Sume continue; 186156952Sume } 187156952Sume if (ch == ':') { 188156952Sume curtok = src; 189156952Sume if (!saw_xdigit) { 190156952Sume if (colonp) 191156952Sume return (0); 192156952Sume colonp = tp; 193156952Sume continue; 194156952Sume } else if (*src == '\0') { 195156952Sume return (0); 196156952Sume } 197156952Sume if (tp + NS_INT16SZ > endp) 198156952Sume return (0); 199156952Sume *tp++ = (u_char) (val >> 8) & 0xff; 200156952Sume *tp++ = (u_char) val & 0xff; 201156952Sume saw_xdigit = 0; 202156952Sume val = 0; 203156952Sume continue; 204156952Sume } 205156952Sume if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && 206156952Sume inet_cidr_pton_ipv4(curtok, tp, &bits, 1) == 0) { 207156952Sume tp += NS_INADDRSZ; 208156952Sume saw_xdigit = 0; 209170244Sume break; /*%< '\\0' was seen by inet_pton4(). */ 210156952Sume } 211156952Sume if (ch == '/') { 212156952Sume bits = getbits(src, 1); 213156952Sume if (bits == -2) 214156952Sume goto enoent; 215156952Sume break; 216156952Sume } 217156952Sume goto enoent; 218156952Sume } 219156952Sume if (saw_xdigit) { 220156952Sume if (tp + NS_INT16SZ > endp) 221156952Sume goto emsgsize; 222156952Sume *tp++ = (u_char) (val >> 8) & 0xff; 223156952Sume *tp++ = (u_char) val & 0xff; 224156952Sume } 225156952Sume if (colonp != NULL) { 226156952Sume /* 227156952Sume * Since some memmove()'s erroneously fail to handle 228156952Sume * overlapping regions, we'll do the shift by hand. 229156952Sume */ 230156952Sume const int n = tp - colonp; 231156952Sume int i; 232156952Sume 233156952Sume if (tp == endp) 234156952Sume goto enoent; 235156952Sume for (i = 1; i <= n; i++) { 236156952Sume endp[- i] = colonp[n - i]; 237156952Sume colonp[n - i] = 0; 238156952Sume } 239156952Sume tp = endp; 240156952Sume } 241156952Sume 242156952Sume memcpy(dst, tmp, NS_IN6ADDRSZ); 243156952Sume 244156952Sume *pbits = bits; 245156952Sume return (0); 246156952Sume 247156952Sume enoent: 248156952Sume errno = ENOENT; 249156952Sume return (-1); 250156952Sume 251156952Sume emsgsize: 252156952Sume errno = EMSGSIZE; 253156952Sume return (-1); 254156952Sume} 255156952Sume 256156952Sumestatic int 257156952Sumegetbits(const char *src, int ipv6) { 258156952Sume int bits = 0; 259156952Sume char *cp, ch; 260156952Sume 261170244Sume if (*src == '\0') /*%< syntax */ 262156952Sume return (-2); 263156952Sume do { 264156952Sume ch = *src++; 265156952Sume cp = strchr(digits, ch); 266170244Sume if (cp == NULL) /*%< syntax */ 267156952Sume return (-2); 268156952Sume bits *= 10; 269156952Sume bits += cp - digits; 270170244Sume if (bits == 0 && *src != '\0') /*%< no leading zeros */ 271156952Sume return (-2); 272170244Sume if (bits > (ipv6 ? 128 : 32)) /*%< range error */ 273156952Sume return (-2); 274156952Sume } while (*src != '\0'); 275156952Sume 276156952Sume return (bits); 277156952Sume} 278170244Sume 279170244Sume/*! \file */ 280