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