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