1/*	$NetBSD: sockaddr_snprintf.c,v 1.11 2016/06/01 22:57:51 christos Exp $	*/
2
3/*-
4 * Copyright (c) 2004 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
35#include <sys/cdefs.h>
36#if defined(LIBC_SCCS) && !defined(lint)
37__RCSID("$NetBSD: sockaddr_snprintf.c,v 1.11 2016/06/01 22:57:51 christos Exp $");
38#endif /* LIBC_SCCS and not lint */
39
40#include <sys/param.h>
41#include <sys/types.h>
42#include <sys/socket.h>
43#include <sys/un.h>
44
45#include <netinet/in.h>
46#ifdef __linux__
47#undef HAVE_NETATALK_AT_H
48#endif
49#ifdef HAVE_NETATALK_AT_H
50#include <netatalk/at.h>
51#endif
52#ifdef HAVE_NET_IF_DL_H
53#include <net/if_dl.h>
54#endif
55
56#include <stdio.h>
57#include <string.h>
58#include <errno.h>
59#include <stdlib.h>
60#ifdef HAVE_LIBUTIL_H
61#include <libutil.h>
62#endif
63#ifdef HAVE_UTIL_H
64#include <util.h>
65#endif
66#include <netdb.h>
67
68#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
69#define SLEN(a)	(a)->a ## _len
70#else
71static socklen_t
72socklen(u_int af)
73{
74	switch (af) {
75	case AF_INET:
76		return sizeof(struct sockaddr_in);
77	case AF_INET6:
78		return sizeof(struct sockaddr_in6);
79	case AF_LOCAL:
80		return sizeof(struct sockaddr_un);
81#ifdef HAVE_NET_IF_DL_H
82	case AF_LINK:
83		return sizeof(struct sockaddr_dl);
84#endif
85#ifdef HAVE_NETATALK_AT_H
86	case AF_APPLETALK:
87		return sizeof(struct sockaddr_at);
88#endif
89	default:
90		return sizeof(struct sockaddr_storage);
91	}
92}
93
94#define SLEN(a)	socklen((a)->a ## _family)
95#endif
96
97#ifdef HAVE_NETATALK_AT_H
98static int
99debug_at(char *str, size_t len, const struct sockaddr_at *sat)
100{
101	return snprintf(str, len, "sat_len=%u, sat_family=%u, sat_port=%u, "
102	    "sat_addr.s_net=%u, sat_addr.s_node=%u, "
103	    "sat_range.r_netrange.nr_phase=%u, "
104	    "sat_range.r_netrange.nr_firstnet=%u, "
105	    "sat_range.r_netrange.nr_lastnet=%u",
106	    SLEN(sat), sat->sat_family, sat->sat_port,
107	    sat->sat_addr.s_net, sat->sat_addr.s_node,
108	    sat->sat_range.r_netrange.nr_phase,
109	    sat->sat_range.r_netrange.nr_firstnet,
110	    sat->sat_range.r_netrange.nr_lastnet);
111}
112#endif
113
114static int
115debug_in(char *str, size_t len, const struct sockaddr_in *sin)
116{
117	return snprintf(str, len, "sin_len=%u, sin_family=%u, sin_port=%u, "
118	    "sin_addr.s_addr=%08x",
119	    SLEN(sin), sin->sin_family, sin->sin_port,
120	    sin->sin_addr.s_addr);
121}
122
123static int
124debug_in6(char *str, size_t len, const struct sockaddr_in6 *sin6)
125{
126	const uint8_t *s = sin6->sin6_addr.s6_addr;
127
128	return snprintf(str, len, "sin6_len=%u, sin6_family=%u, sin6_port=%u, "
129	    "sin6_flowinfo=%u, "
130	    "sin6_addr=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
131	    "%02x:%02x:%02x:%02x:%02x:%02x, sin6_scope_id=%u",
132	    SLEN(sin6), sin6->sin6_family, sin6->sin6_port,
133	    sin6->sin6_flowinfo, s[0x0], s[0x1], s[0x2], s[0x3], s[0x4], s[0x5],
134	    s[0x6], s[0x7], s[0x8], s[0x9], s[0xa], s[0xb], s[0xc], s[0xd],
135	    s[0xe], s[0xf], sin6->sin6_scope_id);
136}
137
138static int
139debug_un(char *str, size_t len, const struct sockaddr_un *sun)
140{
141	return snprintf(str, len, "sun_len=%u, sun_family=%u, sun_path=%*s",
142	    SLEN(sun), sun->sun_family, (int)sizeof(sun->sun_path),
143	    sun->sun_path);
144}
145
146#ifdef HAVE_NET_IF_DL_H
147static int
148debug_dl(char *str, size_t len, const struct sockaddr_dl *sdl)
149{
150	const uint8_t *s = (const void *)sdl->sdl_data;
151
152	return snprintf(str, len, "sdl_len=%u, sdl_family=%u, sdl_index=%u, "
153	    "sdl_type=%u, sdl_nlen=%u, sdl_alen=%u, sdl_slen=%u, sdl_data="
154	    "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
155	    SLEN(sdl), sdl->sdl_family, sdl->sdl_index,
156	    sdl->sdl_type, sdl->sdl_nlen, sdl->sdl_alen, sdl->sdl_slen,
157	    s[0x0], s[0x1], s[0x2], s[0x3], s[0x4], s[0x5],
158	    s[0x6], s[0x7], s[0x8], s[0x9], s[0xa], s[0xb]);
159}
160#endif
161
162int
163sockaddr_snprintf(char * const sbuf, const size_t len, const char * const fmt,
164    const struct sockaddr * const sa)
165{
166	const void *a = NULL;
167	char abuf[1024], nbuf[1024], *addr = NULL;
168
169	char Abuf[1024], pbuf[32], *name = NULL, *port = NULL;
170	char *ebuf = &sbuf[len - 1], *buf = sbuf;
171	const char *ptr, *s;
172	int p = -1;
173#ifdef HAVE_NETATALK_AT_H
174	const struct sockaddr_at *sat = NULL;
175#endif
176	const struct sockaddr_in *sin4 = NULL;
177	const struct sockaddr_in6 *sin6 = NULL;
178	const struct sockaddr_un *sun = NULL;
179#ifdef HAVE_NET_IF_DL_H
180	const struct sockaddr_dl *sdl = NULL;
181	char *w = NULL;
182#endif
183	int na = 1;
184
185#define ADDC(c) do { if (buf < ebuf) *buf++ = c; else buf++; } \
186	while (/*CONSTCOND*/0)
187#define ADDS(p) do { for (s = p; *s; s++) ADDC(*s); } \
188	while (/*CONSTCOND*/0)
189#define ADDNA() do { if (na) ADDS("N/A"); } \
190	while (/*CONSTCOND*/0)
191
192	switch (sa->sa_family) {
193	case AF_UNSPEC:
194		goto done;
195#ifdef HAVE_NETATALK_AT_H
196	case AF_APPLETALK:
197		sat = ((const struct sockaddr_at *)(const void *)sa);
198		p = ntohs(sat->sat_port);
199		(void)snprintf(addr = abuf, sizeof(abuf), "%u.%u",
200			ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node);
201		(void)snprintf(port = pbuf, sizeof(pbuf), "%d", p);
202		break;
203#endif
204	case AF_LOCAL:
205		sun = ((const struct sockaddr_un *)(const void *)sa);
206		(void)strlcpy(addr = abuf, sun->sun_path, sizeof(abuf));
207		break;
208	case AF_INET:
209		sin4 = ((const struct sockaddr_in *)(const void *)sa);
210		p = ntohs(sin4->sin_port);
211		a = &sin4->sin_addr;
212		break;
213	case AF_INET6:
214		sin6 = ((const struct sockaddr_in6 *)(const void *)sa);
215		p = ntohs(sin6->sin6_port);
216		a = &sin6->sin6_addr;
217		break;
218#ifdef HAVE_NET_IF_DL_H
219	case AF_LINK:
220		sdl = ((const struct sockaddr_dl *)(const void *)sa);
221		(void)strlcpy(addr = abuf, link_ntoa(sdl), sizeof(abuf));
222		if ((w = strchr(addr, ':')) != NULL) {
223			*w++ = '\0';
224			addr = w;
225		}
226		break;
227#endif
228	default:
229		errno = EAFNOSUPPORT;
230		return -1;
231	}
232
233	if (addr == abuf)
234		name = addr;
235
236	if (a && getnameinfo(sa, (socklen_t)SLEN(sa), addr = abuf,
237	    (unsigned int)sizeof(abuf), NULL, 0,
238	    NI_NUMERICHOST|NI_NUMERICSERV) != 0)
239		return -1;
240
241	for (ptr = fmt; *ptr; ptr++) {
242		if (*ptr != '%') {
243			ADDC(*ptr);
244			continue;
245		}
246	  next_char:
247		switch (*++ptr) {
248		case '?':
249			na = 0;
250			goto next_char;
251		case 'a':
252			ADDS(addr);
253			break;
254		case 'p':
255			if (p != -1) {
256				(void)snprintf(nbuf, sizeof(nbuf), "%d", p);
257				ADDS(nbuf);
258			} else
259				ADDNA();
260			break;
261		case 'f':
262			(void)snprintf(nbuf, sizeof(nbuf), "%d", sa->sa_family);
263			ADDS(nbuf);
264			break;
265		case 'l':
266			(void)snprintf(nbuf, sizeof(nbuf), "%d", SLEN(sa));
267			ADDS(nbuf);
268			break;
269		case 'A':
270			if (name)
271				ADDS(name);
272			else if (!a)
273				ADDNA();
274			else {
275				getnameinfo(sa, (socklen_t)SLEN(sa),
276					name = Abuf,
277					(unsigned int)sizeof(nbuf), NULL, 0, 0);
278				ADDS(name);
279			}
280			break;
281		case 'P':
282			if (port)
283				ADDS(port);
284			else if (p == -1)
285				ADDNA();
286			else {
287				getnameinfo(sa, (socklen_t)SLEN(sa), NULL, 0,
288					port = pbuf,
289					(unsigned int)sizeof(pbuf), 0);
290				ADDS(port);
291			}
292			break;
293		case 'I':
294#ifdef HAVE_NET_IF_DL_H
295			if (sdl && addr != abuf) {
296				ADDS(abuf);
297			} else
298#endif
299			{
300				ADDNA();
301			}
302			break;
303		case 'F':
304			if (sin6) {
305				(void)snprintf(nbuf, sizeof(nbuf), "%d",
306				    sin6->sin6_flowinfo);
307				ADDS(nbuf);
308				break;
309			} else {
310				ADDNA();
311			}
312			break;
313		case 'S':
314			if (sin6) {
315				(void)snprintf(nbuf, sizeof(nbuf), "%d",
316				    sin6->sin6_scope_id);
317				ADDS(nbuf);
318				break;
319			} else {
320				ADDNA();
321			}
322			break;
323		case 'R':
324#ifdef HAVE_NETATALK_AT_H
325			if (sat) {
326				const struct netrange *n =
327				    &sat->sat_range.r_netrange;
328				(void)snprintf(nbuf, sizeof(nbuf),
329				    "%d:[%d,%d]", n->nr_phase , n->nr_firstnet,
330				    n->nr_lastnet);
331				ADDS(nbuf);
332			} else
333#endif
334			{
335				ADDNA();
336			}
337			break;
338		case 'D':
339			switch (sa->sa_family) {
340#ifdef HAVE_NETATALK_AT_H
341			case AF_APPLETALK:
342				debug_at(nbuf, sizeof(nbuf), sat);
343				break;
344#endif
345			case AF_LOCAL:
346				debug_un(nbuf, sizeof(nbuf), sun);
347				break;
348			case AF_INET:
349				debug_in(nbuf, sizeof(nbuf), sin4);
350				break;
351			case AF_INET6:
352				debug_in6(nbuf, sizeof(nbuf), sin6);
353				break;
354#ifdef HAVE_NET_IF_DL_H
355			case AF_LINK:
356				debug_dl(nbuf, sizeof(nbuf), sdl);
357				break;
358#endif
359			default:
360				abort();
361			}
362			ADDS(nbuf);
363			break;
364		default:
365			ADDC('%');
366			if (na == 0)
367				ADDC('?');
368			if (*ptr == '\0')
369				goto done;
370			/*FALLTHROUGH*/
371		case '%':
372			ADDC(*ptr);
373			break;
374		}
375		na = 1;
376	}
377done:
378	if (buf < ebuf)
379		*buf = '\0';
380	else if (len != 0)
381		sbuf[len - 1] = '\0';
382	return (int)(buf - sbuf);
383}
384