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