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