(rfc3484_sort): Implement rule 4, home addresses.
[glibc/history.git] / inet / getnameinfo.c
blobb7b2b151b216c312fd062baa6eff34b9813ade01
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 (sa->sa_family == AF_INET6)
208 while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in6 *) sa)->sin6_addr),
209 sizeof(struct in6_addr),
210 AF_INET6, &th, tmpbuf, tmpbuflen,
211 &h, &herrno))
212 if (herrno == NETDB_INTERNAL && errno == ERANGE)
213 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
214 else
215 break;
217 else
219 while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in *)sa)->sin_addr),
220 sizeof(struct in_addr), AF_INET,
221 &th, tmpbuf, tmpbuflen,
222 &h, &herrno))
223 if (herrno == NETDB_INTERNAL && errno == ERANGE)
224 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
225 else
226 break;
229 if (h == NULL)
231 if (herrno == NETDB_INTERNAL)
233 __set_h_errno (herrno);
234 return EAI_SYSTEM;
236 if (herrno == TRY_AGAIN)
238 __set_h_errno (herrno);
239 return EAI_AGAIN;
243 if (h)
245 char *c;
246 if ((flags & NI_NOFQDN)
247 && (c = nrl_domainname ())
248 && (c = strstr (h->h_name, c))
249 && (c != h->h_name) && (*(--c) == '.'))
250 /* Terminate the string after the prefix. */
251 *c = '\0';
253 #ifdef HAVE_LIBIDN
254 /* If requested, convert from the IDN format. */
255 if (flags & NI_IDN)
257 int idn_flags = 0;
258 if (flags & NI_IDN_ALLOW_UNASSIGNED)
259 idn_flags |= IDNA_ALLOW_UNASSIGNED;
260 if (flags & NI_IDN_USE_STD3_ASCII_RULES)
261 idn_flags |= IDNA_USE_STD3_ASCII_RULES;
263 char *out;
264 int rc = __idna_to_unicode_lzlz (h->h_name, &out,
265 idn_flags);
266 if (rc != IDNA_SUCCESS)
268 if (rc == IDNA_MALLOC_ERROR)
269 return EAI_MEMORY;
270 if (rc == IDNA_DLOPEN_ERROR)
271 return EAI_SYSTEM;
272 return EAI_IDN_ENCODE;
275 if (out != h->h_name)
277 h->h_name = strdupa (out);
278 free (out);
281 #endif
283 size_t len = strlen (h->h_name) + 1;
284 if (len > hostlen)
285 return EAI_OVERFLOW;
287 memcpy (host, h->h_name, len);
289 ok = 1;
293 if (!ok)
295 if (flags & NI_NAMEREQD)
297 __set_errno (serrno);
298 return EAI_NONAME;
300 else
302 const char *c;
303 if (sa->sa_family == AF_INET6)
305 const struct sockaddr_in6 *sin6p;
306 uint32_t scopeid;
308 sin6p = (const struct sockaddr_in6 *) sa;
310 c = inet_ntop (AF_INET6,
311 (const void *) &sin6p->sin6_addr, host, hostlen);
312 scopeid = sin6p->sin6_scope_id;
313 if (scopeid != 0)
315 /* Buffer is >= IFNAMSIZ+1. */
316 char scopebuf[IFNAMSIZ + 1];
317 char *scopeptr;
318 int ni_numericscope = 0;
319 size_t real_hostlen = __strnlen (host, hostlen);
320 size_t scopelen = 0;
322 scopebuf[0] = SCOPE_DELIMITER;
323 scopebuf[1] = '\0';
324 scopeptr = &scopebuf[1];
326 if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
327 || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
329 if (if_indextoname (scopeid, scopeptr) == NULL)
330 ++ni_numericscope;
331 else
332 scopelen = strlen (scopebuf);
334 else
335 ++ni_numericscope;
337 if (ni_numericscope)
338 scopelen = 1 + __snprintf (scopeptr,
339 (scopebuf
340 + sizeof scopebuf
341 - scopeptr),
342 "%u", scopeid);
344 if (real_hostlen + scopelen + 1 > hostlen)
345 /* XXX We should not fail here. Simply enlarge
346 the buffer or return with out of memory. */
347 return EAI_SYSTEM;
348 memcpy (host + real_hostlen, scopebuf, scopelen + 1);
351 else
352 c = inet_ntop (AF_INET,
353 (const void *) &(((const struct sockaddr_in *) sa)->sin_addr),
354 host, hostlen);
355 if (c == NULL)
356 return EAI_SYSTEM;
358 ok = 1;
360 break;
362 case AF_LOCAL:
363 if (!(flags & NI_NUMERICHOST))
365 struct utsname utsname;
367 if (!uname (&utsname))
369 strncpy (host, utsname.nodename, hostlen);
370 break;
374 if (flags & NI_NAMEREQD)
376 __set_errno (serrno);
377 return EAI_NONAME;
380 strncpy (host, "localhost", hostlen);
381 break;
383 default:
384 return EAI_FAMILY;
387 if (serv && (servlen > 0))
388 switch (sa->sa_family)
390 case AF_INET:
391 case AF_INET6:
392 if (!(flags & NI_NUMERICSERV))
394 struct servent *s, ts;
395 int e;
396 while ((e = __getservbyport_r (((const struct sockaddr_in *) sa)->sin_port,
397 ((flags & NI_DGRAM)
398 ? "udp" : "tcp"),
399 &ts, tmpbuf, tmpbuflen, &s)))
401 if (e == ERANGE)
402 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
403 else
404 break;
406 if (s)
408 strncpy (serv, s->s_name, servlen);
409 break;
413 if (__snprintf (serv, servlen, "%d",
414 ntohs (((const struct sockaddr_in *) sa)->sin_port))
415 + 1 > servlen)
416 return EAI_OVERFLOW;
418 break;
420 case AF_LOCAL:
421 strncpy (serv, ((const struct sockaddr_un *) sa)->sun_path, servlen);
422 break;
425 if (host && (hostlen > 0))
426 host[hostlen-1] = 0;
427 if (serv && (servlen > 0))
428 serv[servlen-1] = 0;
429 errno = serrno;
430 return 0;
432 libc_hidden_def (getnameinfo)