Tweak redirection handling in last change.
[glibc.git] / inet / getnameinfo.c
blob50197f86723a480a0a11179c2d0db02cd122fbad
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 <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <arpa/inet.h>
45 #include <net/if.h>
46 #include <netinet/in.h>
47 #include <sys/param.h>
48 #include <sys/socket.h>
49 #include <sys/types.h>
50 #include <sys/un.h>
51 #include <sys/utsname.h>
52 #include <bits/libc-lock.h>
54 #ifndef min
55 # define min(x,y) (((x) > (y)) ? (y) : (x))
56 #endif /* min */
59 static char *
60 internal_function
61 nrl_domainname (void)
63 static char *domain;
64 static int not_first;
66 if (! not_first)
68 __libc_lock_define_initialized (static, lock);
69 __libc_lock_lock (lock);
71 if (! not_first)
73 char *c;
74 struct hostent *h, th;
75 size_t tmpbuflen = 1024;
76 char *tmpbuf = alloca (tmpbuflen);
77 int herror;
79 not_first = 1;
81 while (__gethostbyname_r ("localhost", &th, tmpbuf, tmpbuflen, &h,
82 &herror))
84 if (herror == NETDB_INTERNAL && errno == ERANGE)
85 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
86 else
87 break;
90 if (h && (c = strchr (h->h_name, '.')))
91 domain = __strdup (++c);
92 else
94 /* The name contains no domain information. Use the name
95 now to get more information. */
96 while (__gethostname (tmpbuf, tmpbuflen))
97 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
99 if ((c = strchr (tmpbuf, '.')))
100 domain = __strdup (++c);
101 else
103 /* We need to preserve the hostname. */
104 const char *hstname = strdupa (tmpbuf);
106 while (__gethostbyname_r (hstname, &th, tmpbuf, tmpbuflen,
107 &h, &herror))
109 if (herror == NETDB_INTERNAL && errno == ERANGE)
110 tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
111 2 * tmpbuflen);
112 else
113 break;
116 if (h && (c = strchr(h->h_name, '.')))
117 domain = __strdup (++c);
118 else
120 struct in_addr in_addr;
122 in_addr.s_addr = htonl (INADDR_LOOPBACK);
124 while (__gethostbyaddr_r ((const char *) &in_addr,
125 sizeof (struct in_addr),
126 AF_INET, &th, tmpbuf,
127 tmpbuflen, &h, &herror))
129 if (herror == NETDB_INTERNAL && errno == ERANGE)
130 tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
131 2 * tmpbuflen);
132 else
133 break;
136 if (h && (c = strchr (h->h_name, '.')))
137 domain = __strdup (++c);
143 __libc_lock_unlock (lock);
146 return domain;
151 getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
152 socklen_t hostlen, char *serv, socklen_t servlen,
153 unsigned int flags)
155 int serrno = errno;
156 int tmpbuflen = 1024;
157 int herrno;
158 char *tmpbuf = alloca (tmpbuflen);
159 struct hostent th;
160 int ok = 0;
162 if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM))
163 return EAI_BADFLAGS;
165 if (sa == NULL || addrlen < sizeof (sa_family_t))
166 return EAI_FAMILY;
168 switch (sa->sa_family)
170 case AF_LOCAL:
171 if (addrlen < (socklen_t) (((struct sockaddr_un *) NULL)->sun_path))
172 return EAI_FAMILY;
173 break;
174 case AF_INET:
175 if (addrlen < sizeof (struct sockaddr_in))
176 return EAI_FAMILY;
177 break;
178 case AF_INET6:
179 if (addrlen < sizeof (struct sockaddr_in6))
180 return EAI_FAMILY;
181 break;
182 default:
183 return EAI_FAMILY;
186 if (host != NULL && hostlen > 0)
187 switch (sa->sa_family)
189 case AF_INET:
190 case AF_INET6:
191 if (!(flags & NI_NUMERICHOST))
193 struct hostent *h = NULL;
194 if (h == NULL)
196 if (sa->sa_family == AF_INET6)
198 while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in6 *) sa)->sin6_addr),
199 sizeof(struct in6_addr),
200 AF_INET6, &th, tmpbuf, tmpbuflen,
201 &h, &herrno))
203 if (herrno == NETDB_INTERNAL)
205 if (errno == ERANGE)
206 tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
207 2 * tmpbuflen);
208 else
210 __set_h_errno (herrno);
211 __set_errno (serrno);
212 return EAI_SYSTEM;
215 else
217 break;
221 else
223 while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in *)sa)->sin_addr),
224 sizeof(struct in_addr), AF_INET,
225 &th, tmpbuf, tmpbuflen,
226 &h, &herrno))
228 if (errno == ERANGE)
229 tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
230 2 * tmpbuflen);
231 else
233 break;
239 if (h)
241 char *c;
242 if ((flags & NI_NOFQDN)
243 && (c = nrl_domainname ())
244 && (c = strstr (h->h_name, c))
245 && (c != h->h_name) && (*(--c) == '.'))
247 strncpy (host, h->h_name,
248 min(hostlen, (size_t) (c - h->h_name)));
249 host[min(hostlen - 1, (size_t) (c - h->h_name))]
250 = '\0';
251 ok = 1;
253 else
255 strncpy (host, h->h_name, hostlen);
256 ok = 1;
261 if (!ok)
263 if (flags & NI_NAMEREQD)
265 __set_errno (serrno);
266 return EAI_NONAME;
268 else
270 const char *c;
271 if (sa->sa_family == AF_INET6)
273 const struct sockaddr_in6 *sin6p;
274 uint32_t scopeid;
276 sin6p = (const struct sockaddr_in6 *) sa;
278 c = inet_ntop (AF_INET6,
279 (const void *) &sin6p->sin6_addr, host, hostlen);
280 scopeid = sin6p->sin6_scope_id;
281 if (scopeid != 0)
283 /* Buffer is >= IFNAMSIZ+1. */
284 char scopebuf[IFNAMSIZ + 1];
285 char *scopeptr;
286 int ni_numericscope = 0;
287 size_t real_hostlen = __strnlen (host, hostlen);
288 size_t scopelen = 0;
290 scopebuf[0] = SCOPE_DELIMITER;
291 scopebuf[1] = '\0';
292 scopeptr = &scopebuf[1];
294 if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
295 || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
297 if (if_indextoname (scopeid, scopeptr) == NULL)
298 ++ni_numericscope;
299 else
300 scopelen = strlen (scopebuf);
302 else
303 ++ni_numericscope;
305 if (ni_numericscope)
306 scopelen = 1 + __snprintf (scopeptr,
307 (scopebuf
308 + sizeof scopebuf
309 - scopeptr),
310 "%u", scopeid);
312 if (real_hostlen + scopelen + 1 > hostlen)
313 /* XXX We should not fail here. Simply enlarge
314 the buffer or return with out of memory. */
315 return EAI_SYSTEM;
316 memcpy (host + real_hostlen, scopebuf, scopelen + 1);
319 else
320 c = inet_ntop (AF_INET,
321 (const void *) &(((const struct sockaddr_in *) sa)->sin_addr),
322 host, hostlen);
323 if (c == NULL)
325 __set_errno (serrno);
326 return EAI_SYSTEM;
329 ok = 1;
331 break;
333 case AF_LOCAL:
334 if (!(flags & NI_NUMERICHOST))
336 struct utsname utsname;
338 if (!uname (&utsname))
340 strncpy (host, utsname.nodename, hostlen);
341 break;
345 if (flags & NI_NAMEREQD)
347 __set_errno (serrno);
348 return EAI_NONAME;
351 strncpy (host, "localhost", hostlen);
352 break;
354 default:
355 return EAI_FAMILY;
358 if (serv && (servlen > 0))
359 switch (sa->sa_family)
361 case AF_INET:
362 case AF_INET6:
363 if (!(flags & NI_NUMERICSERV))
365 struct servent *s, ts;
366 while (__getservbyport_r (((const struct sockaddr_in *) sa)->sin_port,
367 ((flags & NI_DGRAM) ? "udp" : "tcp"),
368 &ts, tmpbuf, tmpbuflen, &s))
370 if (herrno == NETDB_INTERNAL)
372 if (errno == ERANGE)
373 tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
374 2 * tmpbuflen);
375 else
377 __set_errno (serrno);
378 return EAI_SYSTEM;
381 else
383 break;
386 if (s)
388 strncpy (serv, s->s_name, servlen);
389 break;
392 __snprintf (serv, servlen, "%d",
393 ntohs (((const struct sockaddr_in *) sa)->sin_port));
394 break;
396 case AF_LOCAL:
397 strncpy (serv, ((const struct sockaddr_un *) sa)->sun_path, servlen);
398 break;
401 if (host && (hostlen > 0))
402 host[hostlen-1] = 0;
403 if (serv && (servlen > 0))
404 serv[servlen-1] = 0;
405 errno = serrno;
406 return 0;
408 libc_hidden_def (getnameinfo)