getnameinfo.c revision 1.29 1 /* $NetBSD: getnameinfo.c,v 1.29 2001/10/04 23:09:04 bjh21 Exp $ */
2 /* $KAME: getnameinfo.c,v 1.45 2000/09/25 22:43:56 itojun Exp $ */
3
4 /*
5 * Copyright (c) 2000 Ben Harris.
6 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the project nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 /*
35 * Issues to be discussed:
36 * - Thread safe-ness must be checked
37 * - RFC2553 says that we should raise error on short buffer. X/Open says
38 * we need to truncate the result. We obey RFC2553 (and X/Open should be
39 * modified). ipngwg rough consensus seems to follow RFC2553.
40 * - What is "local" in NI_FQDN?
41 * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other.
42 * - (KAME extension) NI_WITHSCOPEID when called with global address,
43 * and sin6_scope_id filled
44 */
45
46 #include <sys/cdefs.h>
47 #if defined(LIBC_SCCS) && !defined(lint)
48 __RCSID("$NetBSD: getnameinfo.c,v 1.29 2001/10/04 23:09:04 bjh21 Exp $");
49 #endif /* LIBC_SCCS and not lint */
50
51 #include "namespace.h"
52 #include <sys/types.h>
53 #include <sys/socket.h>
54 #include <net/if.h>
55 #include <net/if_dl.h>
56 #include <net/if_ieee1394.h>
57 #include <net/if_types.h>
58 #include <netinet/in.h>
59 #include <arpa/inet.h>
60 #include <arpa/nameser.h>
61 #include <assert.h>
62 #include <netdb.h>
63 #include <resolv.h>
64 #include <stddef.h>
65 #include <string.h>
66
67 #ifdef __weak_alias
68 __weak_alias(getnameinfo,_getnameinfo)
69 #endif
70
71 #define SUCCESS 0
72 #define ANY 0
73 #define YES 1
74 #define NO 0
75
76 static const struct afd {
77 int a_af;
78 socklen_t a_addrlen;
79 socklen_t a_socklen;
80 int a_off;
81 } afdl [] = {
82 #ifdef INET6
83 {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
84 offsetof(struct sockaddr_in6, sin6_addr)},
85 #endif
86 {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
87 offsetof(struct sockaddr_in, sin_addr)},
88 {0, 0, 0},
89 };
90
91 struct sockinet {
92 u_char si_len;
93 u_char si_family;
94 u_short si_port;
95 };
96
97 static int getnameinfo_inet __P((const struct sockaddr *, socklen_t, char *,
98 size_t, char *, size_t, int));
99 #ifdef INET6
100 static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *,
101 size_t, int));
102 static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, int));
103 #endif
104 static int getnameinfo_link __P((const struct sockaddr *, socklen_t, char *,
105 size_t, char *, size_t, int));
106 static int hexname __P((u_int8_t *, size_t, char *, size_t));
107
108 /* 2553bis: use EAI_xx for getnameinfo */
109 #define ENI_NOSOCKET EAI_FAIL /*XXX*/
110 #define ENI_NOSERVNAME EAI_NONAME
111 #define ENI_NOHOSTNAME EAI_NONAME
112 #define ENI_MEMORY EAI_MEMORY
113 #define ENI_SYSTEM EAI_SYSTEM
114 #define ENI_FAMILY EAI_FAMILY
115 #define ENI_SALEN EAI_FAMILY
116
117 /*
118 * Top-level getnameinfo() code. Look at the address family, and pick an
119 * appropriate function to call.
120 */
121 int
122 getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
123 const struct sockaddr *sa;
124 socklen_t salen;
125 char *host, *serv;
126 size_t hostlen, servlen;
127 int flags;
128 {
129
130 switch (sa->sa_family) {
131 case AF_INET:
132 case AF_INET6:
133 return getnameinfo_inet(sa, salen, host, hostlen,
134 serv, servlen, flags);
135 case AF_LINK:
136 return getnameinfo_link(sa, salen, host, hostlen,
137 serv, servlen, flags);
138 default:
139 return EAI_FAMILY;
140 }
141 }
142
143
144 /*
145 * getnameinfo_inet():
146 * Format an IPv4 or IPv6 sockaddr into a printable string.
147 */
148 static int
149 getnameinfo_inet(sa, salen, host, hostlen, serv, servlen, flags)
150 const struct sockaddr *sa;
151 socklen_t salen;
152 char *host;
153 size_t hostlen;
154 char *serv;
155 size_t servlen;
156 int flags;
157 {
158 const struct afd *afd;
159 struct servent *sp;
160 struct hostent *hp;
161 u_short port;
162 int family, i;
163 const char *addr;
164 u_int32_t v4a;
165 char numserv[512];
166 char numaddr[512];
167
168 /* sa is checked below */
169 /* host may be NULL */
170 /* serv may be NULL */
171
172 if (sa == NULL)
173 return ENI_NOSOCKET;
174
175 #ifdef BSD4_4
176 if (sa->sa_len != salen)
177 return ENI_SALEN;
178 #endif
179
180 family = sa->sa_family;
181 for (i = 0; afdl[i].a_af; i++)
182 if (afdl[i].a_af == family) {
183 afd = &afdl[i];
184 goto found;
185 }
186 return ENI_FAMILY;
187
188 found:
189 if (salen != afd->a_socklen)
190 return ENI_SALEN;
191
192 /* network byte order */
193 port = ((const struct sockinet *)(const void *)sa)->si_port;
194 addr = (const char *)(const void *)sa + afd->a_off;
195
196 if (serv == NULL || servlen == 0) {
197 /*
198 * do nothing in this case.
199 * in case you are wondering if "&&" is more correct than
200 * "||" here: RFC2553 says that serv == NULL OR servlen == 0
201 * means that the caller does not want the result.
202 */
203 } else {
204 if (flags & NI_NUMERICSERV)
205 sp = NULL;
206 else {
207 sp = getservbyport(port,
208 (flags & NI_DGRAM) ? "udp" : "tcp");
209 }
210 if (sp) {
211 if (strlen(sp->s_name) + 1 > servlen)
212 return ENI_MEMORY;
213 strcpy(serv, sp->s_name);
214 } else {
215 snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
216 if (strlen(numserv) + 1 > servlen)
217 return ENI_MEMORY;
218 strcpy(serv, numserv);
219 }
220 }
221
222 switch (sa->sa_family) {
223 case AF_INET:
224 v4a = (u_int32_t)
225 ntohl(((const struct sockaddr_in *)
226 (const void *)sa)->sin_addr.s_addr);
227 if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
228 flags |= NI_NUMERICHOST;
229 v4a >>= IN_CLASSA_NSHIFT;
230 if (v4a == 0)
231 flags |= NI_NUMERICHOST;
232 break;
233 #ifdef INET6
234 case AF_INET6:
235 {
236 const struct sockaddr_in6 *sin6;
237 sin6 = (const struct sockaddr_in6 *)(const void *)sa;
238 switch (sin6->sin6_addr.s6_addr[0]) {
239 case 0x00:
240 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
241 ;
242 else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
243 ;
244 else
245 flags |= NI_NUMERICHOST;
246 break;
247 default:
248 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
249 flags |= NI_NUMERICHOST;
250 }
251 else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
252 flags |= NI_NUMERICHOST;
253 break;
254 }
255 }
256 break;
257 #endif
258 }
259 if (host == NULL || hostlen == 0) {
260 /*
261 * do nothing in this case.
262 * in case you are wondering if "&&" is more correct than
263 * "||" here: RFC2553 says that host == NULL OR hostlen == 0
264 * means that the caller does not want the result.
265 */
266 } else if (flags & NI_NUMERICHOST) {
267 int numaddrlen;
268
269 /* NUMERICHOST and NAMEREQD conflicts with each other */
270 if (flags & NI_NAMEREQD)
271 return ENI_NOHOSTNAME;
272
273 switch(afd->a_af) {
274 #ifdef INET6
275 case AF_INET6:
276 {
277 int error;
278
279 if ((error = ip6_parsenumeric(sa, addr, host,
280 hostlen, flags)) != 0)
281 return(error);
282 break;
283 }
284 #endif
285 default:
286 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
287 == NULL)
288 return ENI_SYSTEM;
289 numaddrlen = strlen(numaddr);
290 if (numaddrlen + 1 > hostlen) /* don't forget terminator */
291 return ENI_MEMORY;
292 strcpy(host, numaddr);
293 break;
294 }
295 } else {
296 hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
297
298 if (hp) {
299 #if 0
300 /*
301 * commented out, since "for local host" is not
302 * implemented here - see RFC2553 p30
303 */
304 if (flags & NI_NOFQDN) {
305 char *p;
306 p = strchr(hp->h_name, '.');
307 if (p)
308 *p = '\0';
309 }
310 #endif
311 if (strlen(hp->h_name) + 1 > hostlen) {
312 return ENI_MEMORY;
313 }
314 strcpy(host, hp->h_name);
315 } else {
316 if (flags & NI_NAMEREQD)
317 return ENI_NOHOSTNAME;
318 switch(afd->a_af) {
319 #ifdef INET6
320 case AF_INET6:
321 {
322 int error;
323
324 if ((error = ip6_parsenumeric(sa, addr, host,
325 hostlen,
326 flags)) != 0)
327 return(error);
328 break;
329 }
330 #endif
331 default:
332 if (inet_ntop(afd->a_af, addr, host,
333 hostlen) == NULL)
334 return ENI_SYSTEM;
335 break;
336 }
337 }
338 }
339 return SUCCESS;
340 }
341
342 #ifdef INET6
343 static int
344 ip6_parsenumeric(sa, addr, host, hostlen, flags)
345 const struct sockaddr *sa;
346 const char *addr;
347 char *host;
348 size_t hostlen;
349 int flags;
350 {
351 int numaddrlen;
352 char numaddr[512];
353
354 _DIAGASSERT(sa != NULL);
355 _DIAGASSERT(addr != NULL);
356 _DIAGASSERT(host != NULL);
357
358 if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr))
359 == NULL)
360 return ENI_SYSTEM;
361
362 numaddrlen = strlen(numaddr);
363 if (numaddrlen + 1 > hostlen) /* don't forget terminator */
364 return ENI_MEMORY;
365 strcpy(host, numaddr);
366
367 #ifdef NI_WITHSCOPEID
368 if (((const struct sockaddr_in6 *)(const void *)sa)->sin6_scope_id) {
369 if (flags & NI_WITHSCOPEID)
370 {
371 char scopebuf[MAXHOSTNAMELEN];
372 int scopelen;
373
374 scopelen = ip6_sa2str(
375 (const struct sockaddr_in6 *)(const void *)sa,
376 scopebuf, sizeof(scopebuf), 0);
377 if (scopelen < 0)
378 return ENI_MEMORY;
379 if (scopelen + 1 + numaddrlen + 1 > hostlen)
380 return ENI_MEMORY;
381 /*
382 * construct <numeric-addr><delim><scopeid>
383 */
384 memcpy(host + numaddrlen + 1, scopebuf,
385 (size_t)scopelen);
386 host[numaddrlen] = SCOPE_DELIMITER;
387 host[numaddrlen + 1 + scopelen] = '\0';
388 }
389 }
390 #endif /* NI_WITHSCOPEID */
391
392 return 0;
393 }
394
395 /* ARGSUSED */
396 static int
397 ip6_sa2str(sa6, buf, bufsiz, flags)
398 const struct sockaddr_in6 *sa6;
399 char *buf;
400 size_t bufsiz;
401 int flags;
402 {
403 unsigned int ifindex;
404 const struct in6_addr *a6;
405 int n;
406
407 _DIAGASSERT(sa6 != NULL);
408 _DIAGASSERT(buf != NULL);
409
410 ifindex = (unsigned int)sa6->sin6_scope_id;
411 a6 = &sa6->sin6_addr;
412
413 #ifdef NI_NUMERICSCOPE
414 if ((flags & NI_NUMERICSCOPE) != 0) {
415 n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
416 if (n < 0 || n >= bufsiz)
417 return -1;
418 else
419 return n;
420 }
421 #endif
422
423 /* if_indextoname() does not take buffer size. not a good api... */
424 if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) &&
425 bufsiz >= IF_NAMESIZE) {
426 char *p = if_indextoname(ifindex, buf);
427 if (p) {
428 return(strlen(p));
429 }
430 }
431
432 /* last resort */
433 n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
434 if (n < 0 || n >= bufsiz)
435 return -1;
436 else
437 return n;
438 }
439 #endif /* INET6 */
440
441
442 /*
443 * getnameinfo_link():
444 * Format a link-layer address into a printable format, paying attention to
445 * the interface type.
446 */
447 static int
448 getnameinfo_link(const struct sockaddr *sa, socklen_t salen,
449 char *host, size_t hostlen, char *serv, size_t servlen, int flags)
450 {
451 const struct sockaddr_dl *sdl = (const struct sockaddr_dl *)sa;
452 struct ieee1394_hwaddr *iha;
453 int n;
454
455 if (serv != NULL && servlen > 0)
456 *serv = '\0';
457
458 if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) {
459 n = snprintf(host, hostlen, "link#%d", sdl->sdl_index);
460 if (n > hostlen) {
461 *host = '\0';
462 return EAI_MEMORY;
463 }
464 return 0;
465 }
466
467 switch (sdl->sdl_type) {
468 #ifdef IFT_ECONET
469 case IFT_ECONET:
470 if (sdl->sdl_alen < 2)
471 return EAI_FAMILY;
472 if (LLADDR(sdl)[1] == 0)
473 n = snprintf(host, hostlen, "%d", LLADDR(sdl)[0]);
474 else
475 n = snprintf(host, hostlen, "%d.%d",
476 LLADDR(sdl)[1], LLADDR(sdl)[0]);
477 if (n >= hostlen) {
478 *host = '\0';
479 return EAI_MEMORY;
480 } else
481 return 0;
482 #endif
483 case IFT_IEEE1394:
484 if (sdl->sdl_alen < sizeof(iha->iha_uid))
485 return EAI_FAMILY;
486 iha = (struct ieee1394_hwaddr *)LLADDR(sdl);
487 return hexname(iha->iha_uid, sizeof(iha->iha_uid),
488 host, hostlen);
489 /*
490 * The following have zero-length addresses.
491 * IFT_ATM (net/if_atmsubr.c)
492 * IFT_FAITH (net/if_faith.c)
493 * IFT_GIF (net/if_gif.c)
494 * IFT_LOOP (net/if_loop.c)
495 * IFT_PPP (net/if_ppp.c, net/if_spppsubr.c)
496 * IFT_SLIP (net/if_sl.c, net/if_strip.c)
497 * IFT_STF (net/if_stf.c)
498 * IFT_L2VLAN (net/if_vlan.c)
499 * IFT_PROPVIRTUAL (net/if_bridge.h>
500 */
501 /*
502 * The following use IPv4 addresses as link-layer addresses:
503 * IFT_OTHER (net/if_gre.c)
504 * IFT_OTHER (netinet/ip_ipip.c)
505 */
506 case IFT_ARCNET: /* default below is believed correct for all these. */
507 case IFT_ETHER:
508 case IFT_FDDI:
509 case IFT_HIPPI:
510 case IFT_ISO88025:
511 default:
512 return hexname(LLADDR(sdl), sdl->sdl_alen, host, hostlen);
513 }
514 }
515
516 static int
517 hexname(cp, len, host, hostlen)
518 u_int8_t *cp;
519 char *host;
520 size_t len, hostlen;
521 {
522 int i, n, space = hostlen;
523 char *outp = host;
524
525 *outp = '\0';
526 for (i = 0; i < len; i++) {
527 n = snprintf(outp, hostlen, "%s%02x", i ? ":" : "", cp[i]);
528 outp += n;
529 space -= n;
530 if (space <= 0) {
531 *host = '\0';
532 return EAI_MEMORY;
533 }
534 }
535 return 0;
536 }
537