Fix error code for too small input buffer to getnameinfo
[platform/upstream/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              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   if ((flags & NI_NAMEREQD) && host == NULL && serv == NULL)
182     return EAI_NONAME;
183
184   switch (sa->sa_family)
185     {
186     case AF_LOCAL:
187       if (addrlen < (socklen_t) offsetof (struct sockaddr_un, sun_path))
188         return EAI_FAMILY;
189       break;
190     case AF_INET:
191       if (addrlen < sizeof (struct sockaddr_in))
192         return EAI_FAMILY;
193       break;
194     case AF_INET6:
195       if (addrlen < sizeof (struct sockaddr_in6))
196         return EAI_FAMILY;
197       break;
198     default:
199       return EAI_FAMILY;
200     }
201
202   if (host != NULL && hostlen > 0)
203     switch (sa->sa_family)
204       {
205       case AF_INET:
206       case AF_INET6:
207         if (!(flags & NI_NUMERICHOST))
208           {
209             struct hostent *h = NULL;
210             if (sa->sa_family == AF_INET6)
211               {
212                 while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in6 *) sa)->sin6_addr),
213                                           sizeof(struct in6_addr),
214                                           AF_INET6, &th, tmpbuf, tmpbuflen,
215                                           &h, &herrno))
216                   if (herrno == NETDB_INTERNAL && errno == ERANGE)
217                     tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
218                   else
219                     break;
220               }
221             else
222               {
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))
227                   if (herrno == NETDB_INTERNAL && errno == ERANGE)
228                     tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
229                   else
230                     break;
231               }
232
233             if (h == NULL)
234               {
235                 if (herrno == NETDB_INTERNAL)
236                   {
237                     __set_h_errno (herrno);
238                     return EAI_SYSTEM;
239                   }
240                 if (herrno == TRY_AGAIN)
241                   {
242                     __set_h_errno (herrno);
243                     return EAI_AGAIN;
244                   }
245               }
246
247             if (h)
248               {
249                 char *c;
250                 if ((flags & NI_NOFQDN)
251                     && (c = nrl_domainname ())
252                     && (c = strstr (h->h_name, c))
253                     && (c != h->h_name) && (*(--c) == '.'))
254                   /* Terminate the string after the prefix.  */
255                   *c = '\0';
256
257 #ifdef HAVE_LIBIDN
258                 /* If requested, convert from the IDN format.  */
259                 if (flags & NI_IDN)
260                   {
261                     int idn_flags = 0;
262                     if  (flags & NI_IDN_ALLOW_UNASSIGNED)
263                       idn_flags |= IDNA_ALLOW_UNASSIGNED;
264                     if (flags & NI_IDN_USE_STD3_ASCII_RULES)
265                       idn_flags |= IDNA_USE_STD3_ASCII_RULES;
266
267                     char *out;
268                     int rc = __idna_to_unicode_lzlz (h->h_name, &out,
269                                                      idn_flags);
270                     if (rc != IDNA_SUCCESS)
271                       {
272                         if (rc == IDNA_MALLOC_ERROR)
273                           return EAI_MEMORY;
274                         if (rc == IDNA_DLOPEN_ERROR)
275                           return EAI_SYSTEM;
276                         return EAI_IDN_ENCODE;
277                       }
278
279                     if (out != h->h_name)
280                       {
281                         h->h_name = strdupa (out);
282                         free (out);
283                       }
284                   }
285 #endif
286
287                 size_t len = strlen (h->h_name) + 1;
288                 if (len > hostlen)
289                   return EAI_OVERFLOW;
290
291                 memcpy (host, h->h_name, len);
292
293                 ok = 1;
294               }
295           }
296
297         if (!ok)
298           {
299             if (flags & NI_NAMEREQD)
300               {
301                 __set_errno (serrno);
302                 return EAI_NONAME;
303               }
304             else
305               {
306                 const char *c;
307                 if (sa->sa_family == AF_INET6)
308                   {
309                     const struct sockaddr_in6 *sin6p;
310                     uint32_t scopeid;
311
312                     sin6p = (const struct sockaddr_in6 *) sa;
313
314                     c = inet_ntop (AF_INET6,
315                                    (const void *) &sin6p->sin6_addr, host, hostlen);
316                     scopeid = sin6p->sin6_scope_id;
317                     if (scopeid != 0)
318                       {
319                         /* Buffer is >= IFNAMSIZ+1.  */
320                         char scopebuf[IFNAMSIZ + 1];
321                         char *scopeptr;
322                         int ni_numericscope = 0;
323                         size_t real_hostlen = __strnlen (host, hostlen);
324                         size_t scopelen = 0;
325
326                         scopebuf[0] = SCOPE_DELIMITER;
327                         scopebuf[1] = '\0';
328                         scopeptr = &scopebuf[1];
329
330                         if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
331                             || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
332                           {
333                             if (if_indextoname (scopeid, scopeptr) == NULL)
334                               ++ni_numericscope;
335                             else
336                               scopelen = strlen (scopebuf);
337                           }
338                         else
339                           ++ni_numericscope;
340
341                         if (ni_numericscope)
342                           scopelen = 1 + __snprintf (scopeptr,
343                                                      (scopebuf
344                                                       + sizeof scopebuf
345                                                       - scopeptr),
346                                                      "%u", scopeid);
347
348                         if (real_hostlen + scopelen + 1 > hostlen)
349                           /* Signal the buffer is too small.  This is
350                              what inet_ntop does.  */
351                           c = NULL;
352                         else
353                           memcpy (host + real_hostlen, scopebuf, scopelen + 1);
354                       }
355                   }
356                 else
357                   c = inet_ntop (AF_INET,
358                                  (const void *) &(((const struct sockaddr_in *) sa)->sin_addr),
359                                  host, hostlen);
360                 if (c == NULL)
361                   return EAI_OVERFLOW;
362               }
363             ok = 1;
364           }
365         break;
366
367       case AF_LOCAL:
368         if (!(flags & NI_NUMERICHOST))
369           {
370             struct utsname utsname;
371
372             if (!uname (&utsname))
373               {
374                 strncpy (host, utsname.nodename, hostlen);
375                 break;
376               };
377           };
378
379         if (flags & NI_NAMEREQD)
380            {
381             __set_errno (serrno);
382             return EAI_NONAME;
383           }
384
385         strncpy (host, "localhost", hostlen);
386         break;
387
388       default:
389         return EAI_FAMILY;
390     }
391
392   if (serv && (servlen > 0))
393     switch (sa->sa_family)
394       {
395       case AF_INET:
396       case AF_INET6:
397         if (!(flags & NI_NUMERICSERV))
398           {
399             struct servent *s, ts;
400             int e;
401             while ((e = __getservbyport_r (((const struct sockaddr_in *) sa)->sin_port,
402                                            ((flags & NI_DGRAM)
403                                             ? "udp" : "tcp"),
404                                            &ts, tmpbuf, tmpbuflen, &s)))
405               {
406                 if (e == ERANGE)
407                   tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
408                 else
409                   break;
410               }
411             if (s)
412               {
413                 strncpy (serv, s->s_name, servlen);
414                 break;
415               }
416           }
417
418         if (__snprintf (serv, servlen, "%d",
419                         ntohs (((const struct sockaddr_in *) sa)->sin_port))
420             + 1 > servlen)
421           return EAI_OVERFLOW;
422
423         break;
424
425       case AF_LOCAL:
426         strncpy (serv, ((const struct sockaddr_un *) sa)->sun_path, servlen);
427         break;
428     }
429
430   if (host && (hostlen > 0))
431     host[hostlen-1] = 0;
432   if (serv && (servlen > 0))
433     serv[servlen-1] = 0;
434   errno = serrno;
435   return 0;
436 }
437 libc_hidden_def (getnameinfo)