x86-64: Skip zero length in __mem[pcpy|move|set]_erms
[glibc.git] / inet / getnameinfo.c
blob5d4978e38367446c175fd55405b6f007a2ef37e6
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.
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.
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.
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/>. */
19 /* The Inner Net License, Version 2.00
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:
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.
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.
52 If these license terms cause you a real problem, contact the author. */
54 /* This software is Copyright 1996 by Craig Metz, All Rights Reserved. */
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>
76 #ifndef min
77 # define min(x,y) (((x) > (y)) ? (y) : (x))
78 #endif /* min */
80 libc_freeres_ptr (static char *domain);
82 /* Former NI_IDN_ALLOW_UNASSIGNED, NI_IDN_USE_STD3_ASCII_RULES flags,
83 now ignored. */
84 #define DEPRECATED_NI_IDN 192
86 static char *
87 nrl_domainname (void)
89 static int not_first;
91 if (! not_first)
93 __libc_lock_define_initialized (static, lock);
94 __libc_lock_lock (lock);
96 if (! not_first)
98 char *c;
99 struct hostent *h, th;
100 int herror;
101 struct scratch_buffer tmpbuf;
103 scratch_buffer_init (&tmpbuf);
104 not_first = 1;
106 while (__gethostbyname_r ("localhost", &th,
107 tmpbuf.data, tmpbuf.length,
108 &h, &herror))
110 if (herror == NETDB_INTERNAL && errno == ERANGE)
112 if (!scratch_buffer_grow (&tmpbuf))
113 goto done;
115 else
116 break;
119 if (h && (c = strchr (h->h_name, '.')))
120 domain = __strdup (++c);
121 else
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;
129 if ((c = strchr (tmpbuf.data, '.')))
130 domain = __strdup (++c);
131 else
133 /* We need to preserve the hostname. */
134 const char *hstname = strdupa (tmpbuf.data);
136 while (__gethostbyname_r (hstname, &th,
137 tmpbuf.data, tmpbuf.length,
138 &h, &herror))
140 if (herror == NETDB_INTERNAL && errno == ERANGE)
142 if (!scratch_buffer_grow (&tmpbuf))
143 goto done;
145 else
146 break;
149 if (h && (c = strchr(h->h_name, '.')))
150 domain = __strdup (++c);
151 else
153 struct in_addr in_addr;
155 in_addr.s_addr = htonl (INADDR_LOOPBACK);
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))
163 if (herror == NETDB_INTERNAL && errno == ERANGE)
165 if (!scratch_buffer_grow (&tmpbuf))
166 goto done;
168 else
169 break;
172 if (h && (c = strchr (h->h_name, '.')))
173 domain = __strdup (++c);
177 done:
178 scratch_buffer_free (&tmpbuf);
181 __libc_lock_unlock (lock);
184 return domain;
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. */
190 static int
191 checked_copy (char *dest, size_t destlen, const char *source)
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;
200 /* Helper function for CHECKED_SNPRINTF below. */
201 static int
202 check_sprintf_result (int result, size_t destlen)
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;
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)
220 /* Convert host name, AF_INET/AF_INET6 case, name only. */
221 static int
222 gni_host_inet_name (struct scratch_buffer *tmpbuf,
223 const struct sockaddr *sa, socklen_t addrlen,
224 char *host, socklen_t hostlen, int flags)
226 int herrno;
227 struct hostent th;
228 struct hostent *h = NULL;
229 if (sa->sa_family == AF_INET6)
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)
237 if (!scratch_buffer_grow (tmpbuf))
239 __set_h_errno (herrno);
240 return EAI_MEMORY;
243 else
244 break;
246 else
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)
254 if (!scratch_buffer_grow (tmpbuf))
256 __set_h_errno (herrno);
257 return EAI_MEMORY;
260 else
261 break;
264 if (h == NULL)
266 if (herrno == NETDB_INTERNAL)
268 __set_h_errno (herrno);
269 return EAI_SYSTEM;
271 if (herrno == TRY_AGAIN)
273 __set_h_errno (herrno);
274 return EAI_AGAIN;
278 if (h)
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';
288 /* If requested, convert from the IDN format. */
289 bool do_idn = flags & NI_IDN;
290 char *h_name;
291 if (do_idn)
293 int rc = __idna_from_dns_encoding (h->h_name, &h_name);
294 if (rc == EAI_IDN_ENCODE)
295 /* Use the punycode name as a fallback. */
296 do_idn = false;
297 else if (rc != 0)
298 return rc;
300 if (!do_idn)
301 h_name = h->h_name;
303 size_t len = strlen (h_name) + 1;
304 if (len > hostlen)
305 return EAI_OVERFLOW;
306 memcpy (host, h_name, len);
308 if (do_idn)
309 free (h_name);
311 return 0;
314 return EAI_NONAME;
317 /* Convert host name, AF_INET/AF_INET6 case, numeric conversion. */
318 static int
319 gni_host_inet_numeric (struct scratch_buffer *tmpbuf,
320 const struct sockaddr *sa, socklen_t addrlen,
321 char *host, socklen_t hostlen, int flags)
323 if (sa->sa_family == AF_INET6)
325 const struct sockaddr_in6 *sin6p = (const struct sockaddr_in6 *) sa;
326 if (inet_ntop (AF_INET6, &sin6p->sin6_addr, host, hostlen) == NULL)
327 return EAI_OVERFLOW;
329 uint32_t scopeid = sin6p->sin6_scope_id;
330 if (scopeid != 0)
332 size_t used_hostlen = __strnlen (host, hostlen);
333 /* Location of the scope string in the host buffer. */
334 char *scope_start = host + used_hostlen;
335 size_t scope_length = hostlen - used_hostlen;
337 if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
338 || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
340 char scopebuf[IFNAMSIZ];
341 if (if_indextoname (scopeid, scopebuf) != NULL)
342 return CHECKED_SNPRINTF
343 (scope_start, scope_length,
344 "%c%s", SCOPE_DELIMITER, scopebuf);
346 return CHECKED_SNPRINTF
347 (scope_start, scope_length, "%c%u", SCOPE_DELIMITER, scopeid);
350 else
352 const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
353 if (inet_ntop (AF_INET, &sinp->sin_addr, host, hostlen) == NULL)
354 return EAI_OVERFLOW;
356 return 0;
359 /* Convert AF_INET or AF_INET6 socket address, host part. */
360 static int
361 gni_host_inet (struct scratch_buffer *tmpbuf,
362 const struct sockaddr *sa, socklen_t addrlen,
363 char *host, socklen_t hostlen, int flags)
365 if (!(flags & NI_NUMERICHOST))
367 int result = gni_host_inet_name
368 (tmpbuf, sa, addrlen, host, hostlen, flags);
369 if (result != EAI_NONAME)
370 return result;
373 if (flags & NI_NAMEREQD)
374 return EAI_NONAME;
375 else
376 return gni_host_inet_numeric
377 (tmpbuf, sa, addrlen, host, hostlen, flags);
380 /* Convert AF_LOCAL socket address, host part. */
381 static int
382 gni_host_local (struct scratch_buffer *tmpbuf,
383 const struct sockaddr *sa, socklen_t addrlen,
384 char *host, socklen_t hostlen, int flags)
386 if (!(flags & NI_NUMERICHOST))
388 struct utsname utsname;
389 if (uname (&utsname) == 0)
390 return checked_copy (host, hostlen, utsname.nodename);
393 if (flags & NI_NAMEREQD)
394 return EAI_NONAME;
396 return checked_copy (host, hostlen, "localhost");
399 /* Convert the host part of an AF_LOCAK socket address. */
400 static int
401 gni_host (struct scratch_buffer *tmpbuf,
402 const struct sockaddr *sa, socklen_t addrlen,
403 char *host, socklen_t hostlen, int flags)
405 switch (sa->sa_family)
407 case AF_INET:
408 case AF_INET6:
409 return gni_host_inet (tmpbuf, sa, addrlen, host, hostlen, flags);
411 case AF_LOCAL:
412 return gni_host_local (tmpbuf, sa, addrlen, host, hostlen, flags);
414 default:
415 return EAI_FAMILY;
419 /* Convert service to string, AF_INET and AF_INET6 variant. */
420 static int
421 gni_serv_inet (struct scratch_buffer *tmpbuf,
422 const struct sockaddr *sa, socklen_t addrlen,
423 char *serv, socklen_t servlen, int flags)
425 _Static_assert
426 (offsetof (struct sockaddr_in, sin_port)
427 == offsetof (struct sockaddr_in6, sin6_port)
428 && sizeof (((struct sockaddr_in) {}).sin_port) == sizeof (in_port_t)
429 && sizeof (((struct sockaddr_in6) {}).sin6_port) == sizeof (in_port_t),
430 "AF_INET and AF_INET6 port consistency");
431 const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
432 if (!(flags & NI_NUMERICSERV))
434 struct servent *s, ts;
435 int e;
436 while ((e = __getservbyport_r (sinp->sin_port,
437 ((flags & NI_DGRAM)
438 ? "udp" : "tcp"), &ts,
439 tmpbuf->data, tmpbuf->length, &s)))
441 if (e == ERANGE)
443 if (!scratch_buffer_grow (tmpbuf))
444 return EAI_MEMORY;
446 else
447 break;
449 if (s)
450 return checked_copy (serv, servlen, s->s_name);
451 /* Fall through to numeric conversion. */
453 return CHECKED_SNPRINTF (serv, servlen, "%d", ntohs (sinp->sin_port));
456 /* Convert service to string, AF_LOCAL variant. */
457 static int
458 gni_serv_local (struct scratch_buffer *tmpbuf,
459 const struct sockaddr *sa, socklen_t addrlen,
460 char *serv, socklen_t servlen, int flags)
462 return checked_copy
463 (serv, servlen, ((const struct sockaddr_un *) sa)->sun_path);
466 /* Convert service to string, dispatching to the implementations
467 above. */
468 static int
469 gni_serv (struct scratch_buffer *tmpbuf,
470 const struct sockaddr *sa, socklen_t addrlen,
471 char *serv, socklen_t servlen, int flags)
473 switch (sa->sa_family)
475 case AF_INET:
476 case AF_INET6:
477 return gni_serv_inet (tmpbuf, sa, addrlen, serv, servlen, flags);
478 case AF_LOCAL:
479 return gni_serv_local (tmpbuf, sa, addrlen, serv, servlen, flags);
480 default:
481 return EAI_FAMILY;
486 getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
487 socklen_t hostlen, char *serv, socklen_t servlen,
488 int flags)
490 if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM
491 |NI_IDN|DEPRECATED_NI_IDN))
492 return EAI_BADFLAGS;
494 if (sa == NULL || addrlen < sizeof (sa_family_t))
495 return EAI_FAMILY;
497 if ((flags & NI_NAMEREQD) && host == NULL && serv == NULL)
498 return EAI_NONAME;
500 switch (sa->sa_family)
502 case AF_LOCAL:
503 if (addrlen < (socklen_t) offsetof (struct sockaddr_un, sun_path))
504 return EAI_FAMILY;
505 break;
506 case AF_INET:
507 if (addrlen < sizeof (struct sockaddr_in))
508 return EAI_FAMILY;
509 break;
510 case AF_INET6:
511 if (addrlen < sizeof (struct sockaddr_in6))
512 return EAI_FAMILY;
513 break;
514 default:
515 return EAI_FAMILY;
518 struct scratch_buffer tmpbuf;
519 scratch_buffer_init (&tmpbuf);
521 if (host != NULL && hostlen > 0)
523 int result = gni_host (&tmpbuf, sa, addrlen, host, hostlen, flags);
524 if (result != 0)
526 scratch_buffer_free (&tmpbuf);
527 return result;
531 if (serv && (servlen > 0))
533 int result = gni_serv (&tmpbuf, sa, addrlen, serv, servlen, flags);
534 if (result != 0)
536 scratch_buffer_free (&tmpbuf);
537 return result;
541 scratch_buffer_free (&tmpbuf);
542 return 0;
544 libc_hidden_def (getnameinfo)