Home | History | Annotate | Line # | Download | only in inet
      1 /*	$NetBSD: inet_cidr_ntop.c,v 1.9 2026/04/08 14:12:06 christos 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_ntop.c,v 1.7 2006/10/11 02:18:18 marka Exp";
     24 #else
     25 __RCSID("$NetBSD: inet_cidr_ntop.c,v 1.9 2026/04/08 14:12:06 christos 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 <assert.h>
     39 #include <errno.h>
     40 #include <stdio.h>
     41 #include <string.h>
     42 #include <stdlib.h>
     43 
     44 #include "port_after.h"
     45 
     46 #ifdef __weak_alias
     47 __weak_alias(inet_cidr_ntop,_inet_cidr_ntop)
     48 #endif
     49 
     50 static char *
     51 inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size);
     52 static char *
     53 inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size);
     54 
     55 /*%
     56  * char *
     57  * inet_cidr_ntop(af, src, bits, dst, size)
     58  *	convert network address from network to presentation format.
     59  *	"src"'s size is determined from its "af".
     60  * return:
     61  *	pointer to dst, or NULL if an error occurred (check errno).
     62  * note:
     63  *	192.5.5.1/28 has a nonzero host part, which means it isn't a network
     64  *	as called for by inet_net_ntop() but it can be a host address with
     65  *	an included netmask.
     66  * author:
     67  *	Paul Vixie (ISC), October 1998
     68  */
     69 char *
     70 inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size) {
     71 	switch (af) {
     72 	case AF_INET:
     73 		return (inet_cidr_ntop_ipv4(src, bits, dst, size));
     74 	case AF_INET6:
     75 		return (inet_cidr_ntop_ipv6(src, bits, dst, size));
     76 	default:
     77 		errno = EAFNOSUPPORT;
     78 		return (NULL);
     79 	}
     80 }
     81 
     82 static int
     83 decoct(const u_char *src, size_t bytes, char *dst, size_t size) {
     84 	size_t b;
     85 	int l, t = 0;
     86 
     87 	for (b = 1; b <= bytes; b++) {
     88 		ADDS(snprintf(dst + t, size - t, "%u", *src++));
     89 		if (b != bytes)
     90 			ADDC('.');
     91 	}
     92 	return t;
     93 emsgsize:
     94 	return l < 0 ? l : l + t;
     95 }
     96 
     97 /*%
     98  * static char *
     99  * inet_cidr_ntop_ipv4(src, bits, dst, size)
    100  *	convert IPv4 network address from network to presentation format.
    101  *	"src"'s size is determined from its "af".
    102  * return:
    103  *	pointer to dst, or NULL if an error occurred (check errno).
    104  * note:
    105  *	network byte order assumed.  this means 192.5.5.240/28 has
    106  *	0b11110000 in its fourth octet.
    107  * author:
    108  *	Paul Vixie (ISC), October 1998
    109  */
    110 static char *
    111 inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) {
    112 	size_t len = 4, b, bytes, t = 0;
    113 	int l;
    114 
    115 	if ((bits < -1) || (bits > 32)) {
    116 		errno = EINVAL;
    117 		return (NULL);
    118 	}
    119 
    120 	/* Find number of significant bytes in address. */
    121 	if (bits == -1)
    122 		len = 4;
    123 	else
    124 		for (len = 1, b = 1 ; b < 4U; b++)
    125 			if (*(src + b))
    126 				len = b + 1;
    127 
    128 	/* Format whole octets plus nonzero trailing octets. */
    129 	bytes = (((bits <= 0) ? 1 : bits) + 7) / 8;
    130 	if (len > bytes)
    131 		bytes = len;
    132 	ADDS(decoct(src, bytes, dst + t, size - t));
    133 
    134 	if (bits != -1) {
    135 		/* Format CIDR /width. */
    136 		ADDS(snprintf(dst + t, size - t, "/%u", bits));
    137 	}
    138 
    139 	return (dst);
    140 
    141  emsgsize:
    142 	errno = EMSGSIZE;
    143 	return (NULL);
    144 }
    145 
    146 static char *
    147 inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) {
    148 	/*
    149 	 * Note that int32_t and int16_t need only be "at least" large enough
    150 	 * to contain a value of the specified size.  On some systems, like
    151 	 * Crays, there is no such thing as an integer variable with 16 bits.
    152 	 * Keep this in mind if you think this function should have been coded
    153 	 * to use pointer overlays.  All the world's not a VAX.
    154 	 */
    155 	struct { int base, len; } best, cur;
    156 	u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
    157 	int i, l;
    158 	size_t t;
    159 
    160 	if ((bits < -1) || (bits > 128)) {
    161 		errno = EINVAL;
    162 		return (NULL);
    163 	}
    164 
    165 	/*
    166 	 * Preprocess:
    167 	 *	Copy the input (bytewise) array into a wordwise array.
    168 	 *	Find the longest run of 0x00's in src[] for :: shorthanding.
    169 	 */
    170 	memset(words, '\0', sizeof words);
    171 	for (i = 0; i < NS_IN6ADDRSZ; i++)
    172 		words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
    173 	best.base = -1;
    174 	best.len = 0;
    175 	cur.base = -1;
    176 	cur.len = 0;
    177 	for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
    178 		if (words[i] == 0) {
    179 			if (cur.base == -1)
    180 				cur.base = i, cur.len = 1;
    181 			else
    182 				cur.len++;
    183 		} else {
    184 			if (cur.base != -1) {
    185 				if (best.base == -1 || cur.len > best.len)
    186 					best = cur;
    187 				cur.base = -1;
    188 			}
    189 		}
    190 	}
    191 	if (cur.base != -1) {
    192 		if (best.base == -1 || cur.len > best.len)
    193 			best = cur;
    194 	}
    195 	if (best.base != -1 && best.len < 2)
    196 		best.base = -1;
    197 
    198 	/*
    199 	 * Format the result.
    200 	 */
    201 	t = 0;
    202 	for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
    203 		/* Are we inside the best run of 0x00's? */
    204 		if (best.base != -1 && i >= best.base &&
    205 		    i < (best.base + best.len)) {
    206 			if (i == best.base)
    207 				ADDC(':');
    208 			continue;
    209 		}
    210 		/* Are we following an initial run of 0x00s or any real hex? */
    211 		if (i != 0)
    212 			ADDC(':');
    213 		/* Is this address an encapsulated IPv4? */
    214 		if (i == 6 && best.base == 0 && (best.len == 6 ||
    215 		    (best.len == 7 && words[7] != 0x0001) ||
    216 		    (best.len == 5 && words[5] == 0xffff))) {
    217 			size_t n;
    218 
    219 			if (src[15] || bits == -1 || bits > 120)
    220 				n = 4;
    221 			else if (src[14] || bits > 112)
    222 				n = 3;
    223 			else
    224 				n = 2;
    225 			ADDS(decoct(src+12, n, dst + t, size - t));
    226 			break;
    227 		}
    228 		ADDS(snprintf(dst + t, size - t, "%x", words[i]));
    229 	}
    230 
    231 	/* Was it a trailing run of 0x00's? */
    232 	if (best.base != -1 && (best.base + best.len) ==
    233 	    (NS_IN6ADDRSZ / NS_INT16SZ)) {
    234 		ADDC(':');
    235 	}
    236 
    237 	if (bits != -1)
    238 		ADDS(snprintf(dst + t, size - t, "/%u", bits));
    239 
    240 	return dst;
    241 emsgsize:
    242 	errno = EMSGSIZE;
    243 	return (NULL);
    244 }
    245 
    246 /*! \file */
    247