/* $OpenBSD: inet_net_pton.c,v 1.14 2022/12/27 17:10:06 jmc Exp $ */12/*3* Copyright (c) 2012 by Gilles Chehade <[email protected]>4* Copyright (c) 1996,1999 by Internet Software Consortium.5*6* SPDX-License-Identifier: ISC7*8* Permission to use, copy, modify, and distribute this software for any9* purpose with or without fee is hereby granted, provided that the above10* copyright notice and this permission notice appear in all copies.11*12* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS13* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES14* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE15* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL16* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR17* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS18* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS19* SOFTWARE.20*/2122#include "port_before.h"2324#include <sys/types.h>25#include <sys/socket.h>26#include <netinet/in.h>27#include <arpa/inet.h>2829#include <assert.h>30#include <ctype.h>31#include <errno.h>32#include <stdio.h>33#include <string.h>34#include <stdlib.h>3536#include "port_after.h"3738static int inet_net_pton_ipv4(const char *, u_char *, size_t);39static int inet_net_pton_ipv6(const char *, u_char *, size_t);4041/*42* static int43* inet_net_pton(af, src, dst, size)44* convert network number from presentation to network format.45* accepts hex octets, hex strings, decimal octets, and /CIDR.46* "size" is in bytes and describes "dst".47* return:48* number of bits, either imputed classfully or specified with /CIDR,49* or -1 if some failure occurred (check errno). ENOENT means it was50* not a valid network specification.51* author:52* Paul Vixie (ISC), June 199653*/54int55inet_net_pton(int af, const char *src, void *dst, size_t size)56{57switch (af) {58case AF_INET:59return (inet_net_pton_ipv4(src, dst, size));60case AF_INET6:61return (inet_net_pton_ipv6(src, dst, size));62default:63errno = EAFNOSUPPORT;64return (-1);65}66}6768/*69* static int70* inet_net_pton_ipv4(src, dst, size)71* convert IPv4 network number from presentation to network format.72* accepts hex octets, hex strings, decimal octets, and /CIDR.73* "size" is in bytes and describes "dst".74* return:75* number of bits, either imputed classfully or specified with /CIDR,76* or -1 if some failure occurred (check errno). ENOENT means it was77* not an IPv4 network specification.78* note:79* network byte order assumed. this means 192.5.5.240/28 has80* 0b11110000 in its fourth octet.81* author:82* Paul Vixie (ISC), June 199683*/84static int85inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)86{87static const char88xdigits[] = "0123456789abcdef",89digits[] = "0123456789";90int n, ch, tmp, dirty, bits;91const u_char *odst = dst;9293ch = (unsigned char)*src++;94if (ch == '0' && (src[0] == 'x' || src[0] == 'X')95&& isascii((unsigned char)src[1]) && isxdigit((unsigned char)src[1])) {96/* Hexadecimal: Eat nybble string. */97if (size == 0)98goto emsgsize;99tmp = 0, dirty = 0;100src++; /* skip x or X. */101while ((ch = (unsigned char)*src++) != '\0' &&102isascii(ch) && isxdigit(ch)) {103if (isupper(ch))104ch = tolower(ch);105n = strchr(xdigits, ch) - xdigits;106assert(n >= 0 && n <= 15);107if (dirty == 0)108tmp = n;109else110tmp = (tmp << 4) | n;111if (++dirty == 2) {112if (size-- == 0)113goto emsgsize;114*dst++ = (u_char) tmp;115dirty = 0;116}117}118if (dirty) { /* Odd trailing nybble? */119if (size-- == 0)120goto emsgsize;121*dst++ = (u_char) (tmp << 4);122}123} else if (isascii(ch) && isdigit(ch)) {124/* Decimal: eat dotted digit string. */125for (;;) {126tmp = 0;127do {128n = strchr(digits, ch) - digits;129assert(n >= 0 && n <= 9);130tmp *= 10;131tmp += n;132if (tmp > 255)133goto enoent;134} while ((ch = (unsigned char)*src++) != '\0' &&135isascii(ch) && isdigit(ch));136if (size-- == 0)137goto emsgsize;138*dst++ = (u_char) tmp;139if (ch == '\0' || ch == '/')140break;141if (ch != '.')142goto enoent;143ch = (unsigned char)*src++;144if (!isascii(ch) || !isdigit(ch))145goto enoent;146}147} else148goto enoent;149150bits = -1;151if (ch == '/' && isascii((unsigned char)src[0]) &&152isdigit((unsigned char)src[0]) && dst > odst) {153/* CIDR width specifier. Nothing can follow it. */154ch = (unsigned char)*src++; /* Skip over the /. */155bits = 0;156do {157n = strchr(digits, ch) - digits;158assert(n >= 0 && n <= 9);159bits *= 10;160bits += n;161if (bits > 32)162goto emsgsize;163} while ((ch = (unsigned char)*src++) != '\0' &&164isascii(ch) && isdigit(ch));165if (ch != '\0')166goto enoent;167}168169/* Fiery death and destruction unless we prefetched EOS. */170if (ch != '\0')171goto enoent;172173/* If nothing was written to the destination, we found no address. */174if (dst == odst)175goto enoent;176/* If no CIDR spec was given, infer width from net class. */177if (bits == -1) {178if (*odst >= 240) /* Class E */179bits = 32;180else if (*odst >= 224) /* Class D */181bits = 4;182else if (*odst >= 192) /* Class C */183bits = 24;184else if (*odst >= 128) /* Class B */185bits = 16;186else /* Class A */187bits = 8;188/* If imputed mask is narrower than specified octets, widen. */189if (bits < ((dst - odst) * 8))190bits = (dst - odst) * 8;191}192/* Extend network to cover the actual mask. */193while (bits > ((dst - odst) * 8)) {194if (size-- == 0)195goto emsgsize;196*dst++ = '\0';197}198return (bits);199200enoent:201errno = ENOENT;202return (-1);203204emsgsize:205errno = EMSGSIZE;206return (-1);207}208209210static int211inet_net_pton_ipv6(const char *src, u_char *dst, size_t size)212{213struct in6_addr in6;214int ret;215int bits;216size_t bytes;217char buf[INET6_ADDRSTRLEN + sizeof("/128")];218char *sep;219const char *errstr;220221if (strlcpy(buf, src, sizeof buf) >= sizeof buf) {222errno = EMSGSIZE;223return (-1);224}225226sep = strchr(buf, '/');227if (sep != NULL)228*sep++ = '\0';229230ret = inet_pton(AF_INET6, buf, &in6);231if (ret != 1)232return (-1);233234if (sep == NULL)235bits = 128;236else {237bits = strtonum(sep, 0, 128, &errstr);238if (errstr) {239errno = EINVAL;240return (-1);241}242}243244bytes = (bits + 7) / 8;245if (bytes > size) {246errno = EMSGSIZE;247return (-1);248}249memcpy(dst, &in6.s6_addr, bytes);250return (bits);251}252253/*254* Weak aliases for applications that use certain private entry points,255* and fail to include <arpa/inet.h>.256*/257#undef inet_net_pton258__weak_reference(__inet_net_pton, inet_net_pton);259260261