25a8af7a6c8b06ab764d9bbe75a224940e317130
[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. All advertising materials mentioning features or use of this software
19    must display the following acknowledgement with the name(s) of the
20    authors as specified in the copyright notice(s) substituted where
21    indicated:
22
23         This product includes software developed by <name(s)>, The Inner
24         Net, and other contributors.
25
26 5. Neither the name(s) of the author(s) nor the names of its contributors
27    may be used to endorse or promote products derived from this software
28    without specific prior written permission.
29
30 THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
31 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
33 DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
34 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
36 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
37 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
41   If these license terms cause you a real problem, contact the author.  */
42
43 /* This software is Copyright 1996 by Craig Metz, All Rights Reserved.  */
44
45 #include <alloca.h>
46 #include <errno.h>
47 #include <netdb.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <arpa/inet.h>
52 #include <net/if.h>
53 #include <netinet/in.h>
54 #include <sys/param.h>
55 #include <sys/socket.h>
56 #include <sys/types.h>
57 #include <sys/un.h>
58 #include <sys/utsname.h>
59 #include <bits/libc-lock.h>
60
61 #ifndef min
62 # define min(x,y) (((x) > (y)) ? (y) : (x))
63 #endif /* min */
64
65
66 static char *
67 internal_function
68 nrl_domainname (void)
69 {
70   static char *domain;
71   static int not_first;
72
73   if (! not_first)
74     {
75       __libc_lock_define_initialized (static, lock);
76       __libc_lock_lock (lock);
77
78       if (! not_first)
79         {
80           char *c;
81           struct hostent *h, th;
82           size_t tmpbuflen = 1024;
83           char *tmpbuf = alloca (tmpbuflen);
84           int herror;
85
86           not_first = 1;
87
88           while (__gethostbyname_r ("localhost", &th, tmpbuf, tmpbuflen, &h,
89                                     &herror))
90             {
91               if (herror == NETDB_INTERNAL && errno == ERANGE)
92                 {
93                   tmpbuflen *= 2;
94                   tmpbuf = alloca (tmpbuflen);
95                 }
96               else
97                 break;
98             }
99
100           if (h && (c = strchr (h->h_name, '.')))
101             domain = __strdup (++c);
102           else
103             {
104               /* The name contains no domain information.  Use the name
105                  now to get more information.  */
106               while (__gethostname (tmpbuf, tmpbuflen))
107                 {
108                   tmpbuflen *= 2;
109                   tmpbuf = alloca (tmpbuflen);
110                 }
111
112               if ((c = strchr (tmpbuf, '.')))
113                 domain = __strdup (++c);
114               else
115                 {
116                   /* We need to preserve the hostname.  */
117                   const char *hstname = strdupa (tmpbuf);
118
119                   while (__gethostbyname_r (hstname, &th, tmpbuf, tmpbuflen,
120                                             &h, &herror))
121                     {
122                       if (herror == NETDB_INTERNAL && errno == ERANGE)
123                         {
124                           tmpbuflen *= 2;
125                           tmpbuf = alloca (tmpbuflen);
126                         }
127                       else
128                         break;
129                     }
130
131                   if (h && (c = strchr(h->h_name, '.')))
132                     domain = __strdup (++c);
133                   else
134                     {
135                       struct in_addr in_addr;
136
137                       in_addr.s_addr = htonl (INADDR_LOOPBACK);
138
139                       while (__gethostbyaddr_r ((const char *) &in_addr,
140                                                 sizeof (struct in_addr),
141                                                 AF_INET, &th, tmpbuf,
142                                                 tmpbuflen, &h, &herror))
143                         {
144                           if (herror == NETDB_INTERNAL && errno == ERANGE)
145                             {
146                               tmpbuflen *= 2;
147                               tmpbuf = alloca (tmpbuflen);
148                             }
149                           else
150                             break;
151                         }
152
153                       if (h && (c = strchr (h->h_name, '.')))
154                         domain = __strdup (++c);
155                     }
156                 }
157             }
158         }
159
160       __libc_lock_unlock (lock);
161     }
162
163   return domain;
164 };
165
166
167 int
168 getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
169              socklen_t hostlen, char *serv, socklen_t servlen, int flags)
170 {
171   int serrno = errno;
172   int tmpbuflen = 1024;
173   int herrno;
174   char *tmpbuf = alloca (tmpbuflen);
175   struct hostent th;
176   int ok = 0;
177
178   if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM))
179     return EAI_BADFLAGS;
180
181   if (sa == NULL || addrlen < sizeof (sa_family_t))
182     return EAI_FAMILY;
183
184   switch (sa->sa_family)
185     {
186     case AF_LOCAL:
187       if (addrlen < (socklen_t) (((struct sockaddr_un *) NULL)->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 (h == NULL)
211               {
212                 if (sa->sa_family == AF_INET6)
213                   {
214                     while (__gethostbyaddr_r ((void *) &(((struct sockaddr_in6 *) sa)->sin6_addr),
215                                               sizeof(struct in6_addr),
216                                               AF_INET6, &th, tmpbuf, tmpbuflen,
217                                               &h, &herrno))
218                       {
219                         if (herrno == NETDB_INTERNAL)
220                           {
221                             if (errno == ERANGE)
222                               {
223                                 tmpbuflen *= 2;
224                                 tmpbuf = alloca (tmpbuflen);
225                               }
226                             else
227                               {
228                                 __set_h_errno (herrno);
229                                 __set_errno (serrno);
230                                 return EAI_SYSTEM;
231                               }
232                           }
233                         else
234                           {
235                             break;
236                           }
237                       }
238                   }
239                 else
240                   {
241                     while (__gethostbyaddr_r ((void *) &(((struct sockaddr_in *)sa)->sin_addr),
242                                               sizeof(struct in_addr), AF_INET,
243                                               &th, tmpbuf, tmpbuflen,
244                                               &h, &herrno))
245                       {
246                         if (errno == ERANGE)
247                           {
248                             tmpbuflen *= 2;
249                             tmpbuf = alloca (tmpbuflen);
250                           }
251                         else
252                           {
253                             break;
254                           }
255                       }
256                   }
257               }
258
259             if (h)
260               {
261                 char *c;
262                 if ((flags & NI_NOFQDN) == 0
263                     && (c = nrl_domainname ())
264                     && (c = strstr (h->h_name, c))
265                     && (c != h->h_name) && (*(--c) == '.'))
266                   {
267                     strncpy (host, h->h_name,
268                              min(hostlen, (size_t) (c - h->h_name)));
269                     host[min(hostlen - 1, (size_t) (c - h->h_name))]
270                       = '\0';
271                     ok = 1;
272                   }
273                 else
274                   {
275                     strncpy (host, h->h_name, hostlen);
276                     ok = 1;
277                   }
278               }
279           }
280
281         if (!ok)
282           {
283             if (flags & NI_NAMEREQD)
284               {
285                 __set_errno (serrno);
286                 return EAI_NONAME;
287               }
288             else
289               {
290                 const char *c;
291                 if (sa->sa_family == AF_INET6)
292                   {
293                     struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) sa;
294                     uint32_t scopeid;
295
296                     c = inet_ntop (AF_INET6,
297                                    (void *) &sin6p->sin6_addr, host, hostlen);
298                     scopeid = sin6p->sin6_scope_id;
299                     if (scopeid != 0)
300                       {
301                         /* Buffer is >= IFNAMSIZ+1.  */
302                         char scopebuf[IFNAMSIZ + 1];
303                         char *scopeptr;
304                         int ni_numericscope = 0;
305                         size_t real_hostlen = __strnlen (host, hostlen);
306                         size_t scopelen = 0;
307
308                         scopebuf[0] = SCOPE_DELIMITER;
309                         scopebuf[1] = '\0';
310                         scopeptr = &scopebuf[1];
311
312                         if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
313                             || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
314                           {
315                             if (if_indextoname (scopeid, scopeptr) == NULL)
316                               ++ni_numericscope;
317                             else
318                               scopelen = strlen (scopebuf);
319                           }
320                         else
321                           ++ni_numericscope;
322
323                         if (ni_numericscope)
324                           scopelen = 1 + __snprintf (scopeptr,
325                                                      (scopebuf
326                                                       + sizeof scopebuf
327                                                       - scopeptr),
328                                                      "%u", scopeid);
329
330                         if (real_hostlen + scopelen + 1 > hostlen)
331                           /* XXX We should not fail here.  Simply enlarge
332                              the buffer or return with out of memory.  */
333                           return EAI_SYSTEM;
334                         memcpy (host + real_hostlen, scopebuf, scopelen + 1);
335                       }
336                   }
337                 else
338                   c = inet_ntop (AF_INET,
339                                  (void *) &(((struct sockaddr_in *) sa)->sin_addr),
340                                  host, hostlen);
341                 if (c == NULL)
342                   {
343                     __set_errno (serrno);
344                     return EAI_SYSTEM;
345                   }
346               }
347             ok = 1;
348           }
349         break;
350
351       case AF_LOCAL:
352         if (!(flags & NI_NUMERICHOST))
353           {
354             struct utsname utsname;
355
356             if (!uname (&utsname))
357               {
358                 strncpy (host, utsname.nodename, hostlen);
359                 break;
360               };
361           };
362
363         if (flags & NI_NAMEREQD)
364            {
365             __set_errno (serrno);
366             return EAI_NONAME;
367           }
368
369         strncpy (host, "localhost", hostlen);
370         break;
371
372       default:
373         return EAI_FAMILY;
374     }
375
376   if (serv && (servlen > 0))
377     switch (sa->sa_family)
378       {
379       case AF_INET:
380       case AF_INET6:
381         if (!(flags & NI_NUMERICSERV))
382           {
383             struct servent *s, ts;
384             while (__getservbyport_r (((struct sockaddr_in *) sa)->sin_port,
385                                       ((flags & NI_DGRAM) ? "udp" : "tcp"),
386                                       &ts, tmpbuf, tmpbuflen, &s))
387               {
388                 if (herrno == NETDB_INTERNAL)
389                   {
390                     if (errno == ERANGE)
391                       {
392                         tmpbuflen *= 2;
393                         tmpbuf = __alloca (tmpbuflen);
394                       }
395                     else
396                       {
397                         __set_errno (serrno);
398                         return EAI_SYSTEM;
399                       }
400                   }
401                 else
402                   {
403                     break;
404                   }
405               }
406             if (s)
407               {
408                 strncpy (serv, s->s_name, servlen);
409                 break;
410               }
411           }
412         __snprintf (serv, servlen, "%d",
413                     ntohs (((struct sockaddr_in *) sa)->sin_port));
414         break;
415
416       case AF_LOCAL:
417         strncpy (serv, ((struct sockaddr_un *) sa)->sun_path, servlen);
418         break;
419     }
420
421   if (host && (hostlen > 0))
422     host[hostlen-1] = 0;
423   if (serv && (servlen > 0))
424     serv[servlen-1] = 0;
425   errno = serrno;
426   return 0;
427 }