(getnameinfo): Use __snprintf instead of snprintf.
[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 (sa == NULL || addrlen < sizeof (sa_family_t))
179     return -1;
180
181   switch (sa->sa_family)
182     {
183     case AF_LOCAL:
184       if (addrlen < (socklen_t) (((struct sockaddr_un *) NULL)->sun_path))
185         return -1;
186       break;
187     case AF_INET:
188       if (addrlen < sizeof (struct sockaddr_in))
189         return -1;
190       break;
191     case AF_INET6:
192       if (addrlen < sizeof (struct sockaddr_in6))
193         return -1;
194       break;
195     default:
196       return -1;
197     }
198
199   if (host != NULL && hostlen > 0)
200     switch (sa->sa_family)
201       {
202       case AF_INET:
203       case AF_INET6:
204         if (!(flags & NI_NUMERICHOST))
205           {
206             struct hostent *h = NULL;
207             if (h == NULL)
208               {
209                 if (sa->sa_family == AF_INET6)
210                   {
211                     while (__gethostbyaddr_r ((void *) &(((struct sockaddr_in6 *) sa)->sin6_addr),
212                                               sizeof(struct in6_addr),
213                                               AF_INET6, &th, tmpbuf, tmpbuflen,
214                                               &h, &herrno))
215                       {
216                         if (herrno == NETDB_INTERNAL)
217                           {
218                             if (errno == ERANGE)
219                               {
220                                 tmpbuflen *= 2;
221                                 tmpbuf = alloca (tmpbuflen);
222                               }
223                             else
224                               {
225                                 __set_h_errno (herrno);
226                                 __set_errno (serrno);
227                                 return -1;
228                               }
229                           }
230                         else
231                           {
232                             break;
233                           }
234                       }
235                   }
236                 else
237                   {
238                     while (__gethostbyaddr_r ((void *) &(((struct sockaddr_in *)sa)->sin_addr),
239                                               sizeof(struct in_addr), AF_INET,
240                                               &th, tmpbuf, tmpbuflen,
241                                               &h, &herrno))
242                       {
243                         if (errno == ERANGE)
244                           {
245                             tmpbuflen *= 2;
246                             tmpbuf = alloca (tmpbuflen);
247                           }
248                         else
249                           {
250                             break;
251                           }
252                       }
253                   }
254               }
255
256             if (h)
257               {
258                 if (flags & NI_NOFQDN)
259                   {
260                     char *c;
261                     if ((c = nrl_domainname ()) && (c = strstr(h->h_name, c))
262                         && (c != h->h_name) && (*(--c) == '.'))
263                       {
264                         strncpy (host, h->h_name,
265                                  min(hostlen, (size_t) (c - h->h_name)));
266                         host[min(hostlen - 1, (size_t) (c - h->h_name))]
267                           = '\0';
268                         ok = 1;
269                       }
270                     else
271                       {
272                         strncpy (host, h->h_name, hostlen);
273                         ok = 1;
274                       }
275                   }
276                 strncpy (host, h->h_name, hostlen);
277                 ok = 1;
278               }
279           }
280
281         if (!ok)
282           {
283             if (flags & NI_NAMEREQD)
284               {
285                 __set_errno (serrno);
286                 return -1;
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                     if (addrlen > sizeof (struct sockaddr_in6)
299                         && (scopeid = sin6p->sin6_scope_id))
300                       {
301                         /* Buffer is >= IFNAMSIZ+1.  */
302                         char scopebuf[IFNAMSIZ + 1];
303                         int ni_numericscope = 0;
304
305                         if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
306                             || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
307                           {
308                             if (if_indextoname (scopeid, scopebuf) == NULL)
309                               ++ni_numericscope;
310                           }
311                         else
312                           ++ni_numericscope;
313
314                         if (ni_numericscope)
315                           {
316                             char *scopeptr = &scopebuf[1];
317                             size_t real_hostlen;
318                             size_t scopelen;
319
320                             scopebuf[0] = SCOPE_DELIMITER;
321                             scopelen = 1 + __snprintf (scopeptr,
322                                                        (scopebuf
323                                                         + sizeof scopebuf
324                                                         - scopeptr),
325                                                        "%u", scopeid);
326
327                             real_hostlen = __strnlen (host, hostlen);
328                             if (real_hostlen + scopelen + 1 > hostlen)
329                               return -1;
330                             memcpy (host + real_hostlen, scopebuf, scopelen);
331                           }
332                       }
333                   }
334                 else
335                   c = inet_ntop (AF_INET,
336                                  (void *) &(((struct sockaddr_in *) sa)->sin_addr),
337                                  host, hostlen);
338                 if (c == NULL)
339                   {
340                     __set_errno (serrno);
341                     return -1;
342                   }
343               }
344             ok = 1;
345           }
346         break;
347
348       case AF_LOCAL:
349         if (!(flags & NI_NUMERICHOST))
350           {
351             struct utsname utsname;
352
353             if (!uname (&utsname))
354               {
355                 strncpy (host, utsname.nodename, hostlen);
356                 break;
357               };
358           };
359
360         if (flags & NI_NAMEREQD)
361            {
362             __set_errno (serrno);
363             return -1;
364           }
365
366         strncpy (host, "localhost", hostlen);
367         break;
368
369       default:
370         return -1;
371     }
372
373   if (serv && (servlen > 0))
374     switch (sa->sa_family)
375       {
376       case AF_INET:
377       case AF_INET6:
378         if (!(flags & NI_NUMERICSERV))
379           {
380             struct servent *s, ts;
381             while (__getservbyport_r (((struct sockaddr_in *) sa)->sin_port,
382                                       ((flags & NI_DGRAM) ? "udp" : "tcp"),
383                                       &ts, tmpbuf, tmpbuflen, &s))
384               {
385                 if (herrno == NETDB_INTERNAL)
386                   {
387                     if (errno == ERANGE)
388                       {
389                         tmpbuflen *= 2;
390                         tmpbuf = __alloca (tmpbuflen);
391                       }
392                     else
393                       {
394                         __set_errno (serrno);
395                         return -1;
396                       }
397                   }
398                 else
399                   {
400                     break;
401                   }
402               }
403             if (s)
404               {
405                 strncpy (serv, s->s_name, servlen);
406                 break;
407               }
408           }
409         __snprintf (serv, servlen, "%d",
410                     ntohs (((struct sockaddr_in *) sa)->sin_port));
411         break;
412
413       case AF_LOCAL:
414         strncpy (serv, ((struct sockaddr_un *) sa)->sun_path, servlen);
415         break;
416     }
417
418   if (host && (hostlen > 0))
419     host[hostlen-1] = 0;
420   if (serv && (servlen > 0))
421     serv[servlen-1] = 0;
422   errno = serrno;
423   return 0;
424 }