eb074783f71e97b6a1e4801af4ae425bbfd1b548
[platform/upstream/glibc.git] / nis / nss_nis / nis-hosts.c
1 /* Copyright (C) 1996-2020 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, see
17    <https://www.gnu.org/licenses/>.  */
18
19 #include <assert.h>
20 #include <nss.h>
21 #include <ctype.h>
22 #include <netdb.h>
23 #include <string.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <libc-lock.h>
27 #include <rpcsvc/yp.h>
28 #include <rpcsvc/ypclnt.h>
29
30 #include "nss-nis.h"
31
32 #define ENTNAME         hostent
33 #define DATABASE        "hosts"
34 #define NEED_H_ERRNO
35
36 #define EXTRA_ARGS      , af
37 #define EXTRA_ARGS_DECL , int af
38
39 #define ENTDATA hostent_data
40 struct hostent_data
41   {
42     unsigned char host_addr[16];        /* IPv4 or IPv6 address.  */
43     char *h_addr_ptrs[2];       /* Points to that and null terminator.  */
44   };
45
46 #define TRAILING_LIST_MEMBER            h_aliases
47 #define TRAILING_LIST_SEPARATOR_P       isspace
48 #include <nss/nss_files/files-parse.c>
49 LINE_PARSER
50 ("#",
51  {
52    char *addr;
53
54    STRING_FIELD (addr, isspace, 1);
55
56    assert (af == AF_INET || af == AF_INET6 || af == AF_UNSPEC);
57
58    /* Parse address.  */
59    if (af != AF_INET6 && inet_pton (AF_INET, addr, entdata->host_addr) > 0)
60      {
61        result->h_addrtype = AF_INET;
62        result->h_length = INADDRSZ;
63      }
64    else if (af != AF_INET
65             && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
66      {
67        result->h_addrtype = AF_INET6;
68        result->h_length = IN6ADDRSZ;
69      }
70    else
71      /* Illegal address: ignore line.  */
72      return 0;
73
74    /* Store a pointer to the address in the expected form.  */
75    entdata->h_addr_ptrs[0] = (char *) entdata->host_addr;
76    entdata->h_addr_ptrs[1] = NULL;
77    result->h_addr_list = entdata->h_addr_ptrs;
78
79    STRING_FIELD (result->h_name, isspace, 1);
80  })
81
82
83 __libc_lock_define_initialized (static, lock)
84
85 static bool_t new_start = 1;
86 static char *oldkey = NULL;
87 static int oldkeylen = 0;
88
89 enum nss_status
90 _nss_nis_sethostent (int stayopen)
91 {
92   return _nss_nis_endhostent ();
93 }
94
95 enum nss_status
96 _nss_nis_endhostent (void)
97 {
98   __libc_lock_lock (lock);
99
100   new_start = 1;
101   if (oldkey != NULL)
102     {
103       free (oldkey);
104       oldkey = NULL;
105       oldkeylen = 0;
106     }
107
108   __libc_lock_unlock (lock);
109
110   return NSS_STATUS_SUCCESS;
111 }
112 libnss_nis_hidden_def (_nss_nis_endhostent)
113
114 /* The calling function always need to get a lock first. */
115 static enum nss_status
116 internal_nis_gethostent_r (struct hostent *host, char *buffer,
117                            size_t buflen, int *errnop, int *h_errnop,
118                            int af)
119 {
120   char *domain;
121   if (__glibc_unlikely (yp_get_default_domain (&domain)))
122     return NSS_STATUS_UNAVAIL;
123
124   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct parser_data);
125   buffer += pad;
126
127   struct parser_data *data = (void *) buffer;
128   if (__glibc_unlikely (buflen < sizeof *data + 1 + pad))
129     {
130       *errnop = ERANGE;
131       *h_errnop = NETDB_INTERNAL;
132       return NSS_STATUS_TRYAGAIN;
133     }
134   buflen -= pad;
135
136   /* Get the next entry until we found a correct one. */
137   const size_t linebuflen = buffer + buflen - data->linebuffer;
138   int parse_res;
139   do
140     {
141       char *result;
142       int len;
143       char *outkey;
144       int keylen;
145       int yperr;
146       if (new_start)
147         yperr = yp_first (domain, "hosts.byname", &outkey, &keylen, &result,
148                           &len);
149       else
150         yperr = yp_next (domain, "hosts.byname", oldkey, oldkeylen, &outkey,
151                          &keylen, &result, &len);
152
153       if (__glibc_unlikely (yperr != YPERR_SUCCESS))
154         {
155           enum nss_status retval = yperr2nss (yperr);
156
157           switch (retval)
158             {
159             case NSS_STATUS_TRYAGAIN:
160               *errnop = errno;
161               *h_errnop = TRY_AGAIN;
162               break;
163             case NSS_STATUS_NOTFOUND:
164               *h_errnop = HOST_NOT_FOUND;
165               break;
166             default:
167               *h_errnop = NO_RECOVERY;
168               break;
169             }
170           return retval;
171         }
172
173       if (__glibc_unlikely ((size_t) (len + 1) > linebuflen))
174         {
175           free (result);
176           *h_errnop = NETDB_INTERNAL;
177           *errnop = ERANGE;
178           return NSS_STATUS_TRYAGAIN;
179         }
180
181       char *p = strncpy (data->linebuffer, result, len);
182       data->linebuffer[len] = '\0';
183       while (isspace (*p))
184         ++p;
185       free (result);
186
187       parse_res = parse_line (p, host, data, buflen, errnop, af);
188       if (__glibc_unlikely (parse_res == -1))
189         {
190           free (outkey);
191           *h_errnop = NETDB_INTERNAL;
192           *errnop = ERANGE;
193           return NSS_STATUS_TRYAGAIN;
194         }
195       free (oldkey);
196       oldkey = outkey;
197       oldkeylen = keylen;
198       new_start = 0;
199     }
200   while (!parse_res);
201
202   *h_errnop = NETDB_SUCCESS;
203   return NSS_STATUS_SUCCESS;
204 }
205
206
207 enum nss_status
208 _nss_nis_gethostent_r (struct hostent *host, char *buffer, size_t buflen,
209                        int *errnop, int *h_errnop)
210 {
211   enum nss_status status;
212
213   __libc_lock_lock (lock);
214
215   status = internal_nis_gethostent_r (host, buffer, buflen, errnop, h_errnop,
216                                       AF_INET);
217
218   __libc_lock_unlock (lock);
219
220   return status;
221 }
222
223
224 static enum nss_status
225 internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
226                            char *buffer, size_t buflen, int *errnop,
227                            int *h_errnop)
228 {
229   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct parser_data);
230   buffer += pad;
231
232   struct parser_data *data = (void *) buffer;
233
234   if (name == NULL)
235     {
236       *errnop = EINVAL;
237       return NSS_STATUS_UNAVAIL;
238     }
239
240   char *domain;
241   if (yp_get_default_domain (&domain))
242     return NSS_STATUS_UNAVAIL;
243
244   if (buflen < sizeof *data + 1 + pad)
245     {
246       *h_errnop = NETDB_INTERNAL;
247       *errnop = ERANGE;
248       return NSS_STATUS_TRYAGAIN;
249     }
250   buflen -= pad;
251
252   /* Convert name to lowercase.  */
253   size_t namlen = strlen (name);
254   /* Limit name length to the maximum size of an RPC packet.  */
255   if (namlen > UDPMSGSIZE)
256     {
257       *errnop = ERANGE;
258       return NSS_STATUS_UNAVAIL;
259     }
260
261   char name2[namlen + 1];
262   size_t i;
263
264   for (i = 0; i < namlen; ++i)
265     name2[i] = tolower (name[i]);
266   name2[i] = '\0';
267
268   char *result;
269   int len;
270   int yperr = yp_match (domain, "hosts.byname", name2, namlen, &result, &len);
271
272   if (__glibc_unlikely (yperr != YPERR_SUCCESS))
273     {
274       enum nss_status retval = yperr2nss (yperr);
275
276       if (retval == NSS_STATUS_TRYAGAIN)
277         {
278           *h_errnop = TRY_AGAIN;
279           *errnop = errno;
280         }
281       if (retval == NSS_STATUS_NOTFOUND)
282         *h_errnop = HOST_NOT_FOUND;
283       return retval;
284     }
285
286   const size_t linebuflen = buffer + buflen - data->linebuffer;
287   if (__glibc_unlikely ((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   char *p = strncpy (data->linebuffer, result, len);
296   data->linebuffer[len] = '\0';
297   while (isspace (*p))
298     ++p;
299   free (result);
300
301   int parse_res = parse_line (p, host, data, buflen, errnop, af);
302
303   if (__glibc_unlikely (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
322 enum nss_status
323 _nss_nis_gethostbyname2_r (const char *name, int af, struct hostent *host,
324                            char *buffer, size_t buflen, int *errnop,
325                            int *h_errnop)
326 {
327   if (af != AF_INET && af != AF_INET6)
328     {
329       *h_errnop = HOST_NOT_FOUND;
330       return NSS_STATUS_NOTFOUND;
331     }
332
333   return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
334                                     h_errnop);
335 }
336
337
338 enum nss_status
339 _nss_nis_gethostbyname_r (const char *name, struct hostent *host, char *buffer,
340                           size_t buflen, int *errnop, int *h_errnop)
341 {
342   return internal_gethostbyname2_r (name, AF_INET, host, buffer, buflen,
343                                     errnop, h_errnop);
344 }
345
346
347 enum nss_status
348 _nss_nis_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
349                           struct hostent *host, char *buffer, size_t buflen,
350                           int *errnop, int *h_errnop)
351 {
352   char *domain;
353   if (__glibc_unlikely (yp_get_default_domain (&domain)))
354     return NSS_STATUS_UNAVAIL;
355
356   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct parser_data);
357   buffer += pad;
358
359   struct parser_data *data = (void *) buffer;
360   if (__glibc_unlikely (buflen < sizeof *data + 1 + pad))
361     {
362       *errnop = ERANGE;
363       *h_errnop = NETDB_INTERNAL;
364       return NSS_STATUS_TRYAGAIN;
365     }
366   buflen -= pad;
367
368   char *buf = inet_ntoa (*(const struct in_addr *) addr);
369
370   char *result;
371   int len;
372   int yperr = yp_match (domain, "hosts.byaddr", buf, strlen (buf), &result,
373                         &len);
374
375   if (__glibc_unlikely (yperr != YPERR_SUCCESS))
376     {
377       enum nss_status retval = yperr2nss (yperr);
378
379       if (retval == NSS_STATUS_TRYAGAIN)
380         {
381           *h_errnop = TRY_AGAIN;
382           *errnop = errno;
383         }
384       else if (retval == NSS_STATUS_NOTFOUND)
385         *h_errnop = HOST_NOT_FOUND;
386
387       return retval;
388     }
389
390   const size_t linebuflen = buffer + buflen - data->linebuffer;
391   if (__glibc_unlikely ((size_t) (len + 1) > linebuflen))
392     {
393       free (result);
394       *errnop = ERANGE;
395       *h_errnop = NETDB_INTERNAL;
396       return NSS_STATUS_TRYAGAIN;
397     }
398
399   char *p = strncpy (data->linebuffer, result, len);
400   data->linebuffer[len] = '\0';
401   while (isspace (*p))
402     ++p;
403   free (result);
404
405   int parse_res = parse_line (p, host, data, buflen, errnop, af);
406   if (__glibc_unlikely (parse_res < 1))
407     {
408       if (parse_res == -1)
409         {
410           *h_errnop = NETDB_INTERNAL;
411           return NSS_STATUS_TRYAGAIN;
412         }
413       else
414         {
415           *h_errnop = HOST_NOT_FOUND;
416           return NSS_STATUS_NOTFOUND;
417         }
418     }
419
420   *h_errnop = NETDB_SUCCESS;
421   return NSS_STATUS_SUCCESS;
422 }
423
424
425 enum nss_status
426 _nss_nis_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
427                            char *buffer, size_t buflen, int *errnop,
428                            int *herrnop, int32_t *ttlp)
429 {
430   char *domain;
431   if (yp_get_default_domain (&domain))
432     {
433       *herrnop = NO_DATA;
434       return NSS_STATUS_UNAVAIL;
435     }
436
437   /* Convert name to lowercase.  */
438   size_t namlen = strlen (name);
439   /* Limit name length to the maximum size of an RPC packet.  */
440   if (namlen > UDPMSGSIZE)
441     {
442       *errnop = ERANGE;
443       return NSS_STATUS_UNAVAIL;
444     }
445
446   char name2[namlen + 1];
447   size_t i;
448
449   for (i = 0; i < namlen; ++i)
450     name2[i] = tolower (name[i]);
451   name2[i] = '\0';
452
453   char *result;
454   int len;
455   int yperr = yp_match (domain, "hosts.byname", name2, namlen, &result, &len);
456
457   if (__glibc_unlikely (yperr != YPERR_SUCCESS))
458     {
459       enum nss_status retval = yperr2nss (yperr);
460
461       if (retval == NSS_STATUS_TRYAGAIN)
462         {
463           *herrnop = TRY_AGAIN;
464           *errnop = errno;
465         }
466       if (retval == NSS_STATUS_NOTFOUND)
467         *herrnop = HOST_NOT_FOUND;
468       return retval;
469     }
470
471   if (*pat == NULL)
472     {
473       uintptr_t pad = (-(uintptr_t) buffer
474                        % __alignof__ (struct gaih_addrtuple));
475       buffer += pad;
476       buflen = buflen > pad ? buflen - pad : 0;
477
478       if (__glibc_unlikely (buflen < sizeof (struct gaih_addrtuple)))
479         {
480         erange:
481           free (result);
482           *errnop = ERANGE;
483           *herrnop = NETDB_INTERNAL;
484           return NSS_STATUS_TRYAGAIN;
485         }
486
487       *pat = (struct gaih_addrtuple *) buffer;
488       buffer += sizeof (struct gaih_addrtuple);
489       buflen -= sizeof (struct gaih_addrtuple);
490     }
491
492   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct parser_data);
493   buffer += pad;
494
495   struct parser_data *data = (void *) buffer;
496
497   if (__glibc_unlikely (buflen < sizeof *data + 1 + pad))
498     goto erange;
499   buflen -= pad;
500
501   struct hostent host;
502   int parse_res = parse_line (result, &host, data, buflen, errnop, AF_UNSPEC);
503   if (__glibc_unlikely (parse_res < 1))
504     {
505       if (parse_res == -1)
506         {
507           *herrnop = NETDB_INTERNAL;
508           return NSS_STATUS_TRYAGAIN;
509         }
510       else
511         {
512           *herrnop = HOST_NOT_FOUND;
513           return NSS_STATUS_NOTFOUND;
514         }
515     }
516
517   (*pat)->next = NULL;
518   (*pat)->family = host.h_addrtype;
519   memcpy ((*pat)->addr, host.h_addr_list[0], host.h_length);
520   (*pat)->scopeid = 0;
521   assert (host.h_addr_list[1] == NULL);
522
523   /* Undo the alignment for parser_data.  */
524   buffer -= pad;
525   buflen += pad;
526
527   size_t h_name_len = strlen (host.h_name) + 1;
528   if (h_name_len >= buflen)
529     goto erange;
530   (*pat)->name = memcpy (buffer, host.h_name, h_name_len);
531
532   free (result);
533
534   return NSS_STATUS_SUCCESS;
535 }