chiark / gitweb /
Apply https://sourceware.org/git/?p=glibc.git;a=commit;h=d5dd6189d506068ed11c8bfa1e1e...
[eglibc.git] / nss / digits_dots.c
1 /* Copyright (C) 1997, 1999, 2000, 2001, 2004 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 af, int *h_errnop)
41 {
42   int save;
43
44   /* We have to test for the use of IPv6 which can only be done by
45      examining `_res'.  */
46   if (__res_maybe_init (&_res, 0) == -1)
47     {
48       if (h_errnop)
49         *h_errnop = NETDB_INTERNAL;
50       if (buffer_size == NULL)
51         *status = NSS_STATUS_TRYAGAIN;
52       else
53         *result = NULL;
54       return -1;
55     }
56
57   /*
58    * disallow names consisting only of digits/dots, unless
59    * they end in a dot.
60    */
61   if (isdigit (name[0]) || isxdigit (name[0]) || name[0] == ':')
62     {
63       const char *cp;
64       char *hostname;
65       typedef unsigned char host_addr_t[16];
66       host_addr_t *host_addr;
67       typedef char *host_addr_list_t[2];
68       host_addr_list_t *h_addr_ptrs;
69       char **h_alias_ptr;
70       size_t size_needed;
71       int addr_size;
72
73       switch (af)
74         {
75         case AF_INET:
76           addr_size = INADDRSZ;
77           break;
78
79         case AF_INET6:
80           addr_size = IN6ADDRSZ;
81           break;
82
83         default:
84           af = (_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET;
85           addr_size = af == AF_INET6 ? IN6ADDRSZ : INADDRSZ;
86           break;
87         }
88
89       size_needed = (sizeof (*host_addr)
90                      + sizeof (*h_addr_ptrs)
91                      + sizeof (*h_alias_ptr) + strlen (name) + 1);
92
93       if (buffer_size == NULL)
94         {
95           if (buflen < size_needed)
96             {
97               *status = NSS_STATUS_TRYAGAIN;
98               if (h_errnop != NULL)
99                 *h_errnop = NETDB_INTERNAL;
100               __set_errno (ERANGE);
101               goto done;
102             }
103         }
104       else if (buffer_size != NULL && *buffer_size < size_needed)
105         {
106           char *new_buf;
107           *buffer_size = size_needed;
108           new_buf = (char *) realloc (*buffer, *buffer_size);
109
110           if (new_buf == NULL)
111             {
112               save = errno;
113               free (*buffer);
114               *buffer = NULL;
115               *buffer_size = 0;
116               __set_errno (save);
117               if (h_errnop != NULL)
118                 *h_errnop = NETDB_INTERNAL;
119               *result = NULL;
120               goto done;
121             }
122           *buffer = new_buf;
123         }
124
125       memset (*buffer, '\0', size_needed);
126
127       host_addr = (host_addr_t *) *buffer;
128       h_addr_ptrs = (host_addr_list_t *)
129         ((char *) host_addr + sizeof (*host_addr));
130       h_alias_ptr = (char **) ((char *) h_addr_ptrs + sizeof (*h_addr_ptrs));
131       hostname = (char *) h_alias_ptr + sizeof (*h_alias_ptr);
132
133       if (isdigit (name[0]))
134         {
135           for (cp = name;; ++cp)
136             {
137               if (*cp == '\0')
138                 {
139                   int ok;
140
141                   if (*--cp == '.')
142                     break;
143
144                   /* All-numeric, no dot at the end. Fake up a hostent as if
145                      we'd actually done a lookup.  What if someone types
146                      255.255.255.255?  The test below will succeed
147                      spuriously... ???  */
148                   if (af == AF_INET)
149                     ok = __inet_aton (name, (struct in_addr *) host_addr);
150                   else
151                     {
152                       assert (af == AF_INET6);
153                       ok = inet_pton (af, name, host_addr) > 0;
154                     }
155                   if (! ok)
156                     {
157                       *h_errnop = HOST_NOT_FOUND;
158                       if (buffer_size == NULL)
159                         *status = NSS_STATUS_NOTFOUND;
160                       else
161                         *result = NULL;
162                       goto done;
163                     }
164
165                   resbuf->h_name = strcpy (hostname, name);
166                   h_alias_ptr[0] = NULL;
167                   resbuf->h_aliases = h_alias_ptr;
168                   (*h_addr_ptrs)[0] = (char *) host_addr;
169                   (*h_addr_ptrs)[1] = NULL;
170                   resbuf->h_addr_list = *h_addr_ptrs;
171                   if (af == AF_INET && (_res.options & RES_USE_INET6))
172                     {
173                       /* We need to change the IP v4 address into the
174                          IP v6 address.  */
175                       char tmp[INADDRSZ];
176                       char *p = (char *) host_addr;
177                       int i;
178
179                       /* Save a copy of the IP v4 address. */
180                       memcpy (tmp, host_addr, INADDRSZ);
181                       /* Mark this ipv6 addr as a mapped ipv4. */
182                       for (i = 0; i < 10; i++)
183                         *p++ = 0x00;
184                       *p++ = 0xff;
185                       *p++ = 0xff;
186                       /* Copy the IP v4 address. */
187                       memcpy (p, tmp, INADDRSZ);
188                       resbuf->h_addrtype = AF_INET6;
189                       resbuf->h_length = IN6ADDRSZ;
190                     }
191                   else
192                     {
193                       resbuf->h_addrtype = af;
194                       resbuf->h_length = addr_size;
195                     }
196                   if (h_errnop != NULL)
197                     *h_errnop = NETDB_SUCCESS;
198                   if (buffer_size == NULL)
199                     *status = NSS_STATUS_SUCCESS;
200                   else
201                     *result = resbuf;
202                   goto done;
203                 }
204
205               if (!isdigit (*cp) && *cp != '.')
206                 break;
207             }
208         }
209
210       if ((isxdigit (name[0]) && strchr (name, ':') != NULL) || name[0] == ':')
211         {
212           switch (af)
213             {
214             default:
215               af = (_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET;
216               if (af == AF_INET6)
217                 {
218                   addr_size = IN6ADDRSZ;
219                   break;
220                 }
221               /* FALLTHROUGH */
222
223             case AF_INET:
224               /* This is not possible.  We cannot represent an IPv6 address
225                  in an `struct in_addr' variable.  */
226               *h_errnop = HOST_NOT_FOUND;
227               if (buffer_size == NULL)
228                 *status = NSS_STATUS_NOTFOUND;
229               else
230                 *result = NULL;
231               goto done;
232
233             case AF_INET6:
234               addr_size = IN6ADDRSZ;
235               break;
236             }
237
238           for (cp = name;; ++cp)
239             {
240               if (!*cp)
241                 {
242                   if (*--cp == '.')
243                     break;
244
245                   /* All-IPv6-legal, no dot at the end. Fake up a
246                      hostent as if we'd actually done a lookup.  */
247                   if (inet_pton (AF_INET6, name, host_addr) <= 0)
248                     {
249                       *h_errnop = HOST_NOT_FOUND;
250                       if (buffer_size == NULL)
251                         *status = NSS_STATUS_NOTFOUND;
252                       else
253                         *result = NULL;
254                       goto done;
255                     }
256
257                   resbuf->h_name = strcpy (hostname, name);
258                   h_alias_ptr[0] = NULL;
259                   resbuf->h_aliases = h_alias_ptr;
260                   (*h_addr_ptrs)[0] = (char *) host_addr;
261                   (*h_addr_ptrs)[1] = (char *) 0;
262                   resbuf->h_addr_list = *h_addr_ptrs;
263                   resbuf->h_addrtype = AF_INET6;
264                   resbuf->h_length = addr_size;
265                   *h_errnop = NETDB_SUCCESS;
266                   if (buffer_size == NULL)
267                     *status = NSS_STATUS_SUCCESS;
268                   else
269                     *result = resbuf;
270                   goto done;
271                 }
272
273               if (!isxdigit (*cp) && *cp != ':' && *cp != '.')
274                 break;
275             }
276         }
277     }
278
279   return 0;
280
281 done:
282   return 1;
283 }
284 libc_hidden_def (__nss_hostname_digits_dots)