xref: /netbsd-src/lib/libutil/sockaddr_snprintf.c (revision 5d7e998696ff94d2b72a059628d4f6dfafb235b2)
1*5d7e9986Schristos /*	$NetBSD: sockaddr_snprintf.c,v 1.14 2016/12/29 18:30:55 christos Exp $	*/
2eb648470Schristos 
3eb648470Schristos /*-
4*5d7e9986Schristos  * Copyright (c) 2004, 2016 The NetBSD Foundation, Inc.
5eb648470Schristos  * All rights reserved.
6eb648470Schristos  *
7eb648470Schristos  * This code is derived from software contributed to The NetBSD Foundation
8eb648470Schristos  * by Christos Zoulas.
9eb648470Schristos  *
10eb648470Schristos  * Redistribution and use in source and binary forms, with or without
11eb648470Schristos  * modification, are permitted provided that the following conditions
12eb648470Schristos  * are met:
13eb648470Schristos  * 1. Redistributions of source code must retain the above copyright
14eb648470Schristos  *    notice, this list of conditions and the following disclaimer.
15eb648470Schristos  * 2. Redistributions in binary form must reproduce the above copyright
16eb648470Schristos  *    notice, this list of conditions and the following disclaimer in the
17eb648470Schristos  *    documentation and/or other materials provided with the distribution.
18eb648470Schristos  *
19eb648470Schristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20eb648470Schristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21eb648470Schristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22eb648470Schristos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23eb648470Schristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24eb648470Schristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25eb648470Schristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26eb648470Schristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27eb648470Schristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28eb648470Schristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29eb648470Schristos  * POSSIBILITY OF SUCH DAMAGE.
30eb648470Schristos  */
31*5d7e9986Schristos #ifdef HAVE_CONFIG_H
32*5d7e9986Schristos #include "config.h"
33*5d7e9986Schristos #endif
34*5d7e9986Schristos 
35eb648470Schristos #include <sys/cdefs.h>
36eb648470Schristos #if defined(LIBC_SCCS) && !defined(lint)
37*5d7e9986Schristos __RCSID("$NetBSD: sockaddr_snprintf.c,v 1.14 2016/12/29 18:30:55 christos Exp $");
38eb648470Schristos #endif /* LIBC_SCCS and not lint */
39eb648470Schristos 
40*5d7e9986Schristos #include <sys/param.h>
41eb648470Schristos #include <sys/types.h>
42eb648470Schristos #include <sys/socket.h>
43eb648470Schristos #include <sys/un.h>
44eb648470Schristos 
45eb648470Schristos #include <netinet/in.h>
46*5d7e9986Schristos #ifdef HAVE_NETATALK_AT_H
47eb648470Schristos #include <netatalk/at.h>
48*5d7e9986Schristos #endif
49*5d7e9986Schristos #ifdef HAVE_NET_IF_DL_H
50eb648470Schristos #include <net/if_dl.h>
51*5d7e9986Schristos #endif
52eb648470Schristos 
53eb648470Schristos #include <stdio.h>
54eb648470Schristos #include <string.h>
55eb648470Schristos #include <errno.h>
5659aa3006Schristos #include <stdlib.h>
57*5d7e9986Schristos #ifdef HAVE_UTIL_H
58eb648470Schristos #include <util.h>
59*5d7e9986Schristos #endif
60*5d7e9986Schristos #ifdef HAVE_LIBUTIL_H
61*5d7e9986Schristos #include <libutil.h>
62*5d7e9986Schristos #endif
63eb648470Schristos #include <netdb.h>
64eb648470Schristos 
65*5d7e9986Schristos #ifdef BSD4_4
66*5d7e9986Schristos # define SALEN(sa)	((sa)->sa ## _len)
67*5d7e9986Schristos #else
68*5d7e9986Schristos # define SALEN(sa)	((unsigned)sizeof(*sa))
69*5d7e9986Schristos #endif
70*5d7e9986Schristos 
71*5d7e9986Schristos #ifdef HAVE_NETATALK_AT_H
7259aa3006Schristos static int
debug_at(char * str,size_t len,const struct sockaddr_at * sat)7359aa3006Schristos debug_at(char *str, size_t len, const struct sockaddr_at *sat)
7459aa3006Schristos {
7559aa3006Schristos 	return snprintf(str, len, "sat_len=%u, sat_family=%u, sat_port=%u, "
7659aa3006Schristos 	    "sat_addr.s_net=%u, sat_addr.s_node=%u, "
7759aa3006Schristos 	    "sat_range.r_netrange.nr_phase=%u, "
7859aa3006Schristos 	    "sat_range.r_netrange.nr_firstnet=%u, "
7959aa3006Schristos 	    "sat_range.r_netrange.nr_lastnet=%u",
80*5d7e9986Schristos 	    SALEN(sat), sat->sat_family, sat->sat_port,
8159aa3006Schristos 	    sat->sat_addr.s_net, sat->sat_addr.s_node,
8259aa3006Schristos 	    sat->sat_range.r_netrange.nr_phase,
8359aa3006Schristos 	    sat->sat_range.r_netrange.nr_firstnet,
8459aa3006Schristos 	    sat->sat_range.r_netrange.nr_lastnet);
8559aa3006Schristos }
86*5d7e9986Schristos #endif
8759aa3006Schristos 
8859aa3006Schristos static int
debug_in(char * str,size_t len,const struct sockaddr_in * sin)8959aa3006Schristos debug_in(char *str, size_t len, const struct sockaddr_in *sin)
9059aa3006Schristos {
9159aa3006Schristos 	return snprintf(str, len, "sin_len=%u, sin_family=%u, sin_port=%u, "
9259aa3006Schristos 	    "sin_addr.s_addr=%08x",
93*5d7e9986Schristos 	    SALEN(sin), sin->sin_family, sin->sin_port,
9459aa3006Schristos 	    sin->sin_addr.s_addr);
9559aa3006Schristos }
9659aa3006Schristos 
9759aa3006Schristos static int
debug_in6(char * str,size_t len,const struct sockaddr_in6 * sin6)9859aa3006Schristos debug_in6(char *str, size_t len, const struct sockaddr_in6 *sin6)
9959aa3006Schristos {
10059aa3006Schristos 	const uint8_t *s = sin6->sin6_addr.s6_addr;
10159aa3006Schristos 
10259aa3006Schristos 	return snprintf(str, len, "sin6_len=%u, sin6_family=%u, sin6_port=%u, "
10359aa3006Schristos 	    "sin6_flowinfo=%u, "
10459aa3006Schristos 	    "sin6_addr=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
10559aa3006Schristos 	    "%02x:%02x:%02x:%02x:%02x:%02x, sin6_scope_id=%u",
106*5d7e9986Schristos 	    SALEN(sin6), sin6->sin6_family, sin6->sin6_port,
10759aa3006Schristos 	    sin6->sin6_flowinfo, s[0x0], s[0x1], s[0x2], s[0x3], s[0x4], s[0x5],
10859aa3006Schristos 	    s[0x6], s[0x7], s[0x8], s[0x9], s[0xa], s[0xb], s[0xc], s[0xd],
10959aa3006Schristos 	    s[0xe], s[0xf], sin6->sin6_scope_id);
11059aa3006Schristos }
11159aa3006Schristos 
11259aa3006Schristos static int
debug_un(char * str,size_t len,const struct sockaddr_un * sun)11359aa3006Schristos debug_un(char *str, size_t len, const struct sockaddr_un *sun)
11459aa3006Schristos {
11559aa3006Schristos 	return snprintf(str, len, "sun_len=%u, sun_family=%u, sun_path=%*s",
116*5d7e9986Schristos 	    SALEN(sun), sun->sun_family, (int)sizeof(sun->sun_path),
11759aa3006Schristos 	    sun->sun_path);
11859aa3006Schristos }
11959aa3006Schristos 
120*5d7e9986Schristos #ifdef HAVE_NET_IF_DL_H
12159aa3006Schristos static int
debug_dl(char * str,size_t len,const struct sockaddr_dl * sdl)12259aa3006Schristos debug_dl(char *str, size_t len, const struct sockaddr_dl *sdl)
12359aa3006Schristos {
12459aa3006Schristos 	const uint8_t *s = (const void *)sdl->sdl_data;
12559aa3006Schristos 
12659aa3006Schristos 	return snprintf(str, len, "sdl_len=%u, sdl_family=%u, sdl_index=%u, "
12759aa3006Schristos 	    "sdl_type=%u, sdl_nlen=%u, sdl_alen=%u, sdl_slen=%u, sdl_data="
12859aa3006Schristos 	    "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
129*5d7e9986Schristos 	    SALEN(sdl), sdl->sdl_family, sdl->sdl_index,
13059aa3006Schristos 	    sdl->sdl_type, sdl->sdl_nlen, sdl->sdl_alen, sdl->sdl_slen,
13159aa3006Schristos 	    s[0x0], s[0x1], s[0x2], s[0x3], s[0x4], s[0x5],
13259aa3006Schristos 	    s[0x6], s[0x7], s[0x8], s[0x9], s[0xa], s[0xb]);
13359aa3006Schristos }
134*5d7e9986Schristos #endif
13559aa3006Schristos 
136eb648470Schristos int
sockaddr_snprintf(char * const sbuf,const size_t len,const char * const fmt,const struct sockaddr * const sa)137bfa27930Sdyoung sockaddr_snprintf(char * const sbuf, const size_t len, const char * const fmt,
138bfa27930Sdyoung     const struct sockaddr * const sa)
139eb648470Schristos {
140eb648470Schristos 	const void *a = NULL;
141*5d7e9986Schristos 	char abuf[1024], nbuf[1024], *addr = NULL;
142a9321bb1Satatat 	char Abuf[1024], pbuf[32], *name = NULL, *port = NULL;
143bfa27930Sdyoung 	char *ebuf = &sbuf[len - 1], *buf = sbuf;
144eb648470Schristos 	const char *ptr, *s;
145*5d7e9986Schristos 	size_t salen;
146a9321bb1Satatat 	int p = -1;
147*5d7e9986Schristos #ifdef HAVE_NETATALK_AT_H
148eb648470Schristos 	const struct sockaddr_at *sat = NULL;
149*5d7e9986Schristos #endif
150eb648470Schristos 	const struct sockaddr_in *sin4 = NULL;
151eb648470Schristos 	const struct sockaddr_in6 *sin6 = NULL;
152eb648470Schristos 	const struct sockaddr_un *sun = NULL;
153*5d7e9986Schristos #ifdef HAVE_NET_IF_DL_H
154eb648470Schristos 	const struct sockaddr_dl *sdl = NULL;
155*5d7e9986Schristos 	char *w = NULL;
156*5d7e9986Schristos #endif
157a9321bb1Satatat 	int na = 1;
158eb648470Schristos 
159a9321bb1Satatat #define ADDC(c) do { if (buf < ebuf) *buf++ = c; else buf++; } \
160a9321bb1Satatat 	while (/*CONSTCOND*/0)
161a9321bb1Satatat #define ADDS(p) do { for (s = p; *s; s++) ADDC(*s); } \
162a9321bb1Satatat 	while (/*CONSTCOND*/0)
163a9321bb1Satatat #define ADDNA() do { if (na) ADDS("N/A"); } \
164a9321bb1Satatat 	while (/*CONSTCOND*/0)
165eb648470Schristos 
166eb648470Schristos 	switch (sa->sa_family) {
167eb648470Schristos 	case AF_UNSPEC:
168eb648470Schristos 		goto done;
169*5d7e9986Schristos #ifdef HAVE_NETATALK_AT_H
170eb648470Schristos 	case AF_APPLETALK:
171*5d7e9986Schristos 		salen = sizeof(*sat);
172eb648470Schristos 		sat = ((const struct sockaddr_at *)(const void *)sa);
173eb648470Schristos 		p = ntohs(sat->sat_port);
174eb648470Schristos 		(void)snprintf(addr = abuf, sizeof(abuf), "%u.%u",
175eb648470Schristos 			ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node);
176a9321bb1Satatat 		(void)snprintf(port = pbuf, sizeof(pbuf), "%d", p);
177eb648470Schristos 		break;
178*5d7e9986Schristos #endif
179eb648470Schristos 	case AF_LOCAL:
180*5d7e9986Schristos 		salen = sizeof(*sun);
181eb648470Schristos 		sun = ((const struct sockaddr_un *)(const void *)sa);
1828c51442dSmlelstv 		(void)strlcpy(addr = abuf, sun->sun_path, sizeof(abuf));
183eb648470Schristos 		break;
184eb648470Schristos 	case AF_INET:
185*5d7e9986Schristos 		salen = sizeof(*sin4);
186eb648470Schristos 		sin4 = ((const struct sockaddr_in *)(const void *)sa);
187eb648470Schristos 		p = ntohs(sin4->sin_port);
188eb648470Schristos 		a = &sin4->sin_addr;
189eb648470Schristos 		break;
190eb648470Schristos 	case AF_INET6:
191*5d7e9986Schristos 		salen = sizeof(*sin6);
192eb648470Schristos 		sin6 = ((const struct sockaddr_in6 *)(const void *)sa);
193eb648470Schristos 		p = ntohs(sin6->sin6_port);
194eb648470Schristos 		a = &sin6->sin6_addr;
195eb648470Schristos 		break;
196*5d7e9986Schristos #ifdef HAVE_NET_IF_DL_H
197eb648470Schristos 	case AF_LINK:
198eb648470Schristos 		sdl = ((const struct sockaddr_dl *)(const void *)sa);
199ee5f11c1Schristos 		addr = abuf;
200ee5f11c1Schristos 		if (sdl->sdl_slen == 0 && sdl->sdl_nlen == 0
201ee5f11c1Schristos 		    && sdl->sdl_alen == 0) {
202*5d7e9986Schristos 			salen = sizeof(*sdl);
203ee5f11c1Schristos 			(void)snprintf(abuf, sizeof(abuf), "link#%hu",
204ee5f11c1Schristos 			    sdl->sdl_index);
205ee5f11c1Schristos 		} else {
206*5d7e9986Schristos 			salen = sdl->sdl_slen + sdl->sdl_nlen +  sdl->sdl_alen;
207*5d7e9986Schristos 			if (salen < sizeof(*sdl))
208*5d7e9986Schristos 				salen = sizeof(*sdl);
209ee5f11c1Schristos 			(void)strlcpy(abuf, link_ntoa(sdl), sizeof(abuf));
210f32209edSchristos 			if ((w = strchr(addr, ':')) != NULL) {
211eb648470Schristos 			    *w++ = '\0';
212eb648470Schristos 			    addr = w;
213eb648470Schristos 			}
214ee5f11c1Schristos 		}
215eb648470Schristos 		break;
216*5d7e9986Schristos #endif
217eb648470Schristos 	default:
218eb648470Schristos 		errno = EAFNOSUPPORT;
219eb648470Schristos 		return -1;
220eb648470Schristos 	}
221eb648470Schristos 
222a9321bb1Satatat 	if (addr == abuf)
223a9321bb1Satatat 		name = addr;
224a9321bb1Satatat 
225*5d7e9986Schristos 	if (a && getnameinfo(sa, (socklen_t)salen, addr = abuf,
226232f61faSelad 	    (unsigned int)sizeof(abuf), NULL, 0,
227232f61faSelad 	    NI_NUMERICHOST|NI_NUMERICSERV) != 0)
228eb648470Schristos 		return -1;
229eb648470Schristos 
230eb648470Schristos 	for (ptr = fmt; *ptr; ptr++) {
231eb648470Schristos 		if (*ptr != '%') {
232eb648470Schristos 			ADDC(*ptr);
233eb648470Schristos 			continue;
234eb648470Schristos 		}
235a9321bb1Satatat 	  next_char:
236eb648470Schristos 		switch (*++ptr) {
237a9321bb1Satatat 		case '?':
238a9321bb1Satatat 			na = 0;
239a9321bb1Satatat 			goto next_char;
240eb648470Schristos 		case 'a':
241eb648470Schristos 			ADDS(addr);
242eb648470Schristos 			break;
243eb648470Schristos 		case 'p':
244a9321bb1Satatat 			if (p != -1) {
245eb648470Schristos 				(void)snprintf(nbuf, sizeof(nbuf), "%d", p);
246eb648470Schristos 				ADDS(nbuf);
247a9321bb1Satatat 			} else
248eb648470Schristos 				ADDNA();
249eb648470Schristos 			break;
250eb648470Schristos 		case 'f':
251eb648470Schristos 			(void)snprintf(nbuf, sizeof(nbuf), "%d", sa->sa_family);
252eb648470Schristos 			ADDS(nbuf);
253eb648470Schristos 			break;
254eb648470Schristos 		case 'l':
255*5d7e9986Schristos 			(void)snprintf(nbuf, sizeof(nbuf), "%zu", salen);
256eb648470Schristos 			ADDS(nbuf);
257eb648470Schristos 			break;
258a9321bb1Satatat 		case 'A':
259a9321bb1Satatat 			if (name)
260a9321bb1Satatat 				ADDS(name);
261a9321bb1Satatat 			else if (!a)
262a9321bb1Satatat 				ADDNA();
263a9321bb1Satatat 			else {
264*5d7e9986Schristos 				getnameinfo(sa, (socklen_t)salen, name = Abuf,
265232f61faSelad 					(unsigned int)sizeof(nbuf), NULL, 0, 0);
266a9321bb1Satatat 				ADDS(name);
267a9321bb1Satatat 			}
268a9321bb1Satatat 			break;
269a9321bb1Satatat 		case 'P':
270a9321bb1Satatat 			if (port)
271a9321bb1Satatat 				ADDS(port);
272a9321bb1Satatat 			else if (p == -1)
273a9321bb1Satatat 				ADDNA();
274a9321bb1Satatat 			else {
275*5d7e9986Schristos 				getnameinfo(sa, (socklen_t)salen, NULL, 0,
276232f61faSelad 					port = pbuf,
277232f61faSelad 					(unsigned int)sizeof(pbuf), 0);
278a9321bb1Satatat 				ADDS(port);
279a9321bb1Satatat 			}
280a9321bb1Satatat 			break;
281eb648470Schristos 		case 'I':
282*5d7e9986Schristos #ifdef HAVE_NET_IF_DL_H
283eb648470Schristos 			if (sdl && addr != abuf) {
284eb648470Schristos 				ADDS(abuf);
285*5d7e9986Schristos 			} else
286*5d7e9986Schristos #endif
287*5d7e9986Schristos 			{
288eb648470Schristos 				ADDNA();
289eb648470Schristos 			}
290eb648470Schristos 			break;
291eb648470Schristos 		case 'F':
292eb648470Schristos 			if (sin6) {
293eb648470Schristos 				(void)snprintf(nbuf, sizeof(nbuf), "%d",
294eb648470Schristos 				    sin6->sin6_flowinfo);
295eb648470Schristos 				ADDS(nbuf);
296eb648470Schristos 				break;
297eb648470Schristos 			} else {
298eb648470Schristos 				ADDNA();
299eb648470Schristos 			}
300eb648470Schristos 			break;
301eb648470Schristos 		case 'S':
302eb648470Schristos 			if (sin6) {
303eb648470Schristos 				(void)snprintf(nbuf, sizeof(nbuf), "%d",
304eb648470Schristos 				    sin6->sin6_scope_id);
305eb648470Schristos 				ADDS(nbuf);
306eb648470Schristos 				break;
307eb648470Schristos 			} else {
308eb648470Schristos 				ADDNA();
309eb648470Schristos 			}
310eb648470Schristos 			break;
311eb648470Schristos 		case 'R':
312*5d7e9986Schristos #ifdef HAVE_NETATALK_AT_H
313eb648470Schristos 			if (sat) {
314eb648470Schristos 				const struct netrange *n =
315eb648470Schristos 				    &sat->sat_range.r_netrange;
316eb648470Schristos 				(void)snprintf(nbuf, sizeof(nbuf),
317eb648470Schristos 				    "%d:[%d,%d]", n->nr_phase , n->nr_firstnet,
318eb648470Schristos 				    n->nr_lastnet);
319eb648470Schristos 				ADDS(nbuf);
320*5d7e9986Schristos 			} else
321*5d7e9986Schristos #endif
322*5d7e9986Schristos 			{
323eb648470Schristos 				ADDNA();
324eb648470Schristos 			}
325eb648470Schristos 			break;
32659aa3006Schristos 		case 'D':
32759aa3006Schristos 			switch (sa->sa_family) {
328*5d7e9986Schristos #ifdef HAVE_NETATALK_AT_H
32959aa3006Schristos 			case AF_APPLETALK:
33059aa3006Schristos 				debug_at(nbuf, sizeof(nbuf), sat);
33159aa3006Schristos 				break;
332*5d7e9986Schristos #endif
33359aa3006Schristos 			case AF_LOCAL:
33459aa3006Schristos 				debug_un(nbuf, sizeof(nbuf), sun);
33559aa3006Schristos 				break;
33659aa3006Schristos 			case AF_INET:
33759aa3006Schristos 				debug_in(nbuf, sizeof(nbuf), sin4);
33859aa3006Schristos 				break;
33959aa3006Schristos 			case AF_INET6:
34059aa3006Schristos 				debug_in6(nbuf, sizeof(nbuf), sin6);
34159aa3006Schristos 				break;
342*5d7e9986Schristos #ifdef HAVE_NET_IF_DL_H
34359aa3006Schristos 			case AF_LINK:
34459aa3006Schristos 				debug_dl(nbuf, sizeof(nbuf), sdl);
34559aa3006Schristos 				break;
346*5d7e9986Schristos #endif
34759aa3006Schristos 			default:
34859aa3006Schristos 				abort();
34959aa3006Schristos 			}
35059aa3006Schristos 			ADDS(nbuf);
35159aa3006Schristos 			break;
352eb648470Schristos 		default:
353eb648470Schristos 			ADDC('%');
354a9321bb1Satatat 			if (na == 0)
355a9321bb1Satatat 				ADDC('?');
356a9321bb1Satatat 			if (*ptr == '\0')
357a9321bb1Satatat 				goto done;
358a26b3af0Sdyoung 			/*FALLTHROUGH*/
359a26b3af0Sdyoung 		case '%':
360eb648470Schristos 			ADDC(*ptr);
361eb648470Schristos 			break;
362eb648470Schristos 		}
363a9321bb1Satatat 		na = 1;
364eb648470Schristos 	}
365eb648470Schristos done:
366bfa27930Sdyoung 	if (buf < ebuf)
367bfa27930Sdyoung 		*buf = '\0';
368bfa27930Sdyoung 	else if (len != 0)
369bfa27930Sdyoung 		sbuf[len - 1] = '\0';
370232f61faSelad 	return (int)(buf - sbuf);
371eb648470Schristos }
372