xref: /netbsd-src/lib/libc/inet/inet_net_ntop.c (revision 9c26aa2ea46d8224d85bdd83129f785ba95cbdd4)
13fa54233Schristos /*
23fa54233Schristos  * Copyright (c) 1996,1999 by Internet Software Consortium.
33fa54233Schristos  *
43fa54233Schristos  * Permission to use, copy, modify, and distribute this software for any
53fa54233Schristos  * purpose with or without fee is hereby granted, provided that the above
63fa54233Schristos  * copyright notice and this permission notice appear in all copies.
73fa54233Schristos  *
83fa54233Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
93fa54233Schristos  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
103fa54233Schristos  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
113fa54233Schristos  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
123fa54233Schristos  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
133fa54233Schristos  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
143fa54233Schristos  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
153fa54233Schristos  * SOFTWARE.
163fa54233Schristos  */
173fa54233Schristos 
183fa54233Schristos #include <sys/cdefs.h>
193fa54233Schristos #if defined(LIBC_SCCS) && !defined(lint)
203fa54233Schristos #ifdef notdef
213fa54233Schristos static const char rcsid[] = "Id: inet_net_ntop.c,v 1.1.2.1 2002/08/02 02:17:21 marka Exp ";
223fa54233Schristos #else
23*9c26aa2eSmaya __RCSID("$NetBSD: inet_net_ntop.c,v 1.4 2017/05/09 02:56:44 maya Exp $");
243fa54233Schristos #endif
253fa54233Schristos #endif
263fa54233Schristos 
273fa54233Schristos #include "port_before.h"
283fa54233Schristos 
293fa54233Schristos #include "namespace.h"
303fa54233Schristos #include <sys/types.h>
313fa54233Schristos #include <sys/socket.h>
323fa54233Schristos #include <netinet/in.h>
333fa54233Schristos #include <arpa/inet.h>
343fa54233Schristos 
353fa54233Schristos #include <errno.h>
363fa54233Schristos #include <stdio.h>
373fa54233Schristos #include <string.h>
383fa54233Schristos #include <stdlib.h>
393fa54233Schristos 
403fa54233Schristos #include "port_after.h"
413fa54233Schristos 
423fa54233Schristos #ifdef __weak_alias
433fa54233Schristos __weak_alias(inet_net_ntop,_inet_net_ntop)
443fa54233Schristos #endif
453fa54233Schristos 
463fa54233Schristos #ifdef SPRINTF_CHAR
473fa54233Schristos # define SPRINTF(x) strlen(sprintf/**/x)
483fa54233Schristos #else
493fa54233Schristos # define SPRINTF(x) sprintf x
503fa54233Schristos #endif
513fa54233Schristos 
521f043722Smatt static char *	inet_net_ntop_ipv4(const u_char *src, int bits,
531f043722Smatt 					char *dst, size_t size);
541f043722Smatt static char *	inet_net_ntop_ipv6(const u_char *src, int bits,
551f043722Smatt 					char *dst, size_t size);
563fa54233Schristos 
573fa54233Schristos /*
583fa54233Schristos  * char *
593fa54233Schristos  * inet_net_ntop(af, src, bits, dst, size)
603fa54233Schristos  *	convert network number from network to presentation format.
613fa54233Schristos  *	generates CIDR style result always.
623fa54233Schristos  * return:
633fa54233Schristos  *	pointer to dst, or NULL if an error occurred (check errno).
643fa54233Schristos  * author:
653fa54233Schristos  *	Paul Vixie (ISC), July 1996
663fa54233Schristos  */
673fa54233Schristos char *
inet_net_ntop(int af,const void * src,int bits,char * dst,size_t size)681f043722Smatt inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size)
693fa54233Schristos {
703fa54233Schristos 	switch (af) {
713fa54233Schristos 	case AF_INET:
723fa54233Schristos 		return (inet_net_ntop_ipv4(src, bits, dst, size));
733fa54233Schristos 	case AF_INET6:
743fa54233Schristos 		return (inet_net_ntop_ipv6(src, bits, dst, size));
753fa54233Schristos 	default:
763fa54233Schristos 		errno = EAFNOSUPPORT;
773fa54233Schristos 		return (NULL);
783fa54233Schristos 	}
793fa54233Schristos }
803fa54233Schristos 
813fa54233Schristos /*
823fa54233Schristos  * static char *
833fa54233Schristos  * inet_net_ntop_ipv4(src, bits, dst, size)
843fa54233Schristos  *	convert IPv4 network number from network to presentation format.
853fa54233Schristos  *	generates CIDR style result always.
863fa54233Schristos  * return:
873fa54233Schristos  *	pointer to dst, or NULL if an error occurred (check errno).
883fa54233Schristos  * note:
893fa54233Schristos  *	network byte order assumed.  this means 192.5.5.240/28 has
903fa54233Schristos  *	0b11110000 in its fourth octet.
913fa54233Schristos  * author:
923fa54233Schristos  *	Paul Vixie (ISC), July 1996
933fa54233Schristos  */
943fa54233Schristos static char *
inet_net_ntop_ipv4(const u_char * src,int bits,char * dst,size_t size)951f043722Smatt inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size)
963fa54233Schristos {
973fa54233Schristos 	char *odst = dst;
983fa54233Schristos 	char *t;
993fa54233Schristos 	u_int m;
1003fa54233Schristos 	int b;
1013fa54233Schristos 
1023fa54233Schristos 	if (bits < 0 || bits > 32) {
1033fa54233Schristos 		errno = EINVAL;
1043fa54233Schristos 		return (NULL);
1053fa54233Schristos 	}
1063fa54233Schristos 
1073fa54233Schristos 	if (bits == 0) {
1083fa54233Schristos 		if (size < sizeof "0")
1093fa54233Schristos 			goto emsgsize;
1103fa54233Schristos 		*dst++ = '0';
1113fa54233Schristos 		size--;
1123fa54233Schristos 		*dst = '\0';
1133fa54233Schristos 	}
1143fa54233Schristos 
1153fa54233Schristos 	/* Format whole octets. */
1163fa54233Schristos 	for (b = bits / 8; b > 0; b--) {
1173fa54233Schristos 		if (size <= sizeof "255.")
1183fa54233Schristos 			goto emsgsize;
1193fa54233Schristos 		t = dst;
1203fa54233Schristos 		dst += SPRINTF((dst, "%u", *src++));
1213fa54233Schristos 		if (b > 1) {
1223fa54233Schristos 			*dst++ = '.';
1233fa54233Schristos 			*dst = '\0';
1243fa54233Schristos 		}
1253fa54233Schristos 		size -= (size_t)(dst - t);
1263fa54233Schristos 	}
1273fa54233Schristos 
1283fa54233Schristos 	/* Format partial octet. */
1293fa54233Schristos 	b = bits % 8;
1303fa54233Schristos 	if (b > 0) {
1313fa54233Schristos 		if (size <= sizeof ".255")
1323fa54233Schristos 			goto emsgsize;
1333fa54233Schristos 		t = dst;
1343fa54233Schristos 		if (dst != odst)
1353fa54233Schristos 			*dst++ = '.';
1363fa54233Schristos 		m = ((1 << b) - 1) << (8 - b);
1373fa54233Schristos 		dst += SPRINTF((dst, "%u", *src & m));
1383fa54233Schristos 		size -= (size_t)(dst - t);
1393fa54233Schristos 	}
1403fa54233Schristos 
1413fa54233Schristos 	/* Format CIDR /width. */
1423fa54233Schristos 	if (size <= sizeof "/32")
1433fa54233Schristos 		goto emsgsize;
1443fa54233Schristos 	dst += SPRINTF((dst, "/%u", bits));
1453fa54233Schristos 	return (odst);
1463fa54233Schristos 
1473fa54233Schristos  emsgsize:
1483fa54233Schristos 	errno = EMSGSIZE;
1493fa54233Schristos 	return (NULL);
1503fa54233Schristos }
1513fa54233Schristos 
1523fa54233Schristos /*
1533fa54233Schristos  * static char *
1543fa54233Schristos  * inet_net_ntop_ipv6(src, bits, fakebits, dst, size)
1553fa54233Schristos  *	convert IPv6 network number from network to presentation format.
1563fa54233Schristos  *	generates CIDR style result always. Picks the shortest representation
1573fa54233Schristos  *	unless the IP is really IPv4.
1583fa54233Schristos  *	always prints specified number of bits (bits).
1593fa54233Schristos  * return:
1603fa54233Schristos  *	pointer to dst, or NULL if an error occurred (check errno).
1613fa54233Schristos  * note:
1623fa54233Schristos  *	network byte order assumed.  this means 192.5.5.240/28 has
1633fa54233Schristos  *	0x11110000 in its fourth octet.
1643fa54233Schristos  * author:
1653fa54233Schristos  *	Vadim Kogan (UCB), June 2001
1663fa54233Schristos  *  Original version (IPv4) by Paul Vixie (ISC), July 1996
1673fa54233Schristos  */
1683fa54233Schristos 
1693fa54233Schristos static char *
inet_net_ntop_ipv6(const u_char * src,int bits,char * dst,size_t size)1701f043722Smatt inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
1711f043722Smatt {
1723fa54233Schristos 	u_int	m;
1733fa54233Schristos 	int	b;
1743fa54233Schristos 	size_t	p;
17553e9163aSlukem 	size_t	zero_s, zero_l, tmp_zero_s, tmp_zero_l;
17653e9163aSlukem 	size_t	i;
1773fa54233Schristos 	int	is_ipv4 = 0;
1783fa54233Schristos 	unsigned char inbuf[16];
1793fa54233Schristos 	char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
1803fa54233Schristos 	char	*cp;
18153e9163aSlukem 	size_t	words;
1823fa54233Schristos 	u_char	*s;
1833fa54233Schristos 
1843fa54233Schristos 	if (bits < 0 || bits > 128) {
1853fa54233Schristos 		errno = EINVAL;
1863fa54233Schristos 		return (NULL);
1873fa54233Schristos 	}
1883fa54233Schristos 
1893fa54233Schristos 	cp = outbuf;
1903fa54233Schristos 
1913fa54233Schristos 	if (bits == 0) {
1923fa54233Schristos 		*cp++ = ':';
1933fa54233Schristos 		*cp++ = ':';
1943fa54233Schristos 		*cp = '\0';
1953fa54233Schristos 	} else {
1963fa54233Schristos 		/* Copy src to private buffer.  Zero host part. */
1973fa54233Schristos 		p = (bits + 7) / 8;
1983fa54233Schristos 		memcpy(inbuf, src, p);
1993fa54233Schristos 		memset(inbuf + p, 0, 16 - p);
2003fa54233Schristos 		b = bits % 8;
2013fa54233Schristos 		if (b != 0) {
202*9c26aa2eSmaya 			m = ~0u << (8 - b);
2033fa54233Schristos 			inbuf[p-1] &= m;
2043fa54233Schristos 		}
2053fa54233Schristos 
2063fa54233Schristos 		s = inbuf;
2073fa54233Schristos 
2083fa54233Schristos 		/* how many words need to be displayed in output */
2093fa54233Schristos 		words = (bits + 15) / 16;
2103fa54233Schristos 		if (words == 1)
2113fa54233Schristos 			words = 2;
2123fa54233Schristos 
2133fa54233Schristos 		/* Find the longest substring of zero's */
2143fa54233Schristos 		zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0;
2153fa54233Schristos 		for (i = 0; i < (words * 2); i += 2) {
2163fa54233Schristos 			if ((s[i] | s[i+1]) == 0) {
2173fa54233Schristos 				if (tmp_zero_l == 0)
2183fa54233Schristos 					tmp_zero_s = i / 2;
2193fa54233Schristos 				tmp_zero_l++;
2203fa54233Schristos 			} else {
2213fa54233Schristos 				if (tmp_zero_l && zero_l < tmp_zero_l) {
2223fa54233Schristos 					zero_s = tmp_zero_s;
2233fa54233Schristos 					zero_l = tmp_zero_l;
2243fa54233Schristos 					tmp_zero_l = 0;
2253fa54233Schristos 				}
2263fa54233Schristos 			}
2273fa54233Schristos 		}
2283fa54233Schristos 
2293fa54233Schristos 		if (tmp_zero_l && zero_l < tmp_zero_l) {
2303fa54233Schristos 			zero_s = tmp_zero_s;
2313fa54233Schristos 			zero_l = tmp_zero_l;
2323fa54233Schristos 		}
2333fa54233Schristos 
2343fa54233Schristos 		if (zero_l != words && zero_s == 0 && ((zero_l == 6) ||
2353fa54233Schristos 		    ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) ||
2363fa54233Schristos 		    ((zero_l == 7 && s[14] != 0 && s[15] != 1)))))
2373fa54233Schristos 			is_ipv4 = 1;
2383fa54233Schristos 
2393fa54233Schristos 		/* Format whole words. */
2403fa54233Schristos 		for (p = 0; p < words; p++) {
2413fa54233Schristos 			if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) {
2423fa54233Schristos 				/* Time to skip some zeros */
2433fa54233Schristos 				if (p == zero_s)
2443fa54233Schristos 					*cp++ = ':';
2453fa54233Schristos 				if (p == words - 1)
2463fa54233Schristos 					*cp++ = ':';
2473fa54233Schristos 				s++;
2483fa54233Schristos 				s++;
2493fa54233Schristos 				continue;
2503fa54233Schristos 			}
2513fa54233Schristos 
2523fa54233Schristos 			if (is_ipv4 && p > 5 ) {
2533fa54233Schristos 				*cp++ = (p == 6) ? ':' : '.';
2543fa54233Schristos 				cp += SPRINTF((cp, "%u", *s++));
2553fa54233Schristos 				/* we can potentially drop the last octet */
2563fa54233Schristos 				if (p != 7 || bits > 120) {
2573fa54233Schristos 					*cp++ = '.';
2583fa54233Schristos 					cp += SPRINTF((cp, "%u", *s++));
2593fa54233Schristos 				}
2603fa54233Schristos 			} else {
2613fa54233Schristos 				if (cp != outbuf)
2623fa54233Schristos 					*cp++ = ':';
2633fa54233Schristos 				cp += SPRINTF((cp, "%x", *s * 256 + s[1]));
2643fa54233Schristos 				s += 2;
2653fa54233Schristos 			}
2663fa54233Schristos 		}
2673fa54233Schristos 	}
2683fa54233Schristos 	/* Format CIDR /width. */
2693fa54233Schristos 	(void)SPRINTF((cp, "/%u", bits));
2703fa54233Schristos 	if (strlen(outbuf) + 1 > size)
2713fa54233Schristos 		goto emsgsize;
2723fa54233Schristos 	strcpy(dst, outbuf);
2733fa54233Schristos 
2743fa54233Schristos 	return (dst);
2753fa54233Schristos 
2763fa54233Schristos emsgsize:
2773fa54233Schristos 	errno = EMSGSIZE;
2783fa54233Schristos 	return (NULL);
2793fa54233Schristos }
280