(getspnam_plususer): Preserve original return value.
[kopensolaris-gnu/glibc.git] / nis / nss_nis / nis-hosts.c
1 /* Copyright (C) 1996-2000, 2002, 2003 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
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 <nss.h>
21 #include <ctype.h>
22 /* The following is an ugly trick to avoid a prototype declaration for
23    _nss_nis_endgrent.  */
24 #define _nss_nis_endhostent _nss_nis_endhostent_XXX
25 #include <netdb.h>
26 #undef _nss_nis_endhostent
27 #include <string.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <resolv.h>
31 #include <bits/libc-lock.h>
32 #include <rpcsvc/yp.h>
33 #include <rpcsvc/ypclnt.h>
34
35 #include "nss-nis.h"
36
37 /* Get implementation for some internal functions. */
38 #include <resolv/mapv4v6addr.h>
39
40 #define ENTNAME         hostent
41 #define DATABASE        "hosts"
42 #define NEED_H_ERRNO
43
44 #define EXTRA_ARGS      , af, flags
45 #define EXTRA_ARGS_DECL , int af, int flags
46
47 #define ENTDATA hostent_data
48 struct hostent_data
49   {
50     unsigned char host_addr[16];        /* IPv4 or IPv6 address.  */
51     char *h_addr_ptrs[2];       /* Points to that and null terminator.  */
52   };
53
54 #define TRAILING_LIST_MEMBER            h_aliases
55 #define TRAILING_LIST_SEPARATOR_P       isspace
56 #include <nss/nss_files/files-parse.c>
57 LINE_PARSER
58 ("#",
59  {
60    char *addr;
61
62    STRING_FIELD (addr, isspace, 1);
63
64    /* Parse address.  */
65    if (af == AF_INET && inet_pton (AF_INET, addr, entdata->host_addr) > 0)
66      {
67        if (flags & AI_V4MAPPED)
68          {
69            map_v4v6_address ((char *) entdata->host_addr,
70                              (char *) entdata->host_addr);
71            result->h_addrtype = AF_INET6;
72            result->h_length = IN6ADDRSZ;
73          }
74        else
75          {
76            result->h_addrtype = AF_INET;
77            result->h_length = INADDRSZ;
78          }
79      }
80    else if (af == AF_INET6
81             && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
82      {
83        result->h_addrtype = AF_INET6;
84        result->h_length = IN6ADDRSZ;
85      }
86    else
87      /* Illegal address: ignore line.  */
88      return 0;
89
90    /* Store a pointer to the address in the expected form.  */
91    entdata->h_addr_ptrs[0] = entdata->host_addr;
92    entdata->h_addr_ptrs[1] = NULL;
93    result->h_addr_list = entdata->h_addr_ptrs;
94
95    STRING_FIELD (result->h_name, isspace, 1);
96  })
97
98
99 __libc_lock_define_initialized (static, lock)
100
101 static bool_t new_start = 1;
102 static char *oldkey = NULL;
103 static int oldkeylen = 0;
104
105 enum nss_status
106 _nss_nis_sethostent (int stayopen)
107 {
108   __libc_lock_lock (lock);
109
110   new_start = 1;
111   if (oldkey != NULL)
112     {
113       free (oldkey);
114       oldkey = NULL;
115       oldkeylen = 0;
116     }
117
118   __libc_lock_unlock (lock);
119
120   return NSS_STATUS_SUCCESS;
121 }
122 /* Make _nss_nis_endhostent an alias of _nss_nis_sethostent.  We do this
123    even though the prototypes don't match.  The argument of sethostent
124    is used so this makes no difference.  */
125 strong_alias (_nss_nis_sethostent, _nss_nis_endhostent)
126
127 /* The calling function always need to get a lock first. */
128 static enum nss_status
129 internal_nis_gethostent_r (struct hostent *host, char *buffer,
130                            size_t buflen, int *errnop, int *h_errnop,
131                            int af, int flags)
132 {
133   char *domain;
134   char *result;
135   int len, parse_res;
136   char *outkey;
137   int keylen;
138   struct parser_data *data = (void *) buffer;
139   size_t linebuflen = buffer + buflen - data->linebuffer;
140
141   if (yp_get_default_domain (&domain))
142     return NSS_STATUS_UNAVAIL;
143
144   if (buflen < sizeof *data + 1)
145     {
146       *errnop = ERANGE;
147       *h_errnop = NETDB_INTERNAL;
148       return NSS_STATUS_TRYAGAIN;
149     }
150
151   /* Get the next entry until we found a correct one. */
152   do
153     {
154       enum nss_status retval;
155       char *p;
156
157       if (new_start)
158         retval = yperr2nss (yp_first (domain, "hosts.byname",
159                                       &outkey, &keylen, &result, &len));
160       else
161         retval = yperr2nss ( yp_next (domain, "hosts.byname",
162                                       oldkey, oldkeylen,
163                                       &outkey, &keylen, &result, &len));
164
165       if (retval != NSS_STATUS_SUCCESS)
166         {
167           switch (retval)
168             {
169             case NSS_STATUS_TRYAGAIN:
170               *errnop = errno;
171               *h_errnop = TRY_AGAIN;
172               break;
173             case NSS_STATUS_NOTFOUND:
174               *h_errnop = HOST_NOT_FOUND;
175               break;
176             default:
177               *h_errnop = NO_RECOVERY;
178               break;
179             }
180           return retval;
181         }
182
183       if ((size_t) (len + 1) > linebuflen)
184         {
185           free (result);
186           *h_errnop = NETDB_INTERNAL;
187           *errnop = ERANGE;
188           return NSS_STATUS_TRYAGAIN;
189         }
190
191       p = strncpy (data->linebuffer, result, len);
192       data->linebuffer[len] = '\0';
193       while (isspace (*p))
194         ++p;
195       free (result);
196
197       parse_res = parse_line (p, host, data, buflen, errnop, af, flags);
198       if (parse_res == -1)
199         {
200           free (outkey);
201           *h_errnop = NETDB_INTERNAL;
202           *errnop = ERANGE;
203           return NSS_STATUS_TRYAGAIN;
204         }
205       free (oldkey);
206       oldkey = outkey;
207       oldkeylen = keylen;
208       new_start = 0;
209     }
210   while (!parse_res);
211
212   *h_errnop = NETDB_SUCCESS;
213   return NSS_STATUS_SUCCESS;
214 }
215
216 enum nss_status
217 _nss_nis_gethostent_r (struct hostent *host, char *buffer, size_t buflen,
218                        int *errnop, int *h_errnop)
219 {
220   enum nss_status status;
221
222   __libc_lock_lock (lock);
223
224   status = internal_nis_gethostent_r (host, buffer, buflen, errnop, h_errnop,
225                         ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET),
226                         ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0 ));
227
228   __libc_lock_unlock (lock);
229
230   return status;
231 }
232
233 static enum nss_status
234 internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
235                            char *buffer, size_t buflen, int *errnop,
236                            int *h_errnop, int flags)
237 {
238   enum nss_status retval;
239   char *domain, *result, *p;
240   int len, parse_res;
241   struct parser_data *data = (void *) buffer;
242   size_t linebuflen = buffer + buflen - data->linebuffer;
243
244   if (name == NULL)
245     {
246       *errnop = EINVAL;
247       return NSS_STATUS_UNAVAIL;
248     }
249
250   if (yp_get_default_domain (&domain))
251     return NSS_STATUS_UNAVAIL;
252
253   if (buflen < sizeof *data + 1)
254     {
255       *h_errnop = NETDB_INTERNAL;
256       *errnop = ERANGE;
257       return NSS_STATUS_TRYAGAIN;
258     }
259   else
260     {
261       /* Convert name to lowercase.  */
262       size_t namlen = strlen (name);
263       char name2[namlen + 1];
264       size_t i;
265
266       for (i = 0; i < namlen; ++i)
267         name2[i] = tolower (name[i]);
268       name2[i] = '\0';
269
270       retval = yperr2nss (yp_match (domain, "hosts.byname", name2,
271                                     namlen, &result, &len));
272
273     }
274
275   if (retval != NSS_STATUS_SUCCESS)
276     {
277       if (retval == NSS_STATUS_TRYAGAIN)
278         {
279           *h_errnop = TRY_AGAIN;
280           *errnop = errno;
281         }
282       if (retval == NSS_STATUS_NOTFOUND)
283         *h_errnop = HOST_NOT_FOUND;
284       return retval;
285     }
286
287   if ((size_t) (len + 1) > linebuflen)
288     {
289       free (result);
290       *h_errnop = NETDB_INTERNAL;
291       *errnop = ERANGE;
292       return NSS_STATUS_TRYAGAIN;
293     }
294
295   p = strncpy (data->linebuffer, result, len);
296   data->linebuffer[len] = '\0';
297   while (isspace (*p))
298     ++p;
299   free (result);
300
301   parse_res = parse_line (p, host, data, buflen, errnop, af, flags);
302
303   if (parse_res < 1 || host->h_addrtype != af)
304     {
305       if (parse_res == -1)
306         {
307           *h_errnop = NETDB_INTERNAL;
308           return NSS_STATUS_TRYAGAIN;
309         }
310       else
311         {
312           *h_errnop = HOST_NOT_FOUND;
313           return NSS_STATUS_NOTFOUND;
314         }
315     }
316
317   *h_errnop = NETDB_SUCCESS;
318   return NSS_STATUS_SUCCESS;
319 }
320
321 enum nss_status
322 _nss_nis_gethostbyname2_r (const char *name, int af, struct hostent *host,
323                            char *buffer, size_t buflen, int *errnop,
324                            int *h_errnop)
325 {
326   return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
327                                     h_errnop,
328                         ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0));
329 }
330
331 enum nss_status
332 _nss_nis_gethostbyname_r (const char *name, struct hostent *host, char *buffer,
333                           size_t buflen, int *errnop, int *h_errnop)
334 {
335   if (_res.options & RES_USE_INET6)
336     {
337       enum nss_status status;
338
339       status = internal_gethostbyname2_r (name, AF_INET6, host, buffer, buflen,
340                                           errnop, h_errnop, AI_V4MAPPED);
341       if (status == NSS_STATUS_SUCCESS)
342         return status;
343     }
344
345   return internal_gethostbyname2_r (name, AF_INET, host, buffer, buflen,
346                                     errnop, h_errnop, 0);
347 }
348
349 enum nss_status
350 _nss_nis_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
351                           struct hostent *host, char *buffer, size_t buflen,
352                           int *errnop, int *h_errnop)
353 {
354   enum nss_status retval;
355   char *domain, *result, *p;
356   int len, parse_res;
357   char *buf;
358   struct parser_data *data = (void *) buffer;
359   size_t linebuflen = buffer + buflen - data->linebuffer;
360
361   if (yp_get_default_domain (&domain))
362     return NSS_STATUS_UNAVAIL;
363
364   if (buflen < sizeof *data + 1)
365     {
366       *errnop = ERANGE;
367       *h_errnop = NETDB_INTERNAL;
368       return NSS_STATUS_TRYAGAIN;
369     }
370
371   buf = inet_ntoa (*(const struct in_addr *) addr);
372
373   retval = yperr2nss (yp_match (domain, "hosts.byaddr", buf,
374                                 strlen (buf), &result, &len));
375
376   if (retval != NSS_STATUS_SUCCESS)
377     {
378       if (retval == NSS_STATUS_TRYAGAIN)
379         {
380           *h_errnop = TRY_AGAIN;
381           *errnop = errno;
382         }
383       if (retval == NSS_STATUS_NOTFOUND)
384         *h_errnop = HOST_NOT_FOUND;
385
386       return retval;
387     }
388
389   if ((size_t) (len + 1) > linebuflen)
390     {
391       free (result);
392       *errnop = ERANGE;
393       *h_errnop = NETDB_INTERNAL;
394       return NSS_STATUS_TRYAGAIN;
395     }
396
397   p = strncpy (data->linebuffer, result, len);
398   data->linebuffer[len] = '\0';
399   while (isspace (*p))
400     ++p;
401   free (result);
402
403   parse_res = parse_line (p, host, data, buflen, errnop, af,
404                           ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0));
405   if (parse_res < 1)
406     {
407       if (parse_res == -1)
408         {
409           *h_errnop = NETDB_INTERNAL;
410           return NSS_STATUS_TRYAGAIN;
411         }
412       else
413         {
414           *h_errnop = HOST_NOT_FOUND;
415           return NSS_STATUS_NOTFOUND;
416         }
417     }
418
419   *h_errnop = NETDB_SUCCESS;
420   return NSS_STATUS_SUCCESS;
421 }
422
423 #if 0
424 enum nss_status
425 _nss_nis_getipnodebyname_r (const char *name, int af, int flags,
426                             struct hostent *result, char *buffer,
427                             size_t buflen, int *errnop, int *herrnop)
428 {
429   return internal_gethostbyname2_r (name, af, result, buffer, buflen,
430                                     errnop, herrnop, flags);
431 }
432 #endif