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