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