1/* Convert socket address to string using Name Service Switch modules.
2 Copyright (C) 1997-2024 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 <https://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#include <inet/net-internal.h>
75#include <set-freeres.h>
76
77#ifndef min
78# define min(x,y) (((x) > (y)) ? (y) : (x))
79#endif /* min */
80
81static char *domain;
82
83/* Former NI_IDN_ALLOW_UNASSIGNED, NI_IDN_USE_STD3_ASCII_RULES flags,
84 now ignored. */
85#define DEPRECATED_NI_IDN 192
86
87/* Return true if no memory allocation failure happened (even if domain
88 name could not be obtained) or false otherwise. */
89static bool
90nrl_domainname_core (struct scratch_buffer *tmpbuf)
91{
92 char *c;
93 struct hostent *h, th;
94 int herror;
95
96 while (__gethostbyname_r (name: "localhost", result_buf: &th, buf: tmpbuf->data, buflen: tmpbuf->length,
97 result: &h, h_errnop: &herror))
98 {
99 if (herror == NETDB_INTERNAL && errno == ERANGE)
100 {
101 if (!scratch_buffer_grow (buffer: tmpbuf))
102 return false;
103 }
104 else
105 break;
106 }
107
108 if (h != NULL && (c = strchr (h->h_name, '.')) != NULL)
109 {
110 domain = __strdup (++c);
111 return domain != NULL;
112 }
113
114 /* The name contains no domain information. Use the name
115 now to get more information. */
116 while (__gethostname (name: tmpbuf->data, len: tmpbuf->length))
117 if (!scratch_buffer_grow (buffer: tmpbuf))
118 return false;
119
120 if ((c = strchr (tmpbuf->data, '.')) != NULL)
121 {
122 domain = __strdup (++c);
123 return domain != NULL;
124 }
125
126 /* We need to preserve the hostname. */
127 size_t hstnamelen = strlen (tmpbuf->data) + 1;
128 while (__gethostbyname_r (name: tmpbuf->data, result_buf: &th, buf: tmpbuf->data + hstnamelen,
129 buflen: tmpbuf->length - hstnamelen, result: &h, h_errnop: &herror))
130 {
131 if (herror == NETDB_INTERNAL && errno == ERANGE)
132 {
133 if (!scratch_buffer_grow_preserve (buffer: tmpbuf))
134 return false;
135 }
136 else
137 break;
138 }
139
140 if (h != NULL && (c = strchr(h->h_name, '.')) != NULL)
141 {
142 domain = __strdup (++c);
143 return domain != NULL;
144 }
145
146 struct in_addr in_addr = { .s_addr = htonl (INADDR_LOOPBACK) };
147
148 while (__gethostbyaddr_r (addr: (const char *) &in_addr, len: sizeof (struct in_addr),
149 AF_INET, result_buf: &th, buf: tmpbuf->data, buflen: tmpbuf->length, result: &h,
150 h_errnop: &herror))
151 {
152 if (herror == NETDB_INTERNAL && errno == ERANGE)
153 {
154 if (!scratch_buffer_grow (buffer: tmpbuf))
155 return false;
156 }
157 else
158 break;
159 }
160
161 if (h != NULL && (c = strchr (h->h_name, '.')) != NULL)
162 {
163 domain = __strdup (++c);
164 return domain != NULL;
165 }
166 return true;
167}
168
169static bool
170nrl_domainname (void)
171{
172 static int not_first;
173
174 if (__glibc_likely (atomic_load_acquire (&not_first) != 0))
175 return true;
176
177 int r = true;
178
179 __libc_lock_define_initialized (static, lock);
180 __libc_lock_lock (lock);
181
182 if (atomic_load_relaxed (&not_first) == 0)
183 {
184 struct scratch_buffer tmpbuf;
185 scratch_buffer_init (buffer: &tmpbuf);
186
187 if ((r = nrl_domainname_core (tmpbuf: &tmpbuf)))
188 atomic_store_release (&not_first, 1);
189
190 scratch_buffer_free (buffer: &tmpbuf);
191 }
192
193 __libc_lock_unlock (lock);
194
195 return r;
196};
197
198/* Copy a string to a destination buffer with length checking. Return
199 EAI_OVERFLOW if the buffer is not large enough, and 0 on
200 success. */
201static int
202checked_copy (char *dest, size_t destlen, const char *source)
203{
204 size_t source_length = strlen (source);
205 if (source_length + 1 > destlen)
206 return EAI_OVERFLOW;
207 memcpy (dest, source, source_length + 1);
208 return 0;
209}
210
211/* Helper function for CHECKED_SNPRINTF below. */
212static int
213check_sprintf_result (int result, size_t destlen)
214{
215 if (result < 0)
216 return EAI_SYSTEM;
217 if ((size_t) result >= destlen)
218 /* If ret == destlen, there was no room for the terminating NUL
219 character. */
220 return EAI_OVERFLOW;
221 return 0;
222}
223
224/* Format a string in the destination buffer. Return 0 on success,
225 EAI_OVERFLOW in case the buffer is too small, or EAI_SYSTEM on any
226 other error. */
227#define CHECKED_SNPRINTF(dest, destlen, format, ...) \
228 check_sprintf_result \
229 (__snprintf (dest, destlen, format, __VA_ARGS__), destlen)
230
231/* Convert host name, AF_INET/AF_INET6 case, name only. */
232static int
233gni_host_inet_name (struct scratch_buffer *tmpbuf,
234 const struct sockaddr *sa, socklen_t addrlen,
235 char *host, socklen_t hostlen, int flags)
236{
237 int herrno;
238 struct hostent th;
239 struct hostent *h = NULL;
240 if (sa->sa_family == AF_INET6)
241 {
242 const struct sockaddr_in6 *sin6p = (const struct sockaddr_in6 *) sa;
243 while (__gethostbyaddr_r (addr: &sin6p->sin6_addr, len: sizeof(struct in6_addr),
244 AF_INET6, result_buf: &th, buf: tmpbuf->data, buflen: tmpbuf->length,
245 result: &h, h_errnop: &herrno))
246 if (herrno == NETDB_INTERNAL && errno == ERANGE)
247 {
248 if (!scratch_buffer_grow (buffer: tmpbuf))
249 {
250 __set_h_errno (herrno);
251 return EAI_MEMORY;
252 }
253 }
254 else
255 break;
256 }
257 else
258 {
259 const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
260 while (__gethostbyaddr_r (addr: &sinp->sin_addr, len: sizeof(struct in_addr),
261 AF_INET, result_buf: &th, buf: tmpbuf->data, buflen: tmpbuf->length,
262 result: &h, h_errnop: &herrno))
263 if (herrno == NETDB_INTERNAL && errno == ERANGE)
264 {
265 if (!scratch_buffer_grow (buffer: tmpbuf))
266 {
267 __set_h_errno (herrno);
268 return EAI_MEMORY;
269 }
270 }
271 else
272 break;
273 }
274
275 if (h == NULL)
276 {
277 if (herrno == NETDB_INTERNAL)
278 {
279 __set_h_errno (herrno);
280 return EAI_SYSTEM;
281 }
282 if (herrno == TRY_AGAIN)
283 {
284 __set_h_errno (herrno);
285 return EAI_AGAIN;
286 }
287 }
288
289 if (h)
290 {
291 if (flags & NI_NOFQDN)
292 {
293 if (!nrl_domainname ())
294 return EAI_MEMORY;
295
296 char *c = domain;
297 if (c != NULL && (c = strstr (h->h_name, c))
298 && (c != h->h_name) && (*(--c) == '.'))
299 /* Terminate the string after the prefix. */
300 *c = '\0';
301 }
302
303 /* If requested, convert from the IDN format. */
304 bool do_idn = flags & NI_IDN;
305 char *h_name;
306 if (do_idn)
307 {
308 int rc = __idna_from_dns_encoding (h->h_name, &h_name);
309 if (rc == EAI_IDN_ENCODE)
310 /* Use the punycode name as a fallback. */
311 do_idn = false;
312 else if (rc != 0)
313 return rc;
314 }
315 if (!do_idn)
316 h_name = h->h_name;
317
318 size_t len = strlen (h_name) + 1;
319 if (len > hostlen)
320 return EAI_OVERFLOW;
321 memcpy (host, h_name, len);
322
323 if (do_idn)
324 free (ptr: h_name);
325
326 return 0;
327 }
328
329 return EAI_NONAME;
330}
331
332/* Convert host name, AF_INET/AF_INET6 case, numeric conversion. */
333static int
334gni_host_inet_numeric (struct scratch_buffer *tmpbuf,
335 const struct sockaddr *sa, socklen_t addrlen,
336 char *host, socklen_t hostlen, int flags)
337{
338 if (sa->sa_family == AF_INET6)
339 {
340 const struct sockaddr_in6 *sin6p = (const struct sockaddr_in6 *) sa;
341 if (inet_ntop (AF_INET6, &sin6p->sin6_addr, host, hostlen) == NULL)
342 return EAI_OVERFLOW;
343
344 uint32_t scopeid = sin6p->sin6_scope_id;
345 if (scopeid != 0)
346 {
347 size_t used_hostlen = __strnlen (host, hostlen);
348 /* Location of the scope string in the host buffer. */
349 char *scope_start = host + used_hostlen;
350 size_t scope_length = hostlen - used_hostlen;
351
352 if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
353 || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
354 {
355 char scopebuf[IFNAMSIZ];
356 if (if_indextoname (scopeid, scopebuf) != NULL)
357 return CHECKED_SNPRINTF
358 (scope_start, scope_length,
359 "%c%s", SCOPE_DELIMITER, scopebuf);
360 }
361 return CHECKED_SNPRINTF
362 (scope_start, scope_length, "%c%u", SCOPE_DELIMITER, scopeid);
363 }
364 }
365 else
366 {
367 const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
368 if (inet_ntop (AF_INET, &sinp->sin_addr, host, hostlen) == NULL)
369 return EAI_OVERFLOW;
370 }
371 return 0;
372}
373
374/* Convert AF_INET or AF_INET6 socket address, host part. */
375static int
376gni_host_inet (struct scratch_buffer *tmpbuf,
377 const struct sockaddr *sa, socklen_t addrlen,
378 char *host, socklen_t hostlen, int flags)
379{
380 if (!(flags & NI_NUMERICHOST))
381 {
382 int result = gni_host_inet_name
383 (tmpbuf, sa, addrlen, host, hostlen, flags);
384 if (result != EAI_NONAME)
385 return result;
386 }
387
388 if (flags & NI_NAMEREQD)
389 return EAI_NONAME;
390 else
391 return gni_host_inet_numeric
392 (tmpbuf, sa, addrlen, host, hostlen, flags);
393}
394
395/* Convert AF_LOCAL socket address, host part. */
396static int
397gni_host_local (struct scratch_buffer *tmpbuf,
398 const struct sockaddr *sa, socklen_t addrlen,
399 char *host, socklen_t hostlen, int flags)
400{
401 if (!(flags & NI_NUMERICHOST))
402 {
403 struct utsname utsname;
404 if (uname (&utsname) == 0)
405 return checked_copy (dest: host, destlen: hostlen, source: utsname.nodename);
406 }
407
408 if (flags & NI_NAMEREQD)
409 return EAI_NONAME;
410
411 return checked_copy (dest: host, destlen: hostlen, source: "localhost");
412}
413
414/* Convert the host part of an AF_LOCAK socket address. */
415static int
416gni_host (struct scratch_buffer *tmpbuf,
417 const struct sockaddr *sa, socklen_t addrlen,
418 char *host, socklen_t hostlen, int flags)
419{
420 switch (sa->sa_family)
421 {
422 case AF_INET:
423 case AF_INET6:
424 return gni_host_inet (tmpbuf, sa, addrlen, host, hostlen, flags);
425
426 case AF_LOCAL:
427 return gni_host_local (tmpbuf, sa, addrlen, host, hostlen, flags);
428
429 default:
430 return EAI_FAMILY;
431 }
432}
433
434/* Convert service to string, AF_INET and AF_INET6 variant. */
435static int
436gni_serv_inet (struct scratch_buffer *tmpbuf,
437 const struct sockaddr *sa, socklen_t addrlen,
438 char *serv, socklen_t servlen, int flags)
439{
440 _Static_assert
441 (offsetof (struct sockaddr_in, sin_port)
442 == offsetof (struct sockaddr_in6, sin6_port)
443 && sizeof (((struct sockaddr_in) {}).sin_port) == sizeof (in_port_t)
444 && sizeof (((struct sockaddr_in6) {}).sin6_port) == sizeof (in_port_t),
445 "AF_INET and AF_INET6 port consistency");
446 const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
447 if (!(flags & NI_NUMERICSERV))
448 {
449 struct servent *s, ts;
450 int e;
451 while ((e = __getservbyport_r (port: sinp->sin_port,
452 proto: ((flags & NI_DGRAM)
453 ? "udp" : "tcp"), result_buf: &ts,
454 buf: tmpbuf->data, buflen: tmpbuf->length, result: &s)))
455 {
456 if (e == ERANGE)
457 {
458 if (!scratch_buffer_grow (buffer: tmpbuf))
459 return EAI_MEMORY;
460 }
461 else
462 break;
463 }
464 if (s)
465 return checked_copy (dest: serv, destlen: servlen, source: s->s_name);
466 /* Fall through to numeric conversion. */
467 }
468 return CHECKED_SNPRINTF (serv, servlen, "%d", ntohs (sinp->sin_port));
469}
470
471/* Convert service to string, AF_LOCAL variant. */
472static int
473gni_serv_local (struct scratch_buffer *tmpbuf,
474 const struct sockaddr *sa, socklen_t addrlen,
475 char *serv, socklen_t servlen, int flags)
476{
477 return checked_copy
478 (dest: serv, destlen: servlen, source: ((const struct sockaddr_un *) sa)->sun_path);
479}
480
481/* Convert service to string, dispatching to the implementations
482 above. */
483static int
484gni_serv (struct scratch_buffer *tmpbuf,
485 const struct sockaddr *sa, socklen_t addrlen,
486 char *serv, socklen_t servlen, int flags)
487{
488 switch (sa->sa_family)
489 {
490 case AF_INET:
491 case AF_INET6:
492 return gni_serv_inet (tmpbuf, sa, addrlen, serv, servlen, flags);
493 case AF_LOCAL:
494 return gni_serv_local (tmpbuf, sa, addrlen, serv, servlen, flags);
495 default:
496 return EAI_FAMILY;
497 }
498}
499
500int
501getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
502 socklen_t hostlen, char *serv, socklen_t servlen,
503 int flags)
504{
505 if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM
506 |NI_IDN|DEPRECATED_NI_IDN))
507 return EAI_BADFLAGS;
508
509 if (sa == NULL || addrlen < sizeof (sa_family_t))
510 return EAI_FAMILY;
511
512 if ((flags & NI_NAMEREQD) && host == NULL && serv == NULL)
513 return EAI_NONAME;
514
515 switch (sa->sa_family)
516 {
517 case AF_LOCAL:
518 if (addrlen < (socklen_t) offsetof (struct sockaddr_un, sun_path))
519 return EAI_FAMILY;
520 break;
521 case AF_INET:
522 if (addrlen < sizeof (struct sockaddr_in))
523 return EAI_FAMILY;
524 break;
525 case AF_INET6:
526 if (addrlen < sizeof (struct sockaddr_in6))
527 return EAI_FAMILY;
528 break;
529 default:
530 return EAI_FAMILY;
531 }
532
533 struct scratch_buffer tmpbuf;
534 scratch_buffer_init (buffer: &tmpbuf);
535
536 if (host != NULL && hostlen > 0)
537 {
538 int result = gni_host (tmpbuf: &tmpbuf, sa, addrlen, host, hostlen, flags);
539 if (result != 0)
540 {
541 scratch_buffer_free (buffer: &tmpbuf);
542 return result;
543 }
544 }
545
546 if (serv && (servlen > 0))
547 {
548 int result = gni_serv (tmpbuf: &tmpbuf, sa, addrlen, serv, servlen, flags);
549 if (result != 0)
550 {
551 scratch_buffer_free (buffer: &tmpbuf);
552 return result;
553 }
554 }
555
556 scratch_buffer_free (buffer: &tmpbuf);
557 return 0;
558}
559libc_hidden_def (getnameinfo)
560
561weak_alias (domain, __libc_getnameinfo_freemem_ptr)
562

source code of glibc/nss/getnameinfo.c