(getnameinfo): Return error in case argument buffers overflow.
[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 <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>
53
54 #ifdef HAVE_LIBIDN
55 # include <libidn/idna.h>
56 extern int __idna_to_unicode_lzlz (const char *input, char **output,
57                                    int flags);
58 #endif
59
60 #ifndef min
61 # define min(x,y) (((x) > (y)) ? (y) : (x))
62 #endif /* min */
63
64 libc_freeres_ptr (static char *domain);
65
66
67 static char *
68 internal_function
69 nrl_domainname (void)
70 {
71   static int not_first;
72
73   if (! not_first)
74     {
75       __libc_lock_define_initialized (static, lock);
76       __libc_lock_lock (lock);
77
78       if (! not_first)
79         {
80           char *c;
81           struct hostent *h, th;
82           size_t tmpbuflen = 1024;
83           char *tmpbuf = alloca (tmpbuflen);
84           int herror;
85
86           not_first = 1;
87
88           while (__gethostbyname_r ("localhost", &th, tmpbuf, tmpbuflen, &h,
89                                     &herror))
90             {
91               if (herror == NETDB_INTERNAL && errno == ERANGE)
92                 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
93               else
94                 break;
95             }
96
97           if (h && (c = strchr (h->h_name, '.')))
98             domain = __strdup (++c);
99           else
100             {
101               /* The name contains no domain information.  Use the name
102                  now to get more information.  */
103               while (__gethostname (tmpbuf, tmpbuflen))
104                 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
105
106               if ((c = strchr (tmpbuf, '.')))
107                 domain = __strdup (++c);
108               else
109                 {
110                   /* We need to preserve the hostname.  */
111                   const char *hstname = strdupa (tmpbuf);
112
113                   while (__gethostbyname_r (hstname, &th, tmpbuf, tmpbuflen,
114                                             &h, &herror))
115                     {
116                       if (herror == NETDB_INTERNAL && errno == ERANGE)
117                         tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
118                                                 2 * tmpbuflen);
119                       else
120                         break;
121                     }
122
123                   if (h && (c = strchr(h->h_name, '.')))
124                     domain = __strdup (++c);
125                   else
126                     {
127                       struct in_addr in_addr;
128
129                       in_addr.s_addr = htonl (INADDR_LOOPBACK);
130
131                       while (__gethostbyaddr_r ((const char *) &in_addr,
132                                                 sizeof (struct in_addr),
133                                                 AF_INET, &th, tmpbuf,
134                                                 tmpbuflen, &h, &herror))
135                         {
136                           if (herror == NETDB_INTERNAL && errno == ERANGE)
137                             tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
138                                                     2 * tmpbuflen);
139                           else
140                             break;
141                         }
142
143                       if (h && (c = strchr (h->h_name, '.')))
144                         domain = __strdup (++c);
145                     }
146                 }
147             }
148         }
149
150       __libc_lock_unlock (lock);
151     }
152
153   return domain;
154 };
155
156
157 int
158 getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
159              socklen_t hostlen, char *serv, socklen_t servlen,
160              unsigned int flags)
161 {
162   int serrno = errno;
163   int tmpbuflen = 1024;
164   int herrno;
165   char *tmpbuf = alloca (tmpbuflen);
166   struct hostent th;
167   int ok = 0;
168
169   if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM
170 #ifdef HAVE_LIBIDN
171                 |NI_IDN
172 #endif
173                 ))
174     return EAI_BADFLAGS;
175
176   if (sa == NULL || addrlen < sizeof (sa_family_t))
177     return EAI_FAMILY;
178
179   switch (sa->sa_family)
180     {
181     case AF_LOCAL:
182       if (addrlen < (socklen_t) (((struct sockaddr_un *) NULL)->sun_path))
183         return EAI_FAMILY;
184       break;
185     case AF_INET:
186       if (addrlen < sizeof (struct sockaddr_in))
187         return EAI_FAMILY;
188       break;
189     case AF_INET6:
190       if (addrlen < sizeof (struct sockaddr_in6))
191         return EAI_FAMILY;
192       break;
193     default:
194       return EAI_FAMILY;
195     }
196
197   if (host != NULL && hostlen > 0)
198     switch (sa->sa_family)
199       {
200       case AF_INET:
201       case AF_INET6:
202         if (!(flags & NI_NUMERICHOST))
203           {
204             struct hostent *h = NULL;
205             if (h == NULL)
206               {
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                       {
214                         if (herrno == NETDB_INTERNAL)
215                           {
216                             if (errno == ERANGE)
217                               tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
218                                                       2 * tmpbuflen);
219                             else
220                               {
221                                 __set_h_errno (herrno);
222                                 __set_errno (serrno);
223                                 return EAI_SYSTEM;
224                               }
225                           }
226                         else
227                           {
228                             break;
229                           }
230                       }
231                   }
232                 else
233                   {
234                     while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in *)sa)->sin_addr),
235                                               sizeof(struct in_addr), AF_INET,
236                                               &th, tmpbuf, tmpbuflen,
237                                               &h, &herrno))
238                       {
239                         if (errno == ERANGE)
240                           tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
241                                                   2 * tmpbuflen);
242                         else
243                           {
244                             break;
245                           }
246                       }
247                   }
248               }
249
250             if (h)
251               {
252                 char *c;
253                 if ((flags & NI_NOFQDN)
254                     && (c = nrl_domainname ())
255                     && (c = strstr (h->h_name, c))
256                     && (c != h->h_name) && (*(--c) == '.'))
257                   /* Terminate the string after the prefix.  */
258                   *c = '\0';
259
260 #ifdef HAVE_LIBIDN
261                 /* If requested, convert from the IDN format.  */
262                 if (flags & NI_IDN)
263                   {
264                     char *out;
265                     int rc = __idna_to_unicode_lzlz (h->h_name, &out, 0);
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                   {
357                     __set_errno (serrno);
358                     return EAI_SYSTEM;
359                   }
360               }
361             ok = 1;
362           }
363         break;
364
365       case AF_LOCAL:
366         if (!(flags & NI_NUMERICHOST))
367           {
368             struct utsname utsname;
369
370             if (!uname (&utsname))
371               {
372                 strncpy (host, utsname.nodename, hostlen);
373                 break;
374               };
375           };
376
377         if (flags & NI_NAMEREQD)
378            {
379             __set_errno (serrno);
380             return EAI_NONAME;
381           }
382
383         strncpy (host, "localhost", hostlen);
384         break;
385
386       default:
387         return EAI_FAMILY;
388     }
389
390   if (serv && (servlen > 0))
391     switch (sa->sa_family)
392       {
393       case AF_INET:
394       case AF_INET6:
395         if (!(flags & NI_NUMERICSERV))
396           {
397             struct servent *s, ts;
398             while (__getservbyport_r (((const struct sockaddr_in *) sa)->sin_port,
399                                       ((flags & NI_DGRAM) ? "udp" : "tcp"),
400                                       &ts, tmpbuf, tmpbuflen, &s))
401               {
402                 if (herrno == NETDB_INTERNAL)
403                   {
404                     if (errno == ERANGE)
405                       tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
406                                               2 * tmpbuflen);
407                     else
408                       {
409                         __set_errno (serrno);
410                         return EAI_SYSTEM;
411                       }
412                   }
413                 else
414                   {
415                     break;
416                   }
417               }
418             if (s)
419               {
420                 strncpy (serv, s->s_name, servlen);
421                 break;
422               }
423           }
424
425         if (__snprintf (serv, servlen, "%d",
426                         ntohs (((const struct sockaddr_in *) sa)->sin_port))
427             + 1 > servlen)
428           return EAI_OVERFLOW;
429
430         break;
431
432       case AF_LOCAL:
433         strncpy (serv, ((const struct sockaddr_un *) sa)->sun_path, servlen);
434         break;
435     }
436
437   if (host && (hostlen > 0))
438     host[hostlen-1] = 0;
439   if (serv && (servlen > 0))
440     serv[servlen-1] = 0;
441   errno = serrno;
442   return 0;
443 }
444 libc_hidden_def (getnameinfo)