(NEW, NEW1): Define.
[kopensolaris-gnu/glibc.git] / nss / digits_dots.c
1 /* Copyright (C) 1997, 1999, 2000, 2001 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by H.J. Lu <hjl@gnu.ai.mit.edu>, 1997.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <assert.h>
21 #include <errno.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include <wctype.h>
26 #include <resolv.h>
27 #include <netdb.h>
28 #include <arpa/inet.h>
29 #include "nsswitch.h"
30
31 #ifdef USE_NSCD
32 # define inet_aton __inet_aton
33 # include <nscd/nscd_proto.h>
34 #endif
35
36 int
37 __nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
38                             char **buffer, size_t *buffer_size,
39                             size_t buflen, struct hostent **result,
40                             enum nss_status *status, int *typep,
41                             int flags, int *afp, int *h_errnop)
42 {
43   int save;
44
45   /* We have to test for the use of IPv6 which can only be done by
46      examining `_res'.  */
47   if ((_res.options & RES_INIT) == 0 && __res_ninit (&_res) == -1)
48     {
49       if (h_errnop)
50         *h_errnop = NETDB_INTERNAL;
51       *result = NULL;
52       return -1;
53     }
54
55   /*
56    * disallow names consisting only of digits/dots, unless
57    * they end in a dot.
58    */
59   if (isdigit (name[0]) || isxdigit (name[0]) || name[0] == ':')
60     {
61       const char *cp;
62       char *hostname;
63       typedef unsigned char host_addr_t[16];
64       host_addr_t *host_addr;
65       typedef char *host_addr_list_t[2];
66       host_addr_list_t *h_addr_ptrs;
67       char **h_alias_ptr;
68       size_t size_needed;
69       int addr_size;
70       int af;
71
72       if (typep != NULL)
73         af = *typep;
74       else if (afp != NULL)
75         af = *afp;
76       else
77         af = -1;
78
79       switch (af)
80         {
81         case AF_INET:
82           addr_size = INADDRSZ;
83           break;
84
85         case AF_INET6:
86           addr_size = IN6ADDRSZ;
87           break;
88
89         default:
90           if (typep != NULL)
91             {
92               /* This must not happen.  */
93               if (h_errnop != NULL)
94                 *h_errnop = HOST_NOT_FOUND;
95               goto done;
96             }
97           else
98             {
99               af = (_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET;
100               addr_size = af == AF_INET6 ? IN6ADDRSZ : INADDRSZ;
101             }
102           break;
103         }
104
105       size_needed = (sizeof (*host_addr)
106                      + sizeof (*h_addr_ptrs) + strlen (name) + 1);
107
108       if (buffer_size == NULL)
109         {
110           if (buflen < size_needed)
111             {
112               if (h_errnop != NULL)
113                 *h_errnop = TRY_AGAIN;
114               __set_errno (ERANGE);
115               goto done;
116             }
117         }
118       else if (buffer_size != NULL && *buffer_size < size_needed)
119         {
120           char *new_buf;
121           *buffer_size = size_needed;
122           new_buf = (char *) realloc (*buffer, *buffer_size);
123
124           if (new_buf == NULL)
125             {
126               save = errno;
127               free (*buffer);
128               *buffer = NULL;
129               *buffer_size = 0;
130               __set_errno (save);
131               if (h_errnop != NULL)
132                 *h_errnop = TRY_AGAIN;
133               *result = NULL;
134               goto done;
135             }
136           *buffer = new_buf;
137         }
138
139       memset (*buffer, '\0', size_needed);
140
141       host_addr = (host_addr_t *) *buffer;
142       h_addr_ptrs = (host_addr_list_t *)
143         ((char *) host_addr + sizeof (*host_addr));
144       h_alias_ptr = (char **) ((char *) h_addr_ptrs + sizeof (*h_addr_ptrs));
145       hostname = (char *) h_alias_ptr + sizeof (*h_alias_ptr);
146
147       if (isdigit (name[0]))
148         {
149           for (cp = name;; ++cp)
150             {
151               if (*cp == '\0')
152                 {
153                   int ok;
154
155                   if (*--cp == '.')
156                     break;
157
158                   /* All-numeric, no dot at the end. Fake up a hostent as if
159                      we'd actually done a lookup.  What if someone types
160                      255.255.255.255?  The test below will succeed
161                      spuriously... ???  */
162                   if (af == AF_INET)
163                     ok = __inet_aton (name, (struct in_addr *) host_addr);
164                   else
165                     {
166                       assert (af == AF_INET6);
167                       ok = inet_pton (af, name, host_addr) > 0;
168                     }
169                   if (! ok)
170                     {
171                       *h_errnop = HOST_NOT_FOUND;
172                       if (buffer_size)
173                         *result = NULL;
174                       goto done;
175                     }
176
177                   resbuf->h_name = strcpy (hostname, name);
178                   h_alias_ptr[0] = NULL;
179                   resbuf->h_aliases = h_alias_ptr;
180                   (*h_addr_ptrs)[0] = (char *) host_addr;
181                   (*h_addr_ptrs)[1] = NULL;
182                   resbuf->h_addr_list = *h_addr_ptrs;
183                   if ((typep != NULL && *typep == AF_INET6)
184                       || (af == AF_INET
185                           && (_res.options & RES_USE_INET6)))
186                     {
187                       if (typep != NULL && (flags & AI_V4MAPPED) == 0)
188                         {
189                           /* That's bad.  The user hasn't specified that she
190                              allows IPv4 numeric addresses.  */
191                           *result = NULL;
192                           *h_errnop = HOST_NOT_FOUND;
193                           goto done;
194                         }
195                       else
196                         {
197                           /* We need to change the IP v4 address into the
198                              IP v6 address.  */
199                           char tmp[INADDRSZ];
200                           char *p = (char *) host_addr;
201                           int i;
202
203                           /* Save a copy of the IP v4 address. */
204                           memcpy (tmp, host_addr, INADDRSZ);
205                           /* Mark this ipv6 addr as a mapped ipv4. */
206                           for (i = 0; i < 10; i++)
207                             *p++ = 0x00;
208                           *p++ = 0xff;
209                           *p++ = 0xff;
210                           /* Copy the IP v4 address. */
211                           memcpy (p, tmp, INADDRSZ);
212                           resbuf->h_addrtype = AF_INET6;
213                           resbuf->h_length = IN6ADDRSZ;
214                         }
215                     }
216                   else
217                     {
218                       resbuf->h_addrtype = af;
219                       resbuf->h_length = addr_size;
220                     }
221                   if (h_errnop != NULL)
222                     *h_errnop = NETDB_SUCCESS;
223                   if (buffer_size == NULL)
224                     *status = NSS_STATUS_SUCCESS;
225                   else
226                    *result = resbuf;
227                   goto done;
228                 }
229
230               if (!isdigit (*cp) && *cp != '.')
231                 break;
232             }
233         }
234
235       if ((isxdigit (name[0]) && strchr (name, ':') != NULL) || name[0] == ':')
236         {
237           const char *cp;
238           char *hostname;
239           typedef unsigned char host_addr_t[16];
240           host_addr_t *host_addr;
241           typedef char *host_addr_list_t[2];
242           host_addr_list_t *h_addr_ptrs;
243           size_t size_needed;
244           int addr_size;
245           int af;
246
247           if (typep != NULL)
248             af = *typep;
249           else if (afp != NULL)
250             af = *afp;
251           else
252             af = -1;
253
254           switch (af)
255             {
256             default:
257               af = (_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET;
258               if (af == AF_INET6)
259                 {
260                   addr_size = IN6ADDRSZ;
261                   break;
262                 }
263               /* FALLTHROUGH */
264
265             case AF_INET:
266               /* This is not possible.  We cannot represent an IPv6 address
267                  in an `struct in_addr' variable.  */
268               *h_errnop = HOST_NOT_FOUND;
269               *result = NULL;
270               goto done;
271
272             case AF_INET6:
273               addr_size = IN6ADDRSZ;
274               break;
275             }
276
277           size_needed = (sizeof (*host_addr)
278                          + sizeof (*h_addr_ptrs) + strlen (name) + 1);
279
280           if (buffer_size == NULL && buflen < size_needed)
281             {
282               if (h_errnop != NULL)
283                 *h_errnop = TRY_AGAIN;
284               __set_errno (ERANGE);
285               goto done;
286             }
287           else if (buffer_size != NULL && *buffer_size < size_needed)
288             {
289               char *new_buf;
290               *buffer_size = size_needed;
291               new_buf = realloc (*buffer, *buffer_size);
292
293               if (new_buf == NULL)
294                 {
295                   save = errno;
296                   free (*buffer);
297                   __set_errno (save);
298                   *buffer = NULL;
299                   *buffer_size = 0;
300                   *result = NULL;
301                   goto done;
302                 }
303               *buffer = new_buf;
304             }
305
306           memset (*buffer, '\0', size_needed);
307
308           host_addr = (host_addr_t *) *buffer;
309           h_addr_ptrs = (host_addr_list_t *)
310             ((char *) host_addr + sizeof (*host_addr));
311           hostname = (char *) h_addr_ptrs + sizeof (*h_addr_ptrs);
312
313           for (cp = name;; ++cp)
314             {
315               if (!*cp)
316                 {
317                   if (*--cp == '.')
318                     break;
319
320                   /* All-IPv6-legal, no dot at the end. Fake up a
321                      hostent as if we'd actually done a lookup.  */
322                   if (inet_pton (AF_INET6, name, host_addr) <= 0)
323                     {
324                       *h_errnop = HOST_NOT_FOUND;
325                       if (buffer_size)
326                         *result = NULL;
327                       goto done;
328                     }
329
330                   resbuf->h_name = strcpy (hostname, name);
331                   h_alias_ptr[0] = NULL;
332                   resbuf->h_aliases = h_alias_ptr;
333                   (*h_addr_ptrs)[0] = (char *) host_addr;
334                   (*h_addr_ptrs)[1] = (char *) 0;
335                   resbuf->h_addr_list = *h_addr_ptrs;
336                   resbuf->h_addrtype = AF_INET6;
337                   resbuf->h_length = addr_size;
338                   *h_errnop = NETDB_SUCCESS;
339                   if (buffer_size == NULL)
340                     *status = NSS_STATUS_SUCCESS;
341                   else
342                     *result = resbuf;
343                   goto done;
344                 }
345
346               if (!isxdigit (*cp) && *cp != ':' && *cp != '.')
347                 break;
348             }
349         }
350     }
351
352   return 0;
353
354 done:
355   return 1;
356 }