1/* Convert socket address to string using Name Service Switch modules.
2 Copyright (C) 1997-2018 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
18
19/* The Inner Net License, Version 2.00
20
21 The author(s) grant permission for redistribution and use in source and
22binary forms, with or without modification, of the software and documentation
23provided that the following conditions are met:
24
250. If you receive a version of the software that is specifically labelled
26 as not being for redistribution (check the version message and/or README),
27 you are not permitted to redistribute that version of the software in any
28 way or form.
291. All terms of the all other applicable copyrights and licenses must be
30 followed.
312. Redistributions of source code must retain the authors' copyright
32 notice(s), this list of conditions, and the following disclaimer.
333. Redistributions in binary form must reproduce the authors' copyright
34 notice(s), this list of conditions, and the following disclaimer in the
35 documentation and/or other materials provided with the distribution.
364. [The copyright holder has authorized the removal of this clause.]
375. Neither the name(s) of the author(s) nor the names of its contributors
38 may be used to endorse or promote products derived from this software
39 without specific prior written permission.
40
41THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
42EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
43WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
45DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
46(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
48ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
50SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51
52 If these license terms cause you a real problem, contact the author. */
53
54/* This software is Copyright 1996 by Craig Metz, All Rights Reserved. */
55
56#include <errno.h>
57#include <netdb.h>
58#include <stddef.h>
59#include <stdlib.h>
60#include <stdio.h>
61#include <string.h>
62#include <unistd.h>
63#include <stdint.h>
64#include <arpa/inet.h>
65#include <net/if.h>
66#include <netinet/in.h>
67#include <sys/param.h>
68#include <sys/socket.h>
69#include <sys/types.h>
70#include <sys/un.h>
71#include <sys/utsname.h>
72#include <libc-lock.h>
73#include <scratch_buffer.h>
74
75#ifdef HAVE_LIBIDN
76# include <idna.h>
77#endif
78
79#ifndef min
80# define min(x,y) (((x) > (y)) ? (y) : (x))
81#endif /* min */
82
83libc_freeres_ptr (static char *domain);
84
85
86static char *
87nrl_domainname (void)
88{
89 static int not_first;
90
91 if (! not_first)
92 {
93 __libc_lock_define_initialized (static, lock);
94 __libc_lock_lock (lock);
95
96 if (! not_first)
97 {
98 char *c;
99 struct hostent *h, th;
100 int herror;
101 struct scratch_buffer tmpbuf;
102
103 scratch_buffer_init (&tmpbuf);
104 not_first = 1;
105
106 while (__gethostbyname_r ("localhost", &th,
107 tmpbuf.data, tmpbuf.length,
108 &h, &herror))
109 {
110 if (herror == NETDB_INTERNAL && errno == ERANGE)
111 {
112 if (!scratch_buffer_grow (&tmpbuf))
113 goto done;
114 }
115 else
116 break;
117 }
118
119 if (h && (c = strchr (h->h_name, '.')))
120 domain = __strdup (++c);
121 else
122 {
123 /* The name contains no domain information. Use the name
124 now to get more information. */
125 while (__gethostname (tmpbuf.data, tmpbuf.length))
126 if (!scratch_buffer_grow (&tmpbuf))
127 goto done;
128
129 if ((c = strchr (tmpbuf.data, '.')))
130 domain = __strdup (++c);
131 else
132 {
133 /* We need to preserve the hostname. */
134 const char *hstname = strdupa (tmpbuf.data);
135
136 while (__gethostbyname_r (hstname, &th,
137 tmpbuf.data, tmpbuf.length,
138 &h, &herror))
139 {
140 if (herror == NETDB_INTERNAL && errno == ERANGE)
141 {
142 if (!scratch_buffer_grow (&tmpbuf))
143 goto done;
144 }
145 else
146 break;
147 }
148
149 if (h && (c = strchr(h->h_name, '.')))
150 domain = __strdup (++c);
151 else
152 {
153 struct in_addr in_addr;
154
155 in_addr.s_addr = htonl (INADDR_LOOPBACK);
156
157 while (__gethostbyaddr_r ((const char *) &in_addr,
158 sizeof (struct in_addr),
159 AF_INET, &th,
160 tmpbuf.data, tmpbuf.length,
161 &h, &herror))
162 {
163 if (herror == NETDB_INTERNAL && errno == ERANGE)
164 {
165 if (!scratch_buffer_grow (&tmpbuf))
166 goto done;
167 }
168 else
169 break;
170 }
171
172 if (h && (c = strchr (h->h_name, '.')))
173 domain = __strdup (++c);
174 }
175 }
176 }
177 done:
178 scratch_buffer_free (&tmpbuf);
179 }
180
181 __libc_lock_unlock (lock);
182 }
183
184 return domain;
185};
186
187/* Copy a string to a destination buffer with length checking. Return
188 EAI_OVERFLOW if the buffer is not large enough, and 0 on
189 success. */
190static int
191checked_copy (char *dest, size_t destlen, const char *source)
192{
193 size_t source_length = strlen (source);
194 if (source_length + 1 > destlen)
195 return EAI_OVERFLOW;
196 memcpy (dest, source, source_length + 1);
197 return 0;
198}
199
200/* Helper function for CHECKED_SNPRINTF below. */
201static int
202check_sprintf_result (int result, size_t destlen)
203{
204 if (result < 0)
205 return EAI_SYSTEM;
206 if ((size_t) result >= destlen)
207 /* If ret == destlen, there was no room for the terminating NUL
208 character. */
209 return EAI_OVERFLOW;
210 return 0;
211}
212
213/* Format a string in the destination buffer. Return 0 on success,
214 EAI_OVERFLOW in case the buffer is too small, or EAI_SYSTEM on any
215 other error. */
216#define CHECKED_SNPRINTF(dest, destlen, format, ...) \
217 check_sprintf_result \
218 (__snprintf (dest, destlen, format, __VA_ARGS__), destlen)
219
220/* Convert host name, AF_INET/AF_INET6 case, name only. */
221static int
222gni_host_inet_name (struct scratch_buffer *tmpbuf,
223 const struct sockaddr *sa, socklen_t addrlen,
224 char *host, socklen_t hostlen, int flags)
225{
226 int herrno;
227 struct hostent th;
228 struct hostent *h = NULL;
229 if (sa->sa_family == AF_INET6)
230 {
231 const struct sockaddr_in6 *sin6p = (const struct sockaddr_in6 *) sa;
232 while (__gethostbyaddr_r (&sin6p->sin6_addr, sizeof(struct in6_addr),
233 AF_INET6, &th, tmpbuf->data, tmpbuf->length,
234 &h, &herrno))
235 if (herrno == NETDB_INTERNAL && errno == ERANGE)
236 {
237 if (!scratch_buffer_grow (tmpbuf))
238 {
239 __set_h_errno (herrno);
240 return EAI_MEMORY;
241 }
242 }
243 else
244 break;
245 }
246 else
247 {
248 const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
249 while (__gethostbyaddr_r (&sinp->sin_addr, sizeof(struct in_addr),
250 AF_INET, &th, tmpbuf->data, tmpbuf->length,
251 &h, &herrno))
252 if (herrno == NETDB_INTERNAL && errno == ERANGE)
253 {
254 if (!scratch_buffer_grow (tmpbuf))
255 {
256 __set_h_errno (herrno);
257 return EAI_MEMORY;
258 }
259 }
260 else
261 break;
262 }
263
264 if (h == NULL)
265 {
266 if (herrno == NETDB_INTERNAL)
267 {
268 __set_h_errno (herrno);
269 return EAI_SYSTEM;
270 }
271 if (herrno == TRY_AGAIN)
272 {
273 __set_h_errno (herrno);
274 return EAI_AGAIN;
275 }
276 }
277
278 if (h)
279 {
280 char *c;
281 if ((flags & NI_NOFQDN)
282 && (c = nrl_domainname ())
283 && (c = strstr (h->h_name, c))
284 && (c != h->h_name) && (*(--c) == '.'))
285 /* Terminate the string after the prefix. */
286 *c = '\0';
287
288#ifdef HAVE_LIBIDN
289 /* If requested, convert from the IDN format. */
290 if (flags & NI_IDN)
291 {
292 int idn_flags = 0;
293 if (flags & NI_IDN_ALLOW_UNASSIGNED)
294 idn_flags |= IDNA_ALLOW_UNASSIGNED;
295 if (flags & NI_IDN_USE_STD3_ASCII_RULES)
296 idn_flags |= IDNA_USE_STD3_ASCII_RULES;
297
298 char *out;
299 int rc = __idna_to_unicode_lzlz (h->h_name, &out,
300 idn_flags);
301 if (rc != IDNA_SUCCESS)
302 {
303 if (rc == IDNA_MALLOC_ERROR)
304 return EAI_MEMORY;
305 if (rc == IDNA_DLOPEN_ERROR)
306 return EAI_SYSTEM;
307 return EAI_IDN_ENCODE;
308 }
309
310 if (out != h->h_name)
311 {
312 h->h_name = strdupa (out);
313 free (out);
314 }
315 }
316#endif
317
318 size_t len = strlen (h->h_name) + 1;
319 if (len > hostlen)
320 return EAI_OVERFLOW;
321
322 memcpy (host, h->h_name, len);
323
324 return 0;
325 }
326
327 return EAI_NONAME;
328}
329
330/* Convert host name, AF_INET/AF_INET6 case, numeric conversion. */
331static int
332gni_host_inet_numeric (struct scratch_buffer *tmpbuf,
333 const struct sockaddr *sa, socklen_t addrlen,
334 char *host, socklen_t hostlen, int flags)
335{
336 if (sa->sa_family == AF_INET6)
337 {
338 const struct sockaddr_in6 *sin6p = (const struct sockaddr_in6 *) sa;
339 if (inet_ntop (AF_INET6, &sin6p->sin6_addr, host, hostlen) == NULL)
340 return EAI_OVERFLOW;
341
342 uint32_t scopeid = sin6p->sin6_scope_id;
343 if (scopeid != 0)
344 {
345 size_t used_hostlen = __strnlen (host, hostlen);
346 /* Location of the scope string in the host buffer. */
347 char *scope_start = host + used_hostlen;
348 size_t scope_length = hostlen - used_hostlen;
349
350 if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
351 || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
352 {
353 char scopebuf[IFNAMSIZ];
354 if (if_indextoname (scopeid, scopebuf) != NULL)
355 return CHECKED_SNPRINTF
356 (scope_start, scope_length,
357 "%c%s", SCOPE_DELIMITER, scopebuf);
358 }
359 return CHECKED_SNPRINTF
360 (scope_start, scope_length, "%c%u", SCOPE_DELIMITER, scopeid);
361 }
362 }
363 else
364 {
365 const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
366 if (inet_ntop (AF_INET, &sinp->sin_addr, host, hostlen) == NULL)
367 return EAI_OVERFLOW;
368 }
369 return 0;
370}
371
372/* Convert AF_INET or AF_INET6 socket address, host part. */
373static int
374gni_host_inet (struct scratch_buffer *tmpbuf,
375 const struct sockaddr *sa, socklen_t addrlen,
376 char *host, socklen_t hostlen, int flags)
377{
378 if (!(flags & NI_NUMERICHOST))
379 {
380 int result = gni_host_inet_name
381 (tmpbuf, sa, addrlen, host, hostlen, flags);
382 if (result != EAI_NONAME)
383 return result;
384 }
385
386 if (flags & NI_NAMEREQD)
387 return EAI_NONAME;
388 else
389 return gni_host_inet_numeric
390 (tmpbuf, sa, addrlen, host, hostlen, flags);
391}
392
393/* Convert AF_LOCAL socket address, host part. */
394static int
395gni_host_local (struct scratch_buffer *tmpbuf,
396 const struct sockaddr *sa, socklen_t addrlen,
397 char *host, socklen_t hostlen, int flags)
398{
399 if (!(flags & NI_NUMERICHOST))
400 {
401 struct utsname utsname;
402 if (uname (&utsname) == 0)
403 return checked_copy (host, hostlen, utsname.nodename);
404 }
405
406 if (flags & NI_NAMEREQD)
407 return EAI_NONAME;
408
409 return checked_copy (host, hostlen, "localhost");
410}
411
412/* Convert the host part of an AF_LOCAK socket address. */
413static int
414gni_host (struct scratch_buffer *tmpbuf,
415 const struct sockaddr *sa, socklen_t addrlen,
416 char *host, socklen_t hostlen, int flags)
417{
418 switch (sa->sa_family)
419 {
420 case AF_INET:
421 case AF_INET6:
422 return gni_host_inet (tmpbuf, sa, addrlen, host, hostlen, flags);
423
424 case AF_LOCAL:
425 return gni_host_local (tmpbuf, sa, addrlen, host, hostlen, flags);
426
427 default:
428 return EAI_FAMILY;
429 }
430}
431
432/* Convert service to string, AF_INET and AF_INET6 variant. */
433static int
434gni_serv_inet (struct scratch_buffer *tmpbuf,
435 const struct sockaddr *sa, socklen_t addrlen,
436 char *serv, socklen_t servlen, int flags)
437{
438 _Static_assert
439 (offsetof (struct sockaddr_in, sin_port)
440 == offsetof (struct sockaddr_in6, sin6_port)
441 && sizeof (((struct sockaddr_in) {}).sin_port) == sizeof (in_port_t)
442 && sizeof (((struct sockaddr_in6) {}).sin6_port) == sizeof (in_port_t),
443 "AF_INET and AF_INET6 port consistency");
444 const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
445 if (!(flags & NI_NUMERICSERV))
446 {
447 struct servent *s, ts;
448 int e;
449 while ((e = __getservbyport_r (sinp->sin_port,
450 ((flags & NI_DGRAM)
451 ? "udp" : "tcp"), &ts,
452 tmpbuf->data, tmpbuf->length, &s)))
453 {
454 if (e == ERANGE)
455 {
456 if (!scratch_buffer_grow (tmpbuf))
457 return EAI_MEMORY;
458 }
459 else
460 break;
461 }
462 if (s)
463 return checked_copy (serv, servlen, s->s_name);
464 /* Fall through to numeric conversion. */
465 }
466 return CHECKED_SNPRINTF (serv, servlen, "%d", ntohs (sinp->sin_port));
467}
468
469/* Convert service to string, AF_LOCAL variant. */
470static int
471gni_serv_local (struct scratch_buffer *tmpbuf,
472 const struct sockaddr *sa, socklen_t addrlen,
473 char *serv, socklen_t servlen, int flags)
474{
475 return checked_copy
476 (serv, servlen, ((const struct sockaddr_un *) sa)->sun_path);
477}
478
479/* Convert service to string, dispatching to the implementations
480 above. */
481static int
482gni_serv (struct scratch_buffer *tmpbuf,
483 const struct sockaddr *sa, socklen_t addrlen,
484 char *serv, socklen_t servlen, int flags)
485{
486 switch (sa->sa_family)
487 {
488 case AF_INET:
489 case AF_INET6:
490 return gni_serv_inet (tmpbuf, sa, addrlen, serv, servlen, flags);
491 case AF_LOCAL:
492 return gni_serv_local (tmpbuf, sa, addrlen, serv, servlen, flags);
493 default:
494 return EAI_FAMILY;
495 }
496}
497
498int
499getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
500 socklen_t hostlen, char *serv, socklen_t servlen,
501 int flags)
502{
503 if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM
504#ifdef HAVE_LIBIDN
505 |NI_IDN|NI_IDN_ALLOW_UNASSIGNED|NI_IDN_USE_STD3_ASCII_RULES
506#endif
507 ))
508 return EAI_BADFLAGS;
509
510 if (sa == NULL || addrlen < sizeof (sa_family_t))
511 return EAI_FAMILY;
512
513 if ((flags & NI_NAMEREQD) && host == NULL && serv == NULL)
514 return EAI_NONAME;
515
516 switch (sa->sa_family)
517 {
518 case AF_LOCAL:
519 if (addrlen < (socklen_t) offsetof (struct sockaddr_un, sun_path))
520 return EAI_FAMILY;
521 break;
522 case AF_INET:
523 if (addrlen < sizeof (struct sockaddr_in))
524 return EAI_FAMILY;
525 break;
526 case AF_INET6:
527 if (addrlen < sizeof (struct sockaddr_in6))
528 return EAI_FAMILY;
529 break;
530 default:
531 return EAI_FAMILY;
532 }
533
534 struct scratch_buffer tmpbuf;
535 scratch_buffer_init (&tmpbuf);
536
537 if (host != NULL && hostlen > 0)
538 {
539 int result = gni_host (&tmpbuf, sa, addrlen, host, hostlen, flags);
540 if (result != 0)
541 {
542 scratch_buffer_free (&tmpbuf);
543 return result;
544 }
545 }
546
547 if (serv && (servlen > 0))
548 {
549 int result = gni_serv (&tmpbuf, sa, addrlen, serv, servlen, flags);
550 if (result != 0)
551 {
552 scratch_buffer_free (&tmpbuf);
553 return result;
554 }
555 }
556
557 scratch_buffer_free (&tmpbuf);
558 return 0;
559}
560libc_hidden_def (getnameinfo)
561