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