fdwalk should return 0 on an empty directory
[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 <stddef.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <arpa/inet.h>
47 #include <net/if.h>
48 #include <netinet/in.h>
49 #include <sys/param.h>
50 #include <sys/socket.h>
51 #include <sys/types.h>
52 #include <sys/un.h>
53 #include <sys/utsname.h>
54 #include <bits/libc-lock.h>
55
56 #ifdef HAVE_LIBIDN
57 # include <libidn/idna.h>
58 extern int __idna_to_unicode_lzlz (const char *input, char **output,
59                                    int flags);
60 #endif
61
62 #ifndef min
63 # define min(x,y) (((x) > (y)) ? (y) : (x))
64 #endif /* min */
65
66 libc_freeres_ptr (static char *domain);
67
68
69 static char *
70 internal_function
71 nrl_domainname (void)
72 {
73   static int not_first;
74
75   if (! not_first)
76     {
77       __libc_lock_define_initialized (static, lock);
78       __libc_lock_lock (lock);
79
80       if (! not_first)
81         {
82           char *c;
83           struct hostent *h, th;
84           size_t tmpbuflen = 1024;
85           char *tmpbuf = alloca (tmpbuflen);
86           int herror;
87
88           not_first = 1;
89
90           while (__gethostbyname_r ("localhost", &th, tmpbuf, tmpbuflen, &h,
91                                     &herror))
92             {
93               if (herror == NETDB_INTERNAL && errno == ERANGE)
94                 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
95               else
96                 break;
97             }
98
99           if (h && (c = strchr (h->h_name, '.')))
100             domain = __strdup (++c);
101           else
102             {
103               /* The name contains no domain information.  Use the name
104                  now to get more information.  */
105               while (__gethostname (tmpbuf, tmpbuflen))
106                 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
107
108               if ((c = strchr (tmpbuf, '.')))
109                 domain = __strdup (++c);
110               else
111                 {
112                   /* We need to preserve the hostname.  */
113                   const char *hstname = strdupa (tmpbuf);
114
115                   while (__gethostbyname_r (hstname, &th, tmpbuf, tmpbuflen,
116                                             &h, &herror))
117                     {
118                       if (herror == NETDB_INTERNAL && errno == ERANGE)
119                         tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
120                                                 2 * tmpbuflen);
121                       else
122                         break;
123                     }
124
125                   if (h && (c = strchr(h->h_name, '.')))
126                     domain = __strdup (++c);
127                   else
128                     {
129                       struct in_addr in_addr;
130
131                       in_addr.s_addr = htonl (INADDR_LOOPBACK);
132
133                       while (__gethostbyaddr_r ((const char *) &in_addr,
134                                                 sizeof (struct in_addr),
135                                                 AF_INET, &th, tmpbuf,
136                                                 tmpbuflen, &h, &herror))
137                         {
138                           if (herror == NETDB_INTERNAL && errno == ERANGE)
139                             tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
140                                                     2 * tmpbuflen);
141                           else
142                             break;
143                         }
144
145                       if (h && (c = strchr (h->h_name, '.')))
146                         domain = __strdup (++c);
147                     }
148                 }
149             }
150         }
151
152       __libc_lock_unlock (lock);
153     }
154
155   return domain;
156 };
157
158
159 int
160 getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
161              socklen_t hostlen, char *serv, socklen_t servlen,
162              unsigned int flags)
163 {
164   int serrno = errno;
165   int tmpbuflen = 1024;
166   int herrno;
167   char *tmpbuf = alloca (tmpbuflen);
168   struct hostent th;
169   int ok = 0;
170
171   if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM
172 #ifdef HAVE_LIBIDN
173                 |NI_IDN|NI_IDN_ALLOW_UNASSIGNED|NI_IDN_USE_STD3_ASCII_RULES
174 #endif
175                 ))
176     return EAI_BADFLAGS;
177
178   if (sa == NULL || addrlen < sizeof (sa_family_t))
179     return EAI_FAMILY;
180
181   switch (sa->sa_family)
182     {
183     case AF_LOCAL:
184       if (addrlen < (socklen_t) offsetof (struct sockaddr_un, sun_path))
185         return EAI_FAMILY;
186       break;
187     case AF_INET:
188       if (addrlen < sizeof (struct sockaddr_in))
189         return EAI_FAMILY;
190       break;
191     case AF_INET6:
192       if (addrlen < sizeof (struct sockaddr_in6))
193         return EAI_FAMILY;
194       break;
195     default:
196       return EAI_FAMILY;
197     }
198
199   if (host != NULL && hostlen > 0)
200     switch (sa->sa_family)
201       {
202       case AF_INET:
203       case AF_INET6:
204         if (!(flags & NI_NUMERICHOST))
205           {
206             struct hostent *h = NULL;
207             if (sa->sa_family == AF_INET6)
208               {
209                 while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in6 *) sa)->sin6_addr),
210                                           sizeof(struct in6_addr),
211                                           AF_INET6, &th, tmpbuf, tmpbuflen,
212                                           &h, &herrno))
213                   if (herrno == NETDB_INTERNAL && errno == ERANGE)
214                     tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
215                   else
216                     break;
217               }
218             else
219               {
220                 while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in *)sa)->sin_addr),
221                                           sizeof(struct in_addr), AF_INET,
222                                           &th, tmpbuf, tmpbuflen,
223                                           &h, &herrno))
224                   if (herrno == NETDB_INTERNAL && errno == ERANGE)
225                     tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
226                   else
227                     break;
228               }
229
230             if (h == NULL)
231               {
232                 if (herrno == NETDB_INTERNAL)
233                   {
234                     __set_h_errno (herrno);
235                     return EAI_SYSTEM;
236                   }
237                 if (herrno == TRY_AGAIN)
238                   {
239                     __set_h_errno (herrno);
240                     return EAI_AGAIN;
241                   }
242               }
243
244             if (h)
245               {
246                 char *c;
247                 if ((flags & NI_NOFQDN)
248                     && (c = nrl_domainname ())
249                     && (c = strstr (h->h_name, c))
250                     && (c != h->h_name) && (*(--c) == '.'))
251                   /* Terminate the string after the prefix.  */
252                   *c = '\0';
253
254 #ifdef HAVE_LIBIDN
255                 /* If requested, convert from the IDN format.  */
256                 if (flags & NI_IDN)
257                   {
258                     int idn_flags = 0;
259                     if  (flags & NI_IDN_ALLOW_UNASSIGNED)
260                       idn_flags |= IDNA_ALLOW_UNASSIGNED;
261                     if (flags & NI_IDN_USE_STD3_ASCII_RULES)
262                       idn_flags |= IDNA_USE_STD3_ASCII_RULES;
263
264                     char *out;
265                     int rc = __idna_to_unicode_lzlz (h->h_name, &out,
266                                                      idn_flags);
267                     if (rc != IDNA_SUCCESS)
268                       {
269                         if (rc == IDNA_MALLOC_ERROR)
270                           return EAI_MEMORY;
271                         if (rc == IDNA_DLOPEN_ERROR)
272                           return EAI_SYSTEM;
273                         return EAI_IDN_ENCODE;
274                       }
275
276                     if (out != h->h_name)
277                       {
278                         h->h_name = strdupa (out);
279                         free (out);
280                       }
281                   }
282 #endif
283
284                 size_t len = strlen (h->h_name) + 1;
285                 if (len > hostlen)
286                   return EAI_OVERFLOW;
287
288                 memcpy (host, h->h_name, len);
289
290                 ok = 1;
291               }
292           }
293
294         if (!ok)
295           {
296             if (flags & NI_NAMEREQD)
297               {
298                 __set_errno (serrno);
299                 return EAI_NONAME;
300               }
301             else
302               {
303                 const char *c;
304                 if (sa->sa_family == AF_INET6)
305                   {
306                     const struct sockaddr_in6 *sin6p;
307                     uint32_t scopeid;
308
309                     sin6p = (const struct sockaddr_in6 *) sa;
310
311                     c = inet_ntop (AF_INET6,
312                                    (const void *) &sin6p->sin6_addr, host, hostlen);
313                     scopeid = sin6p->sin6_scope_id;
314                     if (scopeid != 0)
315                       {
316                         /* Buffer is >= IFNAMSIZ+1.  */
317                         char scopebuf[IFNAMSIZ + 1];
318                         char *scopeptr;
319                         int ni_numericscope = 0;
320                         size_t real_hostlen = __strnlen (host, hostlen);
321                         size_t scopelen = 0;
322
323                         scopebuf[0] = SCOPE_DELIMITER;
324                         scopebuf[1] = '\0';
325                         scopeptr = &scopebuf[1];
326
327                         if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
328                             || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
329                           {
330                             if (if_indextoname (scopeid, scopeptr) == NULL)
331                               ++ni_numericscope;
332                             else
333                               scopelen = strlen (scopebuf);
334                           }
335                         else
336                           ++ni_numericscope;
337
338                         if (ni_numericscope)
339                           scopelen = 1 + __snprintf (scopeptr,
340                                                      (scopebuf
341                                                       + sizeof scopebuf
342                                                       - scopeptr),
343                                                      "%u", scopeid);
344
345                         if (real_hostlen + scopelen + 1 > hostlen)
346                           /* XXX We should not fail here.  Simply enlarge
347                              the buffer or return with out of memory.  */
348                           return EAI_SYSTEM;
349                         memcpy (host + real_hostlen, scopebuf, scopelen + 1);
350                       }
351                   }
352                 else
353                   c = inet_ntop (AF_INET,
354                                  (const void *) &(((const struct sockaddr_in *) sa)->sin_addr),
355                                  host, hostlen);
356                 if (c == NULL)
357                   return EAI_SYSTEM;
358               }
359             ok = 1;
360           }
361         break;
362
363       case AF_LOCAL:
364         if (!(flags & NI_NUMERICHOST))
365           {
366             struct utsname utsname;
367
368             if (!uname (&utsname))
369               {
370                 strncpy (host, utsname.nodename, hostlen);
371                 break;
372               };
373           };
374
375         if (flags & NI_NAMEREQD)
376            {
377             __set_errno (serrno);
378             return EAI_NONAME;
379           }
380
381         strncpy (host, "localhost", hostlen);
382         break;
383
384       default:
385         return EAI_FAMILY;
386     }
387
388   if (serv && (servlen > 0))
389     switch (sa->sa_family)
390       {
391       case AF_INET:
392       case AF_INET6:
393         if (!(flags & NI_NUMERICSERV))
394           {
395             struct servent *s, ts;
396             int e;
397             while ((e = __getservbyport_r (((const struct sockaddr_in *) sa)->sin_port,
398                                            ((flags & NI_DGRAM)
399                                             ? "udp" : "tcp"),
400                                            &ts, tmpbuf, tmpbuflen, &s)))
401               {
402                 if (e == ERANGE)
403                   tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
404                 else
405                   break;
406               }
407             if (s)
408               {
409                 strncpy (serv, s->s_name, servlen);
410                 break;
411               }
412           }
413
414         if (__snprintf (serv, servlen, "%d",
415                         ntohs (((const struct sockaddr_in *) sa)->sin_port))
416             + 1 > servlen)
417           return EAI_OVERFLOW;
418
419         break;
420
421       case AF_LOCAL:
422         strncpy (serv, ((const struct sockaddr_un *) sa)->sun_path, servlen);
423         break;
424     }
425
426   if (host && (hostlen > 0))
427     host[hostlen-1] = 0;
428   if (serv && (servlen > 0))
429     serv[servlen-1] = 0;
430   errno = serrno;
431   return 0;
432 }
433 libc_hidden_def (getnameinfo)