inet_net_ntop.c revision 156953
1169691Skan/*
297403Sobrien * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
397403Sobrien * Copyright (c) 1996,1999 by Internet Software Consortium.
497403Sobrien *
597403Sobrien * Permission to use, copy, modify, and distribute this software for any
697403Sobrien * purpose with or without fee is hereby granted, provided that the above
797403Sobrien * copyright notice and this permission notice appear in all copies.
897403Sobrien *
997403Sobrien * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
1097403Sobrien * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1197403Sobrien * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
1297403Sobrien * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1397403Sobrien * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1497403Sobrien * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
1597403Sobrien * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1697403Sobrien */
17169691Skan
1897403Sobrien#if defined(LIBC_SCCS) && !defined(lint)
1997403Sobrienstatic const char rcsid[] = "$Id: inet_net_ntop.c,v 1.1.2.1.8.1 2004/03/09 08:33:32 marka Exp $";
2097403Sobrien#endif
2197403Sobrien
2297403Sobrien#include "port_before.h"
2397403Sobrien
2497403Sobrien#include <sys/types.h>
2597403Sobrien#include <sys/socket.h>
2697403Sobrien#include <netinet/in.h>
2797403Sobrien#include <arpa/inet.h>
2897403Sobrien
2997403Sobrien#include <errno.h>
3097403Sobrien#include <stdio.h>
3197403Sobrien#include <string.h>
3297403Sobrien#include <stdlib.h>
33169691Skan
34169691Skan#include "port_after.h"
35107606Sobrien
36107606Sobrien#ifdef SPRINTF_CHAR
37132720Skan# define SPRINTF(x) strlen(sprintf/**/x)
3897403Sobrien#else
39132720Skan# define SPRINTF(x) ((size_t)sprintf x)
40132720Skan#endif
41132720Skan
42132720Skanstatic char *	inet_net_ntop_ipv4 __P((const u_char *src, int bits,
43132720Skan					char *dst, size_t size));
44132720Skanstatic char *	inet_net_ntop_ipv6 __P((const u_char *src, int bits,
45132720Skan					char *dst, size_t size));
46132720Skan
47132720Skan/*
48132720Skan * char *
49132720Skan * inet_net_ntop(af, src, bits, dst, size)
50132720Skan *	convert network number from network to presentation format.
51132720Skan *	generates CIDR style result always.
52132720Skan * return:
53132720Skan *	pointer to dst, or NULL if an error occurred (check errno).
54132720Skan * author:
55132720Skan *	Paul Vixie (ISC), July 1996
56132720Skan */
57132720Skanchar *
58132720Skaninet_net_ntop(af, src, bits, dst, size)
59132720Skan	int af;
60132720Skan	const void *src;
61132720Skan	int bits;
62169691Skan	char *dst;
63169691Skan	size_t size;
64132720Skan{
65132720Skan	switch (af) {
66132720Skan	case AF_INET:
67169691Skan		return (inet_net_ntop_ipv4(src, bits, dst, size));
68132720Skan	case AF_INET6:
69169691Skan		return (inet_net_ntop_ipv6(src, bits, dst, size));
70132720Skan	default:
71132720Skan		errno = EAFNOSUPPORT;
72132720Skan		return (NULL);
73132720Skan	}
74169691Skan}
75132720Skan
76132720Skan/*
77132720Skan * static char *
78132720Skan * inet_net_ntop_ipv4(src, bits, dst, size)
79132720Skan *	convert IPv4 network number from network to presentation format.
80132720Skan *	generates CIDR style result always.
81132720Skan * return:
82132720Skan *	pointer to dst, or NULL if an error occurred (check errno).
83132720Skan * note:
84132720Skan *	network byte order assumed.  this means 192.5.5.240/28 has
85132720Skan *	0b11110000 in its fourth octet.
86132720Skan * author:
87132720Skan *	Paul Vixie (ISC), July 1996
88169691Skan */
89132720Skanstatic char *
90132720Skaninet_net_ntop_ipv4(src, bits, dst, size)
91132720Skan	const u_char *src;
92132720Skan	int bits;
93132720Skan	char *dst;
94132720Skan	size_t size;
95132720Skan{
96132720Skan	char *odst = dst;
97169691Skan	char *t;
98132720Skan	u_int m;
99132720Skan	int b;
100132720Skan
101132720Skan	if (bits < 0 || bits > 32) {
102169691Skan		errno = EINVAL;
103132720Skan		return (NULL);
104132720Skan	}
105132720Skan
106132720Skan	if (bits == 0) {
107132720Skan		if (size < sizeof "0")
108132720Skan			goto emsgsize;
109169691Skan		*dst++ = '0';
110132720Skan		size--;
111132720Skan		*dst = '\0';
112132720Skan	}
113169691Skan
114132720Skan	/* Format whole octets. */
115132720Skan	for (b = bits / 8; b > 0; b--) {
116132720Skan		if (size <= sizeof "255.")
117169691Skan			goto emsgsize;
118132720Skan		t = dst;
119132720Skan		dst += SPRINTF((dst, "%u", *src++));
120132720Skan		if (b > 1) {
121132720Skan			*dst++ = '.';
122169691Skan			*dst = '\0';
123132720Skan		}
124132720Skan		size -= (size_t)(dst - t);
125132720Skan	}
126132720Skan
127132720Skan	/* Format partial octet. */
128132720Skan	b = bits % 8;
129132720Skan	if (b > 0) {
130132720Skan		if (size <= sizeof ".255")
131132720Skan			goto emsgsize;
132132720Skan		t = dst;
133132720Skan		if (dst != odst)
134132720Skan			*dst++ = '.';
135169691Skan		m = ((1 << b) - 1) << (8 - b);
136132720Skan		dst += SPRINTF((dst, "%u", *src & m));
137132720Skan		size -= (size_t)(dst - t);
138169691Skan	}
139132720Skan
140132720Skan	/* Format CIDR /width. */
141132720Skan	if (size <= sizeof "/32")
142132720Skan		goto emsgsize;
143132720Skan	dst += SPRINTF((dst, "/%u", bits));
144132720Skan	return (odst);
145107606Sobrien
146132720Skan emsgsize:
147132720Skan	errno = EMSGSIZE;
148132720Skan	return (NULL);
149132720Skan}
150132720Skan
151132720Skan/*
152132720Skan * static char *
153132720Skan * inet_net_ntop_ipv6(src, bits, fakebits, dst, size)
15497403Sobrien *	convert IPv6 network number from network to presentation format.
15597403Sobrien *	generates CIDR style result always. Picks the shortest representation
156132720Skan *	unless the IP is really IPv4.
157132720Skan *	always prints specified number of bits (bits).
158132720Skan * return:
159132720Skan *	pointer to dst, or NULL if an error occurred (check errno).
160132720Skan * note:
161132720Skan *	network byte order assumed.  this means 192.5.5.240/28 has
162132720Skan *	0x11110000 in its fourth octet.
16397403Sobrien * author:
164132720Skan *	Vadim Kogan (UCB), June 2001
165132720Skan *  Original version (IPv4) by Paul Vixie (ISC), July 1996
166132720Skan */
167132720Skan
168132720Skanstatic char *
169132720Skaninet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) {
17097403Sobrien	u_int	m;
171132720Skan	int	b;
17297403Sobrien	int	p;
17397403Sobrien	int	zero_s, zero_l, tmp_zero_s, tmp_zero_l;
17497403Sobrien	int	i;
17597403Sobrien	int	is_ipv4 = 0;
17697403Sobrien	unsigned char inbuf[16];
17797403Sobrien	char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
178169691Skan	char	*cp;
179132720Skan	int	words;
180169691Skan	u_char	*s;
18197403Sobrien
182132720Skan	if (bits < 0 || bits > 128) {
183132720Skan		errno = EINVAL;
18497403Sobrien		return (NULL);
18597403Sobrien	}
18697403Sobrien
18797403Sobrien	cp = outbuf;
188117397Skan
189132720Skan	if (bits == 0) {
190132720Skan		*cp++ = ':';
19197403Sobrien		*cp++ = ':';
192132720Skan		*cp = '\0';
193132720Skan	} else {
194132720Skan		/* Copy src to private buffer.  Zero host part. */
195132720Skan		p = (bits + 7) / 8;
196132720Skan		memcpy(inbuf, src, p);
197132720Skan		memset(inbuf + p, 0, 16 - p);
19897403Sobrien		b = bits % 8;
199169691Skan		if (b != 0) {
200132720Skan			m = ~0 << (8 - b);
201169691Skan			inbuf[p-1] &= m;
202107606Sobrien		}
203169691Skan
204169691Skan		s = inbuf;
205107606Sobrien
206132720Skan		/* how many words need to be displayed in output */
207107606Sobrien		words = (bits + 15) / 16;
208146897Skan		if (words == 1)
209132720Skan			words = 2;
210132720Skan
211146897Skan		/* Find the longest substring of zero's */
212146897Skan		zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0;
213132720Skan		for (i = 0; i < (words * 2); i += 2) {
214132720Skan			if ((s[i] | s[i+1]) == 0) {
215169691Skan				if (tmp_zero_l == 0)
216169691Skan					tmp_zero_s = i / 2;
217169691Skan				tmp_zero_l++;
218132720Skan			} else {
219107606Sobrien				if (tmp_zero_l && zero_l < tmp_zero_l) {
220132720Skan					zero_s = tmp_zero_s;
221132720Skan					zero_l = tmp_zero_l;
222132720Skan					tmp_zero_l = 0;
223132720Skan				}
224132720Skan			}
225132720Skan		}
226132720Skan
227132720Skan		if (tmp_zero_l && zero_l < tmp_zero_l) {
228132720Skan			zero_s = tmp_zero_s;
229132720Skan			zero_l = tmp_zero_l;
230132720Skan		}
231132720Skan
232132720Skan		if (zero_l != words && zero_s == 0 && ((zero_l == 6) ||
233132720Skan		    ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) ||
234132720Skan		    ((zero_l == 7 && s[14] != 0 && s[15] != 1)))))
235132720Skan			is_ipv4 = 1;
23697403Sobrien
237132720Skan		/* Format whole words. */
238132720Skan		for (p = 0; p < words; p++) {
239132720Skan			if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) {
240132720Skan				/* Time to skip some zeros */
241132720Skan				if (p == zero_s)
242132720Skan					*cp++ = ':';
243132720Skan				if (p == words - 1)
244132720Skan					*cp++ = ':';
245132720Skan				s++;
246132720Skan				s++;
247132720Skan				continue;
248132720Skan			}
249132720Skan
250132720Skan			if (is_ipv4 && p > 5 ) {
251132720Skan				*cp++ = (p == 6) ? ':' : '.';
25297403Sobrien				cp += SPRINTF((cp, "%u", *s++));
253132720Skan				/* we can potentially drop the last octet */
254132720Skan				if (p != 7 || bits > 120) {
255132720Skan					*cp++ = '.';
256132720Skan					cp += SPRINTF((cp, "%u", *s++));
257132720Skan				}
258132720Skan			} else {
259132720Skan				if (cp != outbuf)
260132720Skan					*cp++ = ':';
26197403Sobrien				cp += SPRINTF((cp, "%x", *s * 256 + s[1]));
26297403Sobrien				s += 2;
26397403Sobrien			}
26497403Sobrien		}
26597403Sobrien	}
26697403Sobrien	/* Format CIDR /width. */
267169691Skan	SPRINTF((cp, "/%u", bits));
268169691Skan	if (strlen(outbuf) + 1 > size)
269169691Skan		goto emsgsize;
27097403Sobrien	strcpy(dst, outbuf);
27197403Sobrien
27297403Sobrien	return (dst);
27397403Sobrien
27497403Sobrienemsgsize:
27597403Sobrien	errno = EMSGSIZE;
276169691Skan	return (NULL);
277107606Sobrien}
278169691Skan