2006-01-31 Roland McGrath <roland@redhat.com>
[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 (h == NULL)
207               {
208                 if (sa->sa_family == AF_INET6)
209                   {
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))
214                       {
215                         if (herrno == NETDB_INTERNAL)
216                           {
217                             if (errno == ERANGE)
218                               tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
219                                                       2 * tmpbuflen);
220                             else
221                               {
222                                 __set_h_errno (herrno);
223                                 __set_errno (serrno);
224                                 return EAI_SYSTEM;
225                               }
226                           }
227                         else
228                           {
229                             break;
230                           }
231                       }
232                   }
233                 else
234                   {
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))
239                       {
240                         if (errno == ERANGE)
241                           tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
242                                                   2 * tmpbuflen);
243                         else
244                           {
245                             break;
246                           }
247                       }
248                   }
249               }
250
251             if (h)
252               {
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';
260
261 #ifdef HAVE_LIBIDN
262                 /* If requested, convert from the IDN format.  */
263                 if (flags & NI_IDN)
264                   {
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;
270
271                     char *out;
272                     int rc = __idna_to_unicode_lzlz (h->h_name, &out,
273                                                      idn_flags);
274                     if (rc != IDNA_SUCCESS)
275                       {
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;
281                       }
282
283                     if (out != h->h_name)
284                       {
285                         h->h_name = strdupa (out);
286                         free (out);
287                       }
288                   }
289 #endif
290
291                 size_t len = strlen (h->h_name) + 1;
292                 if (len > hostlen)
293                   return EAI_OVERFLOW;
294
295                 memcpy (host, h->h_name, len);
296
297                 ok = 1;
298               }
299           }
300
301         if (!ok)
302           {
303             if (flags & NI_NAMEREQD)
304               {
305                 __set_errno (serrno);
306                 return EAI_NONAME;
307               }
308             else
309               {
310                 const char *c;
311                 if (sa->sa_family == AF_INET6)
312                   {
313                     const struct sockaddr_in6 *sin6p;
314                     uint32_t scopeid;
315
316                     sin6p = (const struct sockaddr_in6 *) sa;
317
318                     c = inet_ntop (AF_INET6,
319                                    (const void *) &sin6p->sin6_addr, host, hostlen);
320                     scopeid = sin6p->sin6_scope_id;
321                     if (scopeid != 0)
322                       {
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;
329
330                         scopebuf[0] = SCOPE_DELIMITER;
331                         scopebuf[1] = '\0';
332                         scopeptr = &scopebuf[1];
333
334                         if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
335                             || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
336                           {
337                             if (if_indextoname (scopeid, scopeptr) == NULL)
338                               ++ni_numericscope;
339                             else
340                               scopelen = strlen (scopebuf);
341                           }
342                         else
343                           ++ni_numericscope;
344
345                         if (ni_numericscope)
346                           scopelen = 1 + __snprintf (scopeptr,
347                                                      (scopebuf
348                                                       + sizeof scopebuf
349                                                       - scopeptr),
350                                                      "%u", scopeid);
351
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);
357                       }
358                   }
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)
364                   {
365                     __set_errno (serrno);
366                     return EAI_SYSTEM;
367                   }
368               }
369             ok = 1;
370           }
371         break;
372
373       case AF_LOCAL:
374         if (!(flags & NI_NUMERICHOST))
375           {
376             struct utsname utsname;
377
378             if (!uname (&utsname))
379               {
380                 strncpy (host, utsname.nodename, hostlen);
381                 break;
382               };
383           };
384
385         if (flags & NI_NAMEREQD)
386            {
387             __set_errno (serrno);
388             return EAI_NONAME;
389           }
390
391         strncpy (host, "localhost", hostlen);
392         break;
393
394       default:
395         return EAI_FAMILY;
396     }
397
398   if (serv && (servlen > 0))
399     switch (sa->sa_family)
400       {
401       case AF_INET:
402       case AF_INET6:
403         if (!(flags & NI_NUMERICSERV))
404           {
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))
409               {
410                 if (herrno == NETDB_INTERNAL)
411                   {
412                     if (errno == ERANGE)
413                       tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
414                                               2 * tmpbuflen);
415                     else
416                       {
417                         __set_errno (serrno);
418                         return EAI_SYSTEM;
419                       }
420                   }
421                 else
422                   {
423                     break;
424                   }
425               }
426             if (s)
427               {
428                 strncpy (serv, s->s_name, servlen);
429                 break;
430               }
431           }
432
433         if (__snprintf (serv, servlen, "%d",
434                         ntohs (((const struct sockaddr_in *) sa)->sin_port))
435             + 1 > servlen)
436           return EAI_OVERFLOW;
437
438         break;
439
440       case AF_LOCAL:
441         strncpy (serv, ((const struct sockaddr_un *) sa)->sun_path, servlen);
442         break;
443     }
444
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;
451 }
452 libc_hidden_def (getnameinfo)