Updated to fedora-glibc-20070117T0857
[kopensolaris-gnu/glibc.git] / inet / getnameinfo.c
1 /* The Inner Net License, Version 2.00
2
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:
6
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.
22
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.
33
34   If these license terms cause you a real problem, contact the author.  */
35
36 /* This software is Copyright 1996 by Craig Metz, All Rights Reserved.  */
37
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>
54
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
60
61 #ifndef min
62 # define min(x,y) (((x) > (y)) ? (y) : (x))
63 #endif /* min */
64
65 libc_freeres_ptr (static char *domain);
66
67
68 static char *
69 internal_function
70 nrl_domainname (void)
71 {
72   static int not_first;
73
74   if (! not_first)
75     {
76       __libc_lock_define_initialized (static, lock);
77       __libc_lock_lock (lock);
78
79       if (! not_first)
80         {
81           char *c;
82           struct hostent *h, th;
83           size_t tmpbuflen = 1024;
84           char *tmpbuf = alloca (tmpbuflen);
85           int herror;
86
87           not_first = 1;
88
89           while (__gethostbyname_r ("localhost", &th, tmpbuf, tmpbuflen, &h,
90                                     &herror))
91             {
92               if (herror == NETDB_INTERNAL && errno == ERANGE)
93                 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
94               else
95                 break;
96             }
97
98           if (h && (c = strchr (h->h_name, '.')))
99             domain = __strdup (++c);
100           else
101             {
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);
106
107               if ((c = strchr (tmpbuf, '.')))
108                 domain = __strdup (++c);
109               else
110                 {
111                   /* We need to preserve the hostname.  */
112                   const char *hstname = strdupa (tmpbuf);
113
114                   while (__gethostbyname_r (hstname, &th, tmpbuf, tmpbuflen,
115                                             &h, &herror))
116                     {
117                       if (herror == NETDB_INTERNAL && errno == ERANGE)
118                         tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
119                                                 2 * tmpbuflen);
120                       else
121                         break;
122                     }
123
124                   if (h && (c = strchr(h->h_name, '.')))
125                     domain = __strdup (++c);
126                   else
127                     {
128                       struct in_addr in_addr;
129
130                       in_addr.s_addr = htonl (INADDR_LOOPBACK);
131
132                       while (__gethostbyaddr_r ((const char *) &in_addr,
133                                                 sizeof (struct in_addr),
134                                                 AF_INET, &th, tmpbuf,
135                                                 tmpbuflen, &h, &herror))
136                         {
137                           if (herror == NETDB_INTERNAL && errno == ERANGE)
138                             tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
139                                                     2 * tmpbuflen);
140                           else
141                             break;
142                         }
143
144                       if (h && (c = strchr (h->h_name, '.')))
145                         domain = __strdup (++c);
146                     }
147                 }
148             }
149         }
150
151       __libc_lock_unlock (lock);
152     }
153
154   return domain;
155 };
156
157
158 int
159 getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
160              socklen_t hostlen, char *serv, socklen_t servlen,
161              unsigned int flags)
162 {
163   int serrno = errno;
164   int tmpbuflen = 1024;
165   int herrno;
166   char *tmpbuf = alloca (tmpbuflen);
167   struct hostent th;
168   int ok = 0;
169
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
174                 ))
175     return EAI_BADFLAGS;
176
177   if (sa == NULL || addrlen < sizeof (sa_family_t))
178     return EAI_FAMILY;
179
180   switch (sa->sa_family)
181     {
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;
196     }
197
198   if (host != NULL && hostlen > 0)
199     switch (sa->sa_family)
200       {
201       case AF_INET:
202       case AF_INET6:
203         if (!(flags & NI_NUMERICHOST))
204           {
205             struct hostent *h = NULL;
206             if (sa->sa_family == AF_INET6)
207               {
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;
216               }
217             else
218               {
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;
227               }
228
229             if (h == NULL)
230               {
231                 if (herrno == NETDB_INTERNAL)
232                   {
233                     __set_h_errno (herrno);
234                     return EAI_SYSTEM;
235                   }
236                 if (herrno == TRY_AGAIN)
237                   {
238                     __set_h_errno (herrno);
239                     return EAI_AGAIN;
240                   }
241               }
242
243             if (h)
244               {
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';
252
253 #ifdef HAVE_LIBIDN
254                 /* If requested, convert from the IDN format.  */
255                 if (flags & NI_IDN)
256                   {
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;
262
263                     char *out;
264                     int rc = __idna_to_unicode_lzlz (h->h_name, &out,
265                                                      idn_flags);
266                     if (rc != IDNA_SUCCESS)
267                       {
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;
273                       }
274
275                     if (out != h->h_name)
276                       {
277                         h->h_name = strdupa (out);
278                         free (out);
279                       }
280                   }
281 #endif
282
283                 size_t len = strlen (h->h_name) + 1;
284                 if (len > hostlen)
285                   return EAI_OVERFLOW;
286
287                 memcpy (host, h->h_name, len);
288
289                 ok = 1;
290               }
291           }
292
293         if (!ok)
294           {
295             if (flags & NI_NAMEREQD)
296               {
297                 __set_errno (serrno);
298                 return EAI_NONAME;
299               }
300             else
301               {
302                 const char *c;
303                 if (sa->sa_family == AF_INET6)
304                   {
305                     const struct sockaddr_in6 *sin6p;
306                     uint32_t scopeid;
307
308                     sin6p = (const struct sockaddr_in6 *) sa;
309
310                     c = inet_ntop (AF_INET6,
311                                    (const void *) &sin6p->sin6_addr, host, hostlen);
312                     scopeid = sin6p->sin6_scope_id;
313                     if (scopeid != 0)
314                       {
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;
321
322                         scopebuf[0] = SCOPE_DELIMITER;
323                         scopebuf[1] = '\0';
324                         scopeptr = &scopebuf[1];
325
326                         if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
327                             || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
328                           {
329                             if (if_indextoname (scopeid, scopeptr) == NULL)
330                               ++ni_numericscope;
331                             else
332                               scopelen = strlen (scopebuf);
333                           }
334                         else
335                           ++ni_numericscope;
336
337                         if (ni_numericscope)
338                           scopelen = 1 + __snprintf (scopeptr,
339                                                      (scopebuf
340                                                       + sizeof scopebuf
341                                                       - scopeptr),
342                                                      "%u", scopeid);
343
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);
349                       }
350                   }
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;
357               }
358             ok = 1;
359           }
360         break;
361
362       case AF_LOCAL:
363         if (!(flags & NI_NUMERICHOST))
364           {
365             struct utsname utsname;
366
367             if (!uname (&utsname))
368               {
369                 strncpy (host, utsname.nodename, hostlen);
370                 break;
371               };
372           };
373
374         if (flags & NI_NAMEREQD)
375            {
376             __set_errno (serrno);
377             return EAI_NONAME;
378           }
379
380         strncpy (host, "localhost", hostlen);
381         break;
382
383       default:
384         return EAI_FAMILY;
385     }
386
387   if (serv && (servlen > 0))
388     switch (sa->sa_family)
389       {
390       case AF_INET:
391       case AF_INET6:
392         if (!(flags & NI_NUMERICSERV))
393           {
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)))
400               {
401                 if (e == ERANGE)
402                   tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
403                 else
404                   break;
405               }
406             if (s)
407               {
408                 strncpy (serv, s->s_name, servlen);
409                 break;
410               }
411           }
412
413         if (__snprintf (serv, servlen, "%d",
414                         ntohs (((const struct sockaddr_in *) sa)->sin_port))
415             + 1 > servlen)
416           return EAI_OVERFLOW;
417
418         break;
419
420       case AF_LOCAL:
421         strncpy (serv, ((const struct sockaddr_un *) sa)->sun_path, servlen);
422         break;
423     }
424
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;
431 }
432 libc_hidden_def (getnameinfo)