Home | History | Annotate | Line # | Download | only in inet
      1 /*
      2  * Copyright (c) 1996,1999 by Internet Software Consortium.
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
      9  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
     10  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
     11  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
     13  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
     14  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     15  * SOFTWARE.
     16  */
     17 
     18 #include <sys/cdefs.h>
     19 #if defined(LIBC_SCCS) && !defined(lint)
     20 #ifdef notdef
     21 static const char rcsid[] = "Id: inet_net_ntop.c,v 1.1.2.1 2002/08/02 02:17:21 marka Exp ";
     22 #else
     23 __RCSID("$NetBSD: inet_net_ntop.c,v 1.5 2026/04/08 14:12:06 christos Exp $");
     24 #endif
     25 #endif
     26 
     27 #include "port_before.h"
     28 
     29 #include "namespace.h"
     30 #include <sys/types.h>
     31 #include <sys/socket.h>
     32 #include <netinet/in.h>
     33 #include <arpa/inet.h>
     34 
     35 #include <errno.h>
     36 #include <stdio.h>
     37 #include <string.h>
     38 #include <stdlib.h>
     39 
     40 #include "port_after.h"
     41 
     42 #ifdef __weak_alias
     43 __weak_alias(inet_net_ntop,_inet_net_ntop)
     44 #endif
     45 
     46 static char *	inet_net_ntop_ipv4(const u_char *src, int bits,
     47 					char *dst, size_t size);
     48 static char *	inet_net_ntop_ipv6(const u_char *src, int bits,
     49 					char *dst, size_t size);
     50 
     51 /*
     52  * char *
     53  * inet_net_ntop(af, src, bits, dst, size)
     54  *	convert network number from network to presentation format.
     55  *	generates CIDR style result always.
     56  * return:
     57  *	pointer to dst, or NULL if an error occurred (check errno).
     58  * author:
     59  *	Paul Vixie (ISC), July 1996
     60  */
     61 char *
     62 inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size)
     63 {
     64 	switch (af) {
     65 	case AF_INET:
     66 		return (inet_net_ntop_ipv4(src, bits, dst, size));
     67 	case AF_INET6:
     68 		return (inet_net_ntop_ipv6(src, bits, dst, size));
     69 	default:
     70 		errno = EAFNOSUPPORT;
     71 		return (NULL);
     72 	}
     73 }
     74 
     75 /*
     76  * static char *
     77  * inet_net_ntop_ipv4(src, bits, dst, size)
     78  *	convert IPv4 network number from network to presentation format.
     79  *	generates CIDR style result always.
     80  * return:
     81  *	pointer to dst, or NULL if an error occurred (check errno).
     82  * note:
     83  *	network byte order assumed.  this means 192.5.5.240/28 has
     84  *	0b11110000 in its fourth octet.
     85  * author:
     86  *	Paul Vixie (ISC), July 1996
     87  */
     88 static char *
     89 inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size)
     90 {
     91 	size_t t;
     92 	u_int m;
     93 	int b, l;
     94 
     95 	if (bits < 0 || bits > 32) {
     96 		errno = EINVAL;
     97 		return (NULL);
     98 	}
     99 
    100 
    101 	t = 0;
    102 	if (bits == 0)
    103 		ADDC('0');
    104 
    105 	/* Format whole octets. */
    106 	t = 0;
    107 	for (b = bits / 8; b > 0; b--) {
    108 		ADDS(snprintf(dst + t, size - t, "%u", *src++));
    109 		if (b > 1)
    110 			ADDC('.');
    111 	}
    112 
    113 	/* Format partial octet. */
    114 	b = bits % 8;
    115 	if (b > 0) {
    116 		if (t)
    117 			ADDC('.');
    118 		m = ((1 << b) - 1) << (8 - b);
    119 		ADDS(snprintf(dst + t, size - t, "%u", *src & m));
    120 	}
    121 
    122 	/* Format CIDR /width. */
    123 	ADDS(snprintf(dst + t, size - t, "/%u", bits));
    124 	return dst;
    125 
    126  emsgsize:
    127 	errno = EMSGSIZE;
    128 	return (NULL);
    129 }
    130 
    131 /*
    132  * static char *
    133  * inet_net_ntop_ipv6(src, bits, fakebits, dst, size)
    134  *	convert IPv6 network number from network to presentation format.
    135  *	generates CIDR style result always. Picks the shortest representation
    136  *	unless the IP is really IPv4.
    137  *	always prints specified number of bits (bits).
    138  * return:
    139  *	pointer to dst, or NULL if an error occurred (check errno).
    140  * note:
    141  *	network byte order assumed.  this means 192.5.5.240/28 has
    142  *	0x11110000 in its fourth octet.
    143  * author:
    144  *	Vadim Kogan (UCB), June 2001
    145  *  Original version (IPv4) by Paul Vixie (ISC), July 1996
    146  */
    147 
    148 static char *
    149 inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
    150 {
    151 	u_int	m;
    152 	int	b, t, l;
    153 	size_t	p;
    154 	size_t	zero_s, zero_l, tmp_zero_s, tmp_zero_l;
    155 	size_t	i;
    156 	int	is_ipv4 = 0;
    157 	unsigned char inbuf[16];
    158 	size_t	words;
    159 	u_char	*s;
    160 
    161 	if (bits < 0 || bits > 128) {
    162 		errno = EINVAL;
    163 		return (NULL);
    164 	}
    165 
    166 	t = 0;
    167 	if (bits == 0) {
    168 		ADDC(':');
    169 		ADDC(':');
    170 	} else {
    171 		/* Copy src to private buffer.  Zero host part. */
    172 		p = (bits + 7) / 8;
    173 		memcpy(inbuf, src, p);
    174 		memset(inbuf + p, 0, 16 - p);
    175 		b = bits % 8;
    176 		if (b != 0) {
    177 			m = ~0u << (8 - b);
    178 			inbuf[p-1] &= m;
    179 		}
    180 
    181 		s = inbuf;
    182 
    183 		/* how many words need to be displayed in output */
    184 		words = (bits + 15) / 16;
    185 		if (words == 1)
    186 			words = 2;
    187 
    188 		/* Find the longest substring of zero's */
    189 		zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0;
    190 		for (i = 0; i < (words * 2); i += 2) {
    191 			if ((s[i] | s[i+1]) == 0) {
    192 				if (tmp_zero_l == 0)
    193 					tmp_zero_s = i / 2;
    194 				tmp_zero_l++;
    195 			} else {
    196 				if (tmp_zero_l && zero_l < tmp_zero_l) {
    197 					zero_s = tmp_zero_s;
    198 					zero_l = tmp_zero_l;
    199 					tmp_zero_l = 0;
    200 				}
    201 			}
    202 		}
    203 
    204 		if (tmp_zero_l && zero_l < tmp_zero_l) {
    205 			zero_s = tmp_zero_s;
    206 			zero_l = tmp_zero_l;
    207 		}
    208 
    209 		if (zero_l != words && zero_s == 0 && ((zero_l == 6) ||
    210 		    ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) ||
    211 		    ((zero_l == 7 && s[14] != 0 && s[15] != 1)))))
    212 			is_ipv4 = 1;
    213 
    214 		/* Format whole words. */
    215 		for (p = 0; p < words; p++) {
    216 			if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) {
    217 				/* Time to skip some zeros */
    218 				if (p == zero_s)
    219 					ADDC(':');
    220 				if (p == words - 1)
    221 					ADDC(':');
    222 				s++;
    223 				s++;
    224 				continue;
    225 			}
    226 
    227 			if (is_ipv4 && p > 5 ) {
    228 				ADDS(snprintf(dst + t, size - t, "%c%u",
    229 				    (p == 6) ? ':' : '.', *s++));
    230 				/* we can potentially drop the last octet */
    231 				if (p != 7 || bits > 120) {
    232 					ADDS(snprintf(dst + t, size - t, ".%u",
    233 					    *s++));
    234 				}
    235 			} else {
    236 				if (t)
    237 					ADDC(':');
    238 				ADDS(snprintf(dst + t, size - t, "%x",
    239 				    *s * 256 + s[1]));
    240 				s += 2;
    241 			}
    242 		}
    243 	}
    244 	/* Format CIDR /width. */
    245 	ADDS(snprintf(dst + t, size - t, "/%u", bits));
    246 	return (dst);
    247 
    248 emsgsize:
    249 	errno = EMSGSIZE;
    250 	return (NULL);
    251 }
    252