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 71 static socklen_t 72 socklen(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 98 static int 99 debug_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 114 static int 115 debug_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 123 static int 124 debug_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 138 static int 139 debug_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 147 static int 148 debug_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 162 int 163 sockaddr_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 } 377 done: 378 if (buf < ebuf) 379 *buf = '\0'; 380 else if (len != 0) 381 sbuf[len - 1] = '\0'; 382 return (int)(buf - sbuf); 383 } 384