1 /* Convert socket address to string using Name Service Switch modules.
2    Copyright (C) 1997-2021 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 static char *
nrl_domainname(void)87 nrl_domainname (void)
88 {
89   static int not_first;
90 
91   if (! not_first)
92     {
93       __libc_lock_define_initialized (static, lock);
94       __libc_lock_lock (lock);
95 
96       if (! not_first)
97 	{
98 	  char *c;
99 	  struct hostent *h, th;
100 	  int herror;
101 	  struct scratch_buffer tmpbuf;
102 
103 	  scratch_buffer_init (&tmpbuf);
104 	  not_first = 1;
105 
106 	  while (__gethostbyname_r ("localhost", &th,
107 				    tmpbuf.data, tmpbuf.length,
108 				    &h, &herror))
109 	    {
110 	      if (herror == NETDB_INTERNAL && errno == ERANGE)
111 		{
112 		  if (!scratch_buffer_grow (&tmpbuf))
113 		    goto done;
114 		}
115 	      else
116 		break;
117 	    }
118 
119 	  if (h && (c = strchr (h->h_name, '.')))
120 	    domain = __strdup (++c);
121 	  else
122 	    {
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;
128 
129 	      if ((c = strchr (tmpbuf.data, '.')))
130 		domain = __strdup (++c);
131 	      else
132 		{
133 		  /* We need to preserve the hostname.  */
134 		  const char *hstname = strdupa (tmpbuf.data);
135 
136 		  while (__gethostbyname_r (hstname, &th,
137 					    tmpbuf.data, tmpbuf.length,
138 					    &h, &herror))
139 		    {
140 		      if (herror == NETDB_INTERNAL && errno == ERANGE)
141 			{
142 			  if (!scratch_buffer_grow (&tmpbuf))
143 			    goto done;
144 			}
145 		      else
146 			break;
147 		    }
148 
149 		  if (h && (c = strchr(h->h_name, '.')))
150 		    domain = __strdup (++c);
151 		  else
152 		    {
153 		      struct in_addr in_addr;
154 
155 		      in_addr.s_addr = htonl (INADDR_LOOPBACK);
156 
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))
162 			{
163 			  if (herror == NETDB_INTERNAL && errno == ERANGE)
164 			    {
165 			      if (!scratch_buffer_grow (&tmpbuf))
166 				goto done;
167 			    }
168 			  else
169 			    break;
170 			}
171 
172 		      if (h && (c = strchr (h->h_name, '.')))
173 			domain = __strdup (++c);
174 		    }
175 		}
176 	    }
177 	done:
178 	  scratch_buffer_free (&tmpbuf);
179 	}
180 
181       __libc_lock_unlock (lock);
182     }
183 
184   return domain;
185 };
186 
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
checked_copy(char * dest,size_t destlen,const char * source)191 checked_copy (char *dest, size_t destlen, const char *source)
192 {
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;
198 }
199 
200 /* Helper function for CHECKED_SNPRINTF below.  */
201 static int
check_sprintf_result(int result,size_t destlen)202 check_sprintf_result (int result, size_t destlen)
203 {
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;
211 }
212 
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)
219 
220 /* Convert host name, AF_INET/AF_INET6 case, name only.  */
221 static int
gni_host_inet_name(struct scratch_buffer * tmpbuf,const struct sockaddr * sa,socklen_t addrlen,char * host,socklen_t hostlen,int flags)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)
225 {
226   int herrno;
227   struct hostent th;
228   struct hostent *h = NULL;
229   if (sa->sa_family == AF_INET6)
230     {
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)
236 	  {
237 	    if (!scratch_buffer_grow (tmpbuf))
238 	      {
239 		__set_h_errno (herrno);
240 		return EAI_MEMORY;
241 	      }
242 	  }
243 	else
244 	  break;
245     }
246   else
247     {
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)
253 	    {
254 	      if (!scratch_buffer_grow (tmpbuf))
255 		{
256 		  __set_h_errno (herrno);
257 		  return EAI_MEMORY;
258 		}
259 	    }
260 	else
261 	  break;
262     }
263 
264   if (h == NULL)
265     {
266       if (herrno == NETDB_INTERNAL)
267 	{
268 	  __set_h_errno (herrno);
269 	  return EAI_SYSTEM;
270 	}
271       if (herrno == TRY_AGAIN)
272 	{
273 	  __set_h_errno (herrno);
274 	  return EAI_AGAIN;
275 	}
276     }
277 
278   if (h)
279     {
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';
287 
288       /* If requested, convert from the IDN format.  */
289       bool do_idn = flags & NI_IDN;
290       char *h_name;
291       if (do_idn)
292 	{
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;
299 	}
300       if (!do_idn)
301 	h_name = h->h_name;
302 
303       size_t len = strlen (h_name) + 1;
304       if (len > hostlen)
305 	return EAI_OVERFLOW;
306       memcpy (host, h_name, len);
307 
308       if (do_idn)
309 	free (h_name);
310 
311       return 0;
312     }
313 
314   return EAI_NONAME;
315 }
316 
317 /* Convert host name, AF_INET/AF_INET6 case, numeric conversion.  */
318 static int
gni_host_inet_numeric(struct scratch_buffer * tmpbuf,const struct sockaddr * sa,socklen_t addrlen,char * host,socklen_t hostlen,int flags)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)
322 {
323   if (sa->sa_family == AF_INET6)
324     {
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;
328 
329       uint32_t scopeid = sin6p->sin6_scope_id;
330       if (scopeid != 0)
331 	{
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;
336 
337 	  if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
338 	      || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
339 	    {
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);
345 	    }
346 	  return CHECKED_SNPRINTF
347 	    (scope_start, scope_length, "%c%u", SCOPE_DELIMITER, scopeid);
348 	}
349     }
350   else
351     {
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;
355     }
356   return 0;
357 }
358 
359 /* Convert AF_INET or AF_INET6 socket address, host part.  */
360 static int
gni_host_inet(struct scratch_buffer * tmpbuf,const struct sockaddr * sa,socklen_t addrlen,char * host,socklen_t hostlen,int flags)361 gni_host_inet (struct scratch_buffer *tmpbuf,
362 	       const struct sockaddr *sa, socklen_t addrlen,
363 	       char *host, socklen_t hostlen, int flags)
364 {
365   if (!(flags & NI_NUMERICHOST))
366     {
367       int result = gni_host_inet_name
368 	(tmpbuf, sa, addrlen, host, hostlen, flags);
369       if (result != EAI_NONAME)
370 	return result;
371     }
372 
373   if (flags & NI_NAMEREQD)
374     return EAI_NONAME;
375   else
376     return gni_host_inet_numeric
377       (tmpbuf, sa, addrlen, host, hostlen, flags);
378 }
379 
380 /* Convert AF_LOCAL socket address, host part.   */
381 static int
gni_host_local(struct scratch_buffer * tmpbuf,const struct sockaddr * sa,socklen_t addrlen,char * host,socklen_t hostlen,int flags)382 gni_host_local (struct scratch_buffer *tmpbuf,
383 		const struct sockaddr *sa, socklen_t addrlen,
384 		char *host, socklen_t hostlen, int flags)
385 {
386   if (!(flags & NI_NUMERICHOST))
387     {
388       struct utsname utsname;
389       if (uname (&utsname) == 0)
390 	return checked_copy (host, hostlen, utsname.nodename);
391     }
392 
393   if (flags & NI_NAMEREQD)
394     return EAI_NONAME;
395 
396   return checked_copy (host, hostlen, "localhost");
397 }
398 
399 /* Convert the host part of an AF_LOCAK socket address.   */
400 static int
gni_host(struct scratch_buffer * tmpbuf,const struct sockaddr * sa,socklen_t addrlen,char * host,socklen_t hostlen,int flags)401 gni_host (struct scratch_buffer *tmpbuf,
402 	  const struct sockaddr *sa, socklen_t addrlen,
403 	  char *host, socklen_t hostlen, int flags)
404 {
405   switch (sa->sa_family)
406     {
407     case AF_INET:
408     case AF_INET6:
409       return gni_host_inet (tmpbuf, sa, addrlen, host, hostlen, flags);
410 
411     case AF_LOCAL:
412       return gni_host_local (tmpbuf, sa, addrlen, host, hostlen, flags);
413 
414     default:
415       return EAI_FAMILY;
416     }
417 }
418 
419 /* Convert service to string, AF_INET and AF_INET6 variant.  */
420 static int
gni_serv_inet(struct scratch_buffer * tmpbuf,const struct sockaddr * sa,socklen_t addrlen,char * serv,socklen_t servlen,int flags)421 gni_serv_inet (struct scratch_buffer *tmpbuf,
422 	       const struct sockaddr *sa, socklen_t addrlen,
423 	       char *serv, socklen_t servlen, int flags)
424 {
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))
433     {
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)))
440 	{
441 	  if (e == ERANGE)
442 	    {
443 	      if (!scratch_buffer_grow (tmpbuf))
444 		return EAI_MEMORY;
445 	    }
446 	  else
447 	    break;
448 	}
449       if (s)
450 	return checked_copy (serv, servlen, s->s_name);
451       /* Fall through to numeric conversion.  */
452     }
453   return CHECKED_SNPRINTF (serv, servlen, "%d", ntohs (sinp->sin_port));
454 }
455 
456 /* Convert service to string, AF_LOCAL variant.  */
457 static int
gni_serv_local(struct scratch_buffer * tmpbuf,const struct sockaddr * sa,socklen_t addrlen,char * serv,socklen_t servlen,int flags)458 gni_serv_local (struct scratch_buffer *tmpbuf,
459 	       const struct sockaddr *sa, socklen_t addrlen,
460 	       char *serv, socklen_t servlen, int flags)
461 {
462   return checked_copy
463     (serv, servlen, ((const struct sockaddr_un *) sa)->sun_path);
464 }
465 
466 /* Convert service to string, dispatching to the implementations
467    above.  */
468 static int
gni_serv(struct scratch_buffer * tmpbuf,const struct sockaddr * sa,socklen_t addrlen,char * serv,socklen_t servlen,int flags)469 gni_serv (struct scratch_buffer *tmpbuf,
470 	  const struct sockaddr *sa, socklen_t addrlen,
471 	  char *serv, socklen_t servlen, int flags)
472 {
473   switch (sa->sa_family)
474     {
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;
482     }
483 }
484 
485 int
getnameinfo(const struct sockaddr * sa,socklen_t addrlen,char * host,socklen_t hostlen,char * serv,socklen_t servlen,int flags)486 getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
487 	     socklen_t hostlen, char *serv, socklen_t servlen,
488 	     int flags)
489 {
490   if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM
491 		|NI_IDN|DEPRECATED_NI_IDN))
492     return EAI_BADFLAGS;
493 
494   if (sa == NULL || addrlen < sizeof (sa_family_t))
495     return EAI_FAMILY;
496 
497   if ((flags & NI_NAMEREQD) && host == NULL && serv == NULL)
498     return EAI_NONAME;
499 
500   switch (sa->sa_family)
501     {
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;
516     }
517 
518   struct scratch_buffer tmpbuf;
519   scratch_buffer_init (&tmpbuf);
520 
521   if (host != NULL && hostlen > 0)
522     {
523       int result = gni_host (&tmpbuf, sa, addrlen, host, hostlen, flags);
524       if (result != 0)
525 	{
526 	  scratch_buffer_free (&tmpbuf);
527 	  return result;
528 	}
529     }
530 
531   if (serv && (servlen > 0))
532     {
533       int result = gni_serv (&tmpbuf, sa, addrlen, serv, servlen, flags);
534       if (result != 0)
535 	{
536 	  scratch_buffer_free (&tmpbuf);
537 	  return result;
538 	}
539     }
540 
541   scratch_buffer_free (&tmpbuf);
542   return 0;
543 }
544 libc_hidden_def (getnameinfo)
545