2005-12-29 Roland McGrath <roland@frob.com>
[glibc.git] / inet / getnameinfo.c
blob493a423c1089f83434f7298685224070aa09db5b
1 /* The Inner Net License, Version 2.00
3 The author(s) grant permission for redistribution and use in source and
4 binary forms, with or without modification, of the software and documentation
5 provided that the following conditions are met:
7 0. If you receive a version of the software that is specifically labelled
8 as not being for redistribution (check the version message and/or README),
9 you are not permitted to redistribute that version of the software in any
10 way or form.
11 1. All terms of the all other applicable copyrights and licenses must be
12 followed.
13 2. Redistributions of source code must retain the authors' copyright
14 notice(s), this list of conditions, and the following disclaimer.
15 3. Redistributions in binary form must reproduce the authors' copyright
16 notice(s), this list of conditions, and the following disclaimer in the
17 documentation and/or other materials provided with the distribution.
18 4. [The copyright holder has authorized the removal of this clause.]
19 5. Neither the name(s) of the author(s) nor the names of its contributors
20 may be used to endorse or promote products derived from this software
21 without specific prior written permission.
23 THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
24 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
27 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
30 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 If these license terms cause you a real problem, contact the author. */
36 /* This software is Copyright 1996 by Craig Metz, All Rights Reserved. */
38 #include <alloca.h>
39 #include <errno.h>
40 #include <netdb.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <arpa/inet.h>
46 #include <net/if.h>
47 #include <netinet/in.h>
48 #include <sys/param.h>
49 #include <sys/socket.h>
50 #include <sys/types.h>
51 #include <sys/un.h>
52 #include <sys/utsname.h>
53 #include <bits/libc-lock.h>
55 #ifdef HAVE_LIBIDN
56 # include <libidn/idna.h>
57 extern int __idna_to_unicode_lzlz (const char *input, char **output,
58 int flags);
59 #endif
61 #ifndef min
62 # define min(x,y) (((x) > (y)) ? (y) : (x))
63 #endif /* min */
65 libc_freeres_ptr (static char *domain);
68 static char *
69 internal_function
70 nrl_domainname (void)
72 static int not_first;
74 if (! not_first)
76 __libc_lock_define_initialized (static, lock);
77 __libc_lock_lock (lock);
79 if (! not_first)
81 char *c;
82 struct hostent *h, th;
83 size_t tmpbuflen = 1024;
84 char *tmpbuf = alloca (tmpbuflen);
85 int herror;
87 not_first = 1;
89 while (__gethostbyname_r ("localhost", &th, tmpbuf, tmpbuflen, &h,
90 &herror))
92 if (herror == NETDB_INTERNAL && errno == ERANGE)
93 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
94 else
95 break;
98 if (h && (c = strchr (h->h_name, '.')))
99 domain = __strdup (++c);
100 else
102 /* The name contains no domain information. Use the name
103 now to get more information. */
104 while (__gethostname (tmpbuf, tmpbuflen))
105 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
107 if ((c = strchr (tmpbuf, '.')))
108 domain = __strdup (++c);
109 else
111 /* We need to preserve the hostname. */
112 const char *hstname = strdupa (tmpbuf);
114 while (__gethostbyname_r (hstname, &th, tmpbuf, tmpbuflen,
115 &h, &herror))
117 if (herror == NETDB_INTERNAL && errno == ERANGE)
118 tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
119 2 * tmpbuflen);
120 else
121 break;
124 if (h && (c = strchr(h->h_name, '.')))
125 domain = __strdup (++c);
126 else
128 struct in_addr in_addr;
130 in_addr.s_addr = htonl (INADDR_LOOPBACK);
132 while (__gethostbyaddr_r ((const char *) &in_addr,
133 sizeof (struct in_addr),
134 AF_INET, &th, tmpbuf,
135 tmpbuflen, &h, &herror))
137 if (herror == NETDB_INTERNAL && errno == ERANGE)
138 tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
139 2 * tmpbuflen);
140 else
141 break;
144 if (h && (c = strchr (h->h_name, '.')))
145 domain = __strdup (++c);
151 __libc_lock_unlock (lock);
154 return domain;
159 getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
160 socklen_t hostlen, char *serv, socklen_t servlen,
161 unsigned int flags)
163 int serrno = errno;
164 int tmpbuflen = 1024;
165 int herrno;
166 char *tmpbuf = alloca (tmpbuflen);
167 struct hostent th;
168 int ok = 0;
170 if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM
171 #ifdef HAVE_LIBIDN
172 |NI_IDN|NI_IDN_ALLOW_UNASSIGNED|NI_IDN_USE_STD3_ASCII_RULES
173 #endif
175 return EAI_BADFLAGS;
177 if (sa == NULL || addrlen < sizeof (sa_family_t))
178 return EAI_FAMILY;
180 switch (sa->sa_family)
182 case AF_LOCAL:
183 if (addrlen < (socklen_t) (((struct sockaddr_un *) NULL)->sun_path))
184 return EAI_FAMILY;
185 break;
186 case AF_INET:
187 if (addrlen < sizeof (struct sockaddr_in))
188 return EAI_FAMILY;
189 break;
190 case AF_INET6:
191 if (addrlen < sizeof (struct sockaddr_in6))
192 return EAI_FAMILY;
193 break;
194 default:
195 return EAI_FAMILY;
198 if (host != NULL && hostlen > 0)
199 switch (sa->sa_family)
201 case AF_INET:
202 case AF_INET6:
203 if (!(flags & NI_NUMERICHOST))
205 struct hostent *h = NULL;
206 if (h == NULL)
208 if (sa->sa_family == AF_INET6)
210 while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in6 *) sa)->sin6_addr),
211 sizeof(struct in6_addr),
212 AF_INET6, &th, tmpbuf, tmpbuflen,
213 &h, &herrno))
215 if (herrno == NETDB_INTERNAL)
217 if (errno == ERANGE)
218 tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
219 2 * tmpbuflen);
220 else
222 __set_h_errno (herrno);
223 __set_errno (serrno);
224 return EAI_SYSTEM;
227 else
229 break;
233 else
235 while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in *)sa)->sin_addr),
236 sizeof(struct in_addr), AF_INET,
237 &th, tmpbuf, tmpbuflen,
238 &h, &herrno))
240 if (errno == ERANGE)
241 tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
242 2 * tmpbuflen);
243 else
245 break;
251 if (h)
253 char *c;
254 if ((flags & NI_NOFQDN)
255 && (c = nrl_domainname ())
256 && (c = strstr (h->h_name, c))
257 && (c != h->h_name) && (*(--c) == '.'))
258 /* Terminate the string after the prefix. */
259 *c = '\0';
261 #ifdef HAVE_LIBIDN
262 /* If requested, convert from the IDN format. */
263 if (flags & NI_IDN)
265 int idn_flags = 0;
266 if (flags & NI_IDN_ALLOW_UNASSIGNED)
267 idn_flags |= IDNA_ALLOW_UNASSIGNED;
268 if (flags & NI_IDN_USE_STD3_ASCII_RULES)
269 idn_flags |= IDNA_USE_STD3_ASCII_RULES;
271 char *out;
272 int rc = __idna_to_unicode_lzlz (h->h_name, &out,
273 idn_flags);
274 if (rc != IDNA_SUCCESS)
276 if (rc == IDNA_MALLOC_ERROR)
277 return EAI_MEMORY;
278 if (rc == IDNA_DLOPEN_ERROR)
279 return EAI_SYSTEM;
280 return EAI_IDN_ENCODE;
283 if (out != h->h_name)
285 h->h_name = strdupa (out);
286 free (out);
289 #endif
291 size_t len = strlen (h->h_name) + 1;
292 if (len > hostlen)
293 return EAI_OVERFLOW;
295 memcpy (host, h->h_name, len);
297 ok = 1;
301 if (!ok)
303 if (flags & NI_NAMEREQD)
305 __set_errno (serrno);
306 return EAI_NONAME;
308 else
310 const char *c;
311 if (sa->sa_family == AF_INET6)
313 const struct sockaddr_in6 *sin6p;
314 uint32_t scopeid;
316 sin6p = (const struct sockaddr_in6 *) sa;
318 c = inet_ntop (AF_INET6,
319 (const void *) &sin6p->sin6_addr, host, hostlen);
320 scopeid = sin6p->sin6_scope_id;
321 if (scopeid != 0)
323 /* Buffer is >= IFNAMSIZ+1. */
324 char scopebuf[IFNAMSIZ + 1];
325 char *scopeptr;
326 int ni_numericscope = 0;
327 size_t real_hostlen = __strnlen (host, hostlen);
328 size_t scopelen = 0;
330 scopebuf[0] = SCOPE_DELIMITER;
331 scopebuf[1] = '\0';
332 scopeptr = &scopebuf[1];
334 if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
335 || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
337 if (if_indextoname (scopeid, scopeptr) == NULL)
338 ++ni_numericscope;
339 else
340 scopelen = strlen (scopebuf);
342 else
343 ++ni_numericscope;
345 if (ni_numericscope)
346 scopelen = 1 + __snprintf (scopeptr,
347 (scopebuf
348 + sizeof scopebuf
349 - scopeptr),
350 "%u", scopeid);
352 if (real_hostlen + scopelen + 1 > hostlen)
353 /* XXX We should not fail here. Simply enlarge
354 the buffer or return with out of memory. */
355 return EAI_SYSTEM;
356 memcpy (host + real_hostlen, scopebuf, scopelen + 1);
359 else
360 c = inet_ntop (AF_INET,
361 (const void *) &(((const struct sockaddr_in *) sa)->sin_addr),
362 host, hostlen);
363 if (c == NULL)
365 __set_errno (serrno);
366 return EAI_SYSTEM;
369 ok = 1;
371 break;
373 case AF_LOCAL:
374 if (!(flags & NI_NUMERICHOST))
376 struct utsname utsname;
378 if (!uname (&utsname))
380 strncpy (host, utsname.nodename, hostlen);
381 break;
385 if (flags & NI_NAMEREQD)
387 __set_errno (serrno);
388 return EAI_NONAME;
391 strncpy (host, "localhost", hostlen);
392 break;
394 default:
395 return EAI_FAMILY;
398 if (serv && (servlen > 0))
399 switch (sa->sa_family)
401 case AF_INET:
402 case AF_INET6:
403 if (!(flags & NI_NUMERICSERV))
405 struct servent *s, ts;
406 while (__getservbyport_r (((const struct sockaddr_in *) sa)->sin_port,
407 ((flags & NI_DGRAM) ? "udp" : "tcp"),
408 &ts, tmpbuf, tmpbuflen, &s))
410 if (herrno == NETDB_INTERNAL)
412 if (errno == ERANGE)
413 tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
414 2 * tmpbuflen);
415 else
417 __set_errno (serrno);
418 return EAI_SYSTEM;
421 else
423 break;
426 if (s)
428 strncpy (serv, s->s_name, servlen);
429 break;
433 if (__snprintf (serv, servlen, "%d",
434 ntohs (((const struct sockaddr_in *) sa)->sin_port))
435 + 1 > servlen)
436 return EAI_OVERFLOW;
438 break;
440 case AF_LOCAL:
441 strncpy (serv, ((const struct sockaddr_un *) sa)->sun_path, servlen);
442 break;
445 if (host && (hostlen > 0))
446 host[hostlen-1] = 0;
447 if (serv && (servlen > 0))
448 serv[servlen-1] = 0;
449 errno = serrno;
450 return 0;
452 libc_hidden_def (getnameinfo)