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 (&not_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 (&not_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 (&not_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