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