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