chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.git] / nis / nss_nis / nis-hosts.c
1 /* Copyright (C) 1996-2000, 2002, 2003, 2006, 2007, 2008
2    Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <assert.h>
22 #include <nss.h>
23 #include <ctype.h>
24 /* The following is an ugly trick to avoid a prototype declaration for
25    _nss_nis_endgrent.  */
26 #define _nss_nis_endhostent _nss_nis_endhostent_XXX
27 #include <netdb.h>
28 #undef _nss_nis_endhostent
29 #include <string.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <resolv.h>
33 #include <bits/libc-lock.h>
34 #include <rpcsvc/yp.h>
35 #include <rpcsvc/ypclnt.h>
36
37 #include "nss-nis.h"
38
39 /* Get implementation for some internal functions. */
40 #include <resolv/mapv4v6addr.h>
41
42 #define ENTNAME         hostent
43 #define DATABASE        "hosts"
44 #define NEED_H_ERRNO
45
46 #define EXTRA_ARGS      , af, flags
47 #define EXTRA_ARGS_DECL , int af, int flags
48
49 #define ENTDATA hostent_data
50 struct hostent_data
51   {
52     unsigned char host_addr[16];        /* IPv4 or IPv6 address.  */
53     char *h_addr_ptrs[2];       /* Points to that and null terminator.  */
54   };
55
56 #define TRAILING_LIST_MEMBER            h_aliases
57 #define TRAILING_LIST_SEPARATOR_P       isspace
58 #include <nss/nss_files/files-parse.c>
59 LINE_PARSER
60 ("#",
61  {
62    char *addr;
63
64    STRING_FIELD (addr, isspace, 1);
65
66    assert (af == AF_INET || af == AF_INET6 || af == AF_UNSPEC);
67
68    /* Parse address.  */
69    if (af != AF_INET6 && inet_pton (AF_INET, addr, entdata->host_addr) > 0)
70      {
71        assert ((flags & AI_V4MAPPED) == 0 || af != AF_UNSPEC);
72        if (flags & AI_V4MAPPED)
73          {
74            map_v4v6_address ((char *) entdata->host_addr,
75                              (char *) entdata->host_addr);
76            result->h_addrtype = AF_INET6;
77            result->h_length = IN6ADDRSZ;
78          }
79        else
80          {
81            result->h_addrtype = AF_INET;
82            result->h_length = INADDRSZ;
83          }
84      }
85    else if (af != AF_INET
86             && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
87      {
88        result->h_addrtype = AF_INET6;
89        result->h_length = IN6ADDRSZ;
90      }
91    else
92      /* Illegal address: ignore line.  */
93      return 0;
94
95    /* Store a pointer to the address in the expected form.  */
96    entdata->h_addr_ptrs[0] = (char *) entdata->host_addr;
97    entdata->h_addr_ptrs[1] = NULL;
98    result->h_addr_list = entdata->h_addr_ptrs;
99
100    STRING_FIELD (result->h_name, isspace, 1);
101  })
102
103
104 __libc_lock_define_initialized (static, lock)
105
106 static bool_t new_start = 1;
107 static char *oldkey = NULL;
108 static int oldkeylen = 0;
109
110
111 enum nss_status
112 _nss_nis_sethostent (int stayopen)
113 {
114   __libc_lock_lock (lock);
115
116   new_start = 1;
117   if (oldkey != NULL)
118     {
119       free (oldkey);
120       oldkey = NULL;
121       oldkeylen = 0;
122     }
123
124   __libc_lock_unlock (lock);
125
126   return NSS_STATUS_SUCCESS;
127 }
128 /* Make _nss_nis_endhostent an alias of _nss_nis_sethostent.  We do this
129    even though the prototypes don't match.  The argument of sethostent
130    is used so this makes no difference.  */
131 strong_alias (_nss_nis_sethostent, _nss_nis_endhostent)
132
133
134 /* The calling function always need to get a lock first. */
135 static enum nss_status
136 internal_nis_gethostent_r (struct hostent *host, char *buffer,
137                            size_t buflen, int *errnop, int *h_errnop,
138                            int af, int flags)
139 {
140   char *domain;
141   if (__builtin_expect (yp_get_default_domain (&domain), 0))
142     return NSS_STATUS_UNAVAIL;
143
144   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct parser_data);
145   buffer += pad;
146
147   struct parser_data *data = (void *) buffer;
148   if (__builtin_expect (buflen < sizeof *data + 1 + pad, 0))
149     {
150       *errnop = ERANGE;
151       *h_errnop = NETDB_INTERNAL;
152       return NSS_STATUS_TRYAGAIN;
153     }
154   buflen -= pad;
155
156   /* Get the next entry until we found a correct one. */
157   const size_t linebuflen = buffer + buflen - data->linebuffer;
158   int parse_res;
159   do
160     {
161       char *result;
162       int len;
163       char *outkey;
164       int keylen;
165       int yperr;
166       if (new_start)
167         yperr = yp_first (domain, "hosts.byname", &outkey, &keylen, &result,
168                           &len);
169       else
170         yperr = yp_next (domain, "hosts.byname", oldkey, oldkeylen, &outkey,
171                          &keylen, &result, &len);
172
173       if (__builtin_expect (yperr != YPERR_SUCCESS, 0))
174         {
175           enum nss_status retval = yperr2nss (yperr);
176
177           switch (retval)
178             {
179             case NSS_STATUS_TRYAGAIN:
180               *errnop = errno;
181               *h_errnop = TRY_AGAIN;
182               break;
183             case NSS_STATUS_NOTFOUND:
184               *h_errnop = HOST_NOT_FOUND;
185               break;
186             default:
187               *h_errnop = NO_RECOVERY;
188               break;
189             }
190           return retval;
191         }
192
193       if (__builtin_expect ((size_t) (len + 1) > linebuflen, 0))
194         {
195           free (result);
196           *h_errnop = NETDB_INTERNAL;
197           *errnop = ERANGE;
198           return NSS_STATUS_TRYAGAIN;
199         }
200
201       char *p = strncpy (data->linebuffer, result, len);
202       data->linebuffer[len] = '\0';
203       while (isspace (*p))
204         ++p;
205       free (result);
206
207       parse_res = parse_line (p, host, data, buflen, errnop, af, flags);
208       if (__builtin_expect (parse_res == -1, 0))
209         {
210           free (outkey);
211           *h_errnop = NETDB_INTERNAL;
212           *errnop = ERANGE;
213           return NSS_STATUS_TRYAGAIN;
214         }
215       free (oldkey);
216       oldkey = outkey;
217       oldkeylen = keylen;
218       new_start = 0;
219     }
220   while (!parse_res);
221
222   *h_errnop = NETDB_SUCCESS;
223   return NSS_STATUS_SUCCESS;
224 }
225
226
227 enum nss_status
228 _nss_nis_gethostent_r (struct hostent *host, char *buffer, size_t buflen,
229                        int *errnop, int *h_errnop)
230 {
231   enum nss_status status;
232
233   __libc_lock_lock (lock);
234
235   status = internal_nis_gethostent_r (host, buffer, buflen, errnop, h_errnop,
236                         ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET),
237                         ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0 ));
238
239   __libc_lock_unlock (lock);
240
241   return status;
242 }
243
244
245 static enum nss_status
246 internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
247                            char *buffer, size_t buflen, int *errnop,
248                            int *h_errnop, int flags)
249 {
250   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct parser_data);
251   buffer += pad;
252
253   struct parser_data *data = (void *) buffer;
254
255   if (name == NULL)
256     {
257       *errnop = EINVAL;
258       return NSS_STATUS_UNAVAIL;
259     }
260
261   char *domain;
262   if (yp_get_default_domain (&domain))
263     return NSS_STATUS_UNAVAIL;
264
265   if (buflen < sizeof *data + 1 + pad)
266     {
267       *h_errnop = NETDB_INTERNAL;
268       *errnop = ERANGE;
269       return NSS_STATUS_TRYAGAIN;
270     }
271   buflen -= pad;
272
273   /* Convert name to lowercase.  */
274   size_t namlen = strlen (name);
275   char name2[namlen + 1];
276   size_t i;
277
278   for (i = 0; i < namlen; ++i)
279     name2[i] = tolower (name[i]);
280   name2[i] = '\0';
281
282   char *result;
283   int len;
284   int yperr = yp_match (domain, "hosts.byname", name2, namlen, &result, &len);
285
286   if (__builtin_expect (yperr != YPERR_SUCCESS, 0))
287     {
288       enum nss_status retval = yperr2nss (yperr);
289
290       if (retval == NSS_STATUS_TRYAGAIN)
291         {
292           *h_errnop = TRY_AGAIN;
293           *errnop = errno;
294         }
295       if (retval == NSS_STATUS_NOTFOUND)
296         *h_errnop = HOST_NOT_FOUND;
297       return retval;
298     }
299
300   const size_t linebuflen = buffer + buflen - data->linebuffer;
301   if (__builtin_expect ((size_t) (len + 1) > linebuflen, 0))
302     {
303       free (result);
304       *h_errnop = NETDB_INTERNAL;
305       *errnop = ERANGE;
306       return NSS_STATUS_TRYAGAIN;
307     }
308
309   char *p = strncpy (data->linebuffer, result, len);
310   data->linebuffer[len] = '\0';
311   while (isspace (*p))
312     ++p;
313   free (result);
314
315   int parse_res = parse_line (p, host, data, buflen, errnop, af, flags);
316
317   if (__builtin_expect (parse_res < 1 || host->h_addrtype != af, 0))
318     {
319       if (parse_res == -1)
320         {
321           *h_errnop = NETDB_INTERNAL;
322           return NSS_STATUS_TRYAGAIN;
323         }
324       else
325         {
326           *h_errnop = HOST_NOT_FOUND;
327           return NSS_STATUS_NOTFOUND;
328         }
329     }
330
331   *h_errnop = NETDB_SUCCESS;
332   return NSS_STATUS_SUCCESS;
333 }
334
335
336 enum nss_status
337 _nss_nis_gethostbyname2_r (const char *name, int af, struct hostent *host,
338                            char *buffer, size_t buflen, int *errnop,
339                            int *h_errnop)
340 {
341   if (af != AF_INET && af != AF_INET6)
342     {
343       *h_errnop = HOST_NOT_FOUND;
344       return NSS_STATUS_NOTFOUND;
345     }
346
347   return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
348                                     h_errnop,
349                         ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0));
350 }
351
352
353 enum nss_status
354 _nss_nis_gethostbyname_r (const char *name, struct hostent *host, char *buffer,
355                           size_t buflen, int *errnop, int *h_errnop)
356 {
357   if (_res.options & RES_USE_INET6)
358     {
359       enum nss_status status;
360
361       status = internal_gethostbyname2_r (name, AF_INET6, host, buffer, buflen,
362                                           errnop, h_errnop, AI_V4MAPPED);
363       if (status == NSS_STATUS_SUCCESS)
364         return status;
365     }
366
367   return internal_gethostbyname2_r (name, AF_INET, host, buffer, buflen,
368                                     errnop, h_errnop, 0);
369 }
370
371
372 enum nss_status
373 _nss_nis_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
374                           struct hostent *host, char *buffer, size_t buflen,
375                           int *errnop, int *h_errnop)
376 {
377   char *domain;
378   if (__builtin_expect (yp_get_default_domain (&domain), 0))
379     return NSS_STATUS_UNAVAIL;
380
381   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct parser_data);
382   buffer += pad;
383
384   struct parser_data *data = (void *) buffer;
385   if (__builtin_expect (buflen < sizeof *data + 1 + pad, 0))
386     {
387       *errnop = ERANGE;
388       *h_errnop = NETDB_INTERNAL;
389       return NSS_STATUS_TRYAGAIN;
390     }
391   buflen -= pad;
392
393   char *buf = inet_ntoa (*(const struct in_addr *) addr);
394
395   char *result;
396   int len;
397   int yperr = yp_match (domain, "hosts.byaddr", buf, strlen (buf), &result,
398                         &len);
399
400   if (__builtin_expect (yperr != YPERR_SUCCESS, 0))
401     {
402       enum nss_status retval = yperr2nss (yperr);
403
404       if (retval == NSS_STATUS_TRYAGAIN)
405         {
406           *h_errnop = TRY_AGAIN;
407           *errnop = errno;
408         }
409       else if (retval == NSS_STATUS_NOTFOUND)
410         *h_errnop = HOST_NOT_FOUND;
411
412       return retval;
413     }
414
415   const size_t linebuflen = buffer + buflen - data->linebuffer;
416   if (__builtin_expect ((size_t) (len + 1) > linebuflen, 0))
417     {
418       free (result);
419       *errnop = ERANGE;
420       *h_errnop = NETDB_INTERNAL;
421       return NSS_STATUS_TRYAGAIN;
422     }
423
424   char *p = strncpy (data->linebuffer, result, len);
425   data->linebuffer[len] = '\0';
426   while (isspace (*p))
427     ++p;
428   free (result);
429
430   int parse_res = parse_line (p, host, data, buflen, errnop, af,
431                               ((_res.options & RES_USE_INET6)
432                                ? AI_V4MAPPED : 0));
433   if (__builtin_expect (parse_res < 1, 0))
434     {
435       if (parse_res == -1)
436         {
437           *h_errnop = NETDB_INTERNAL;
438           return NSS_STATUS_TRYAGAIN;
439         }
440       else
441         {
442           *h_errnop = HOST_NOT_FOUND;
443           return NSS_STATUS_NOTFOUND;
444         }
445     }
446
447   *h_errnop = NETDB_SUCCESS;
448   return NSS_STATUS_SUCCESS;
449 }
450
451
452 enum nss_status
453 _nss_nis_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
454                            char *buffer, size_t buflen, int *errnop,
455                            int *herrnop, int32_t *ttlp)
456 {
457   char *domain;
458   if (yp_get_default_domain (&domain))
459     {
460       *herrnop = NO_DATA;
461       return NSS_STATUS_UNAVAIL;
462     }
463
464   /* Convert name to lowercase.  */
465   size_t namlen = strlen (name);
466   char name2[namlen + 1];
467   size_t i;
468
469   for (i = 0; i < namlen; ++i)
470     name2[i] = tolower (name[i]);
471   name2[i] = '\0';
472
473   char *result;
474   int len;
475   int yperr = yp_match (domain, "hosts.byname", name2, namlen, &result, &len);
476
477   if (__builtin_expect (yperr != YPERR_SUCCESS, 0))
478     {
479       enum nss_status retval = yperr2nss (yperr);
480
481       if (retval == NSS_STATUS_TRYAGAIN)
482         {
483           *herrnop = TRY_AGAIN;
484           *errnop = errno;
485         }
486       if (retval == NSS_STATUS_NOTFOUND)
487         *herrnop = HOST_NOT_FOUND;
488       return retval;
489     }
490
491   if (*pat == NULL)
492     {
493       uintptr_t pad = (-(uintptr_t) buffer
494                        % __alignof__ (struct gaih_addrtuple));
495       buffer += pad;
496       buflen = buflen > pad ? buflen - pad : 0;
497
498       if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple), 0))
499         {
500         erange:
501           free (result);
502           *errnop = ERANGE;
503           *herrnop = NETDB_INTERNAL;
504           return NSS_STATUS_TRYAGAIN;
505         }
506
507       *pat = (struct gaih_addrtuple *) buffer;
508       buffer += sizeof (struct gaih_addrtuple);
509       buflen -= sizeof (struct gaih_addrtuple);
510     }
511
512   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct parser_data);
513   buffer += pad;
514
515   struct parser_data *data = (void *) buffer;
516
517   if (__builtin_expect (buflen < sizeof *data + 1 + pad, 0))
518     goto erange;
519   buflen -= pad;
520
521   struct hostent host;
522   int parse_res = parse_line (result, &host, data, buflen, errnop, AF_UNSPEC,
523                               0);
524   if (__builtin_expect (parse_res < 1, 0))
525     {
526       if (parse_res == -1)
527         {
528           *herrnop = NETDB_INTERNAL;
529           return NSS_STATUS_TRYAGAIN;
530         }
531       else
532         {
533           *herrnop = HOST_NOT_FOUND;
534           return NSS_STATUS_NOTFOUND;
535         }
536     }
537
538   (*pat)->next = NULL;
539   (*pat)->family = host.h_addrtype;
540   memcpy ((*pat)->addr, host.h_addr_list[0], host.h_length);
541   (*pat)->scopeid = 0;
542   assert (host.h_addr_list[1] == NULL);
543
544   /* Undo the alignment for parser_data.  */
545   buffer -= pad;
546   buflen += pad;
547
548   size_t h_name_len = strlen (host.h_name) + 1;
549   if (h_name_len >= buflen)
550     goto erange;
551   (*pat)->name = memcpy (buffer, host.h_name, h_name_len);
552
553   free (result);
554
555   return NSS_STATUS_SUCCESS;
556 }