1 /* Copyright (C) 1996-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 /* Parts of this file are plain copies of the file `getnetnamadr.c' from
19    the bind package and it has the following copyright.  */
20 
21 /* Copyright (c) 1993 Carlos Leandro and Rui Salgueiro
22  *      Dep. Matematica Universidade de Coimbra, Portugal, Europe
23  *
24  * Permission to use, copy, modify, and distribute this software for any
25  * purpose with or without fee is hereby granted, provided that the above
26  * copyright notice and this permission notice appear in all copies.
27  */
28 /*
29  * Copyright (c) 1983, 1993
30  *      The Regents of the University of California.  All rights reserved.
31  *
32  * Redistribution and use in source and binary forms, with or without
33  * modification, are permitted provided that the following conditions
34  * are met:
35  * 1. Redistributions of source code must retain the above copyright
36  *    notice, this list of conditions and the following disclaimer.
37  * 2. Redistributions in binary form must reproduce the above copyright
38  *    notice, this list of conditions and the following disclaimer in the
39  *    documentation and/or other materials provided with the distribution.
40  * 4. Neither the name of the University nor the names of its contributors
41  *    may be used to endorse or promote products derived from this software
42  *    without specific prior written permission.
43  *
44  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
45  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
48  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54  * SUCH DAMAGE.
55  */
56 
57 #include <ctype.h>
58 #include <errno.h>
59 #include <netdb.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <stdint.h>
64 #include <stddef.h>
65 
66 #include "nsswitch.h"
67 #include <arpa/inet.h>
68 #include <arpa/nameser.h>
69 #include <nss_dns.h>
70 #include <resolv/resolv-internal.h>
71 #include <resolv/resolv_context.h>
72 
73 /* Maximum number of aliases we allow.  */
74 #define MAX_NR_ALIASES	48
75 
76 
77 #if PACKETSZ > 65536
78 # define MAXPACKET	PACKETSZ
79 #else
80 # define MAXPACKET	65536
81 #endif
82 
83 
84 typedef enum
85 {
86   BYADDR,
87   BYNAME
88 } lookup_method;
89 
90 
91 /* We need this time later.  */
92 typedef union querybuf
93 {
94   HEADER hdr;
95   u_char buf[MAXPACKET];
96 } querybuf;
97 
98 /* Prototypes for local functions.  */
99 static enum nss_status getanswer_r (const querybuf *answer, int anslen,
100 				    struct netent *result, char *buffer,
101 				    size_t buflen, int *errnop, int *h_errnop,
102 				    lookup_method net_i);
103 
104 
105 enum nss_status
_nss_dns_getnetbyname_r(const char * name,struct netent * result,char * buffer,size_t buflen,int * errnop,int * herrnop)106 _nss_dns_getnetbyname_r (const char *name, struct netent *result,
107 			 char *buffer, size_t buflen, int *errnop,
108 			 int *herrnop)
109 {
110   /* Return entry for network with NAME.  */
111   union
112   {
113     querybuf *buf;
114     u_char *ptr;
115   } net_buffer;
116   querybuf *orig_net_buffer;
117   int anslen;
118   enum nss_status status;
119 
120   struct resolv_context *ctx = __resolv_context_get ();
121   if (ctx == NULL)
122     {
123       *errnop = errno;
124       *herrnop = NETDB_INTERNAL;
125       return NSS_STATUS_UNAVAIL;
126     }
127 
128   net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
129 
130   anslen = __res_context_search
131     (ctx, name, C_IN, T_PTR, net_buffer.buf->buf,
132      1024, &net_buffer.ptr, NULL, NULL, NULL, NULL);
133   if (anslen < 0)
134     {
135       /* Nothing found.  */
136       *errnop = errno;
137       if (net_buffer.buf != orig_net_buffer)
138 	free (net_buffer.buf);
139       __resolv_context_put (ctx);
140       return (errno == ECONNREFUSED
141 	      || errno == EPFNOSUPPORT
142 	      || errno == EAFNOSUPPORT)
143 	? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
144     }
145 
146   status = getanswer_r (net_buffer.buf, anslen, result, buffer, buflen,
147 			errnop, herrnop, BYNAME);
148   if (net_buffer.buf != orig_net_buffer)
149     free (net_buffer.buf);
150   __resolv_context_put (ctx);
151   return status;
152 }
libc_hidden_def(_nss_dns_getnetbyname_r)153 libc_hidden_def (_nss_dns_getnetbyname_r)
154 
155 enum nss_status
156 _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
157 			 char *buffer, size_t buflen, int *errnop,
158 			 int *herrnop)
159 {
160   /* Return entry for network with NAME.  */
161   enum nss_status status;
162   union
163   {
164     querybuf *buf;
165     u_char *ptr;
166   } net_buffer;
167   querybuf *orig_net_buffer;
168   unsigned int net_bytes[4];
169   char qbuf[MAXDNAME];
170   int cnt, anslen;
171   uint32_t net2;
172   int olderr = errno;
173 
174   /* No net address lookup for IPv6 yet.  */
175   if (type != AF_INET)
176     return NSS_STATUS_UNAVAIL;
177 
178   struct resolv_context *ctx = __resolv_context_get ();
179   if (ctx == NULL)
180     {
181       *errnop = errno;
182       *herrnop = NETDB_INTERNAL;
183       return NSS_STATUS_UNAVAIL;
184     }
185 
186   net2 = (uint32_t) net;
187   for (cnt = 4; net2 != 0; net2 >>= 8)
188     net_bytes[--cnt] = net2 & 0xff;
189 
190   switch (cnt)
191     {
192     case 3:
193       /* Class A network.  */
194       sprintf (qbuf, "0.0.0.%u.in-addr.arpa", net_bytes[3]);
195       break;
196     case 2:
197       /* Class B network.  */
198       sprintf (qbuf, "0.0.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2]);
199       break;
200     case 1:
201       /* Class C network.  */
202       sprintf (qbuf, "0.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2],
203 	       net_bytes[1]);
204       break;
205     case 0:
206       /* Class D - E network.  */
207       sprintf (qbuf, "%u.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2],
208 	       net_bytes[1], net_bytes[0]);
209       break;
210     }
211 
212   net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
213 
214   anslen = __res_context_query (ctx, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
215 				1024, &net_buffer.ptr, NULL, NULL, NULL, NULL);
216   if (anslen < 0)
217     {
218       /* Nothing found.  */
219       int err = errno;
220       __set_errno (olderr);
221       if (net_buffer.buf != orig_net_buffer)
222 	free (net_buffer.buf);
223       __resolv_context_put (ctx);
224       return (err == ECONNREFUSED
225 	      || err == EPFNOSUPPORT
226 	      || err == EAFNOSUPPORT)
227 	? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
228     }
229 
230   status = getanswer_r (net_buffer.buf, anslen, result, buffer, buflen,
231 			errnop, herrnop, BYADDR);
232   if (net_buffer.buf != orig_net_buffer)
233     free (net_buffer.buf);
234   if (status == NSS_STATUS_SUCCESS)
235     {
236       /* Strip trailing zeros.  */
237       unsigned int u_net = net;	/* Maybe net should be unsigned?  */
238 
239       while ((u_net & 0xff) == 0 && u_net != 0)
240 	u_net >>= 8;
241       result->n_net = u_net;
242     }
243 
244   __resolv_context_put (ctx);
245   return status;
246 }
libc_hidden_def(_nss_dns_getnetbyaddr_r)247 libc_hidden_def (_nss_dns_getnetbyaddr_r)
248 
249 static enum nss_status
250 getanswer_r (const querybuf *answer, int anslen, struct netent *result,
251 	     char *buffer, size_t buflen, int *errnop, int *h_errnop,
252 	     lookup_method net_i)
253 {
254   /*
255    * Find first satisfactory answer
256    *
257    *      answer --> +------------+  ( MESSAGE )
258    *                 |   Header   |
259    *                 +------------+
260    *                 |  Question  | the question for the name server
261    *                 +------------+
262    *                 |   Answer   | RRs answering the question
263    *                 +------------+
264    *                 | Authority  | RRs pointing toward an authority
265    *                 | Additional | RRs holding additional information
266    *                 +------------+
267    */
268   struct net_data
269   {
270     char *aliases[MAX_NR_ALIASES];
271     char linebuffer[0];
272   } *net_data;
273 
274   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct net_data);
275   buffer += pad;
276 
277   if (__glibc_unlikely (buflen < sizeof (*net_data) + pad))
278     {
279       /* The buffer is too small.  */
280     too_small:
281       *errnop = ERANGE;
282       *h_errnop = NETDB_INTERNAL;
283       return NSS_STATUS_TRYAGAIN;
284     }
285   buflen -= pad;
286 
287   net_data = (struct net_data *) buffer;
288   int linebuflen = buflen - offsetof (struct net_data, linebuffer);
289   if (buflen - offsetof (struct net_data, linebuffer) != linebuflen)
290     linebuflen = INT_MAX;
291   const unsigned char *end_of_message = &answer->buf[anslen];
292   const HEADER *header_pointer = &answer->hdr;
293   /* #/records in the answer section.  */
294   int answer_count =  ntohs (header_pointer->ancount);
295   /* #/entries in the question section.  */
296   int question_count = ntohs (header_pointer->qdcount);
297   char *bp = net_data->linebuffer;
298   const unsigned char *cp = &answer->buf[HFIXEDSZ];
299   char **alias_pointer;
300   int have_answer;
301   u_char packtmp[NS_MAXCDNAME];
302 
303   if (question_count == 0)
304     {
305       /* FIXME: the Sun version uses for host name lookup an additional
306 	 parameter for pointing to h_errno.  this is missing here.
307 	 OSF/1 has a per-thread h_errno variable.  */
308       if (header_pointer->aa != 0)
309 	{
310 	  __set_h_errno (HOST_NOT_FOUND);
311 	  return NSS_STATUS_NOTFOUND;
312 	}
313       else
314 	{
315 	  __set_h_errno (TRY_AGAIN);
316 	  return NSS_STATUS_TRYAGAIN;
317 	}
318     }
319 
320   /* Skip the question part.  */
321   while (question_count-- > 0)
322     {
323       int n = __libc_dn_skipname (cp, end_of_message);
324       if (n < 0 || end_of_message - (cp + n) < QFIXEDSZ)
325        {
326          __set_h_errno (NO_RECOVERY);
327          return NSS_STATUS_UNAVAIL;
328        }
329       cp += n + QFIXEDSZ;
330     }
331 
332   alias_pointer = result->n_aliases = &net_data->aliases[0];
333   *alias_pointer = NULL;
334   have_answer = 0;
335 
336   while (--answer_count >= 0 && cp < end_of_message)
337     {
338       int n = __ns_name_unpack (answer->buf, end_of_message, cp,
339 				packtmp, sizeof packtmp);
340       if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
341 	{
342 	  if (errno == EMSGSIZE)
343 	    goto too_small;
344 
345 	  n = -1;
346 	}
347 
348       if (n < 0 || __libc_res_dnok (bp) == 0)
349 	break;
350       cp += n;
351 
352       if (end_of_message - cp < 10)
353 	{
354 	  __set_h_errno (NO_RECOVERY);
355 	  return NSS_STATUS_UNAVAIL;
356 	}
357 
358       int type, class;
359       GETSHORT (type, cp);
360       GETSHORT (class, cp);
361       cp += INT32SZ;		/* TTL */
362       uint16_t rdatalen;
363       GETSHORT (rdatalen, cp);
364       if (end_of_message - cp < rdatalen)
365 	{
366 	  __set_h_errno (NO_RECOVERY);
367 	  return NSS_STATUS_UNAVAIL;
368 	}
369 
370       if (class == C_IN && type == T_PTR)
371 	{
372 	  n = __ns_name_unpack (answer->buf, end_of_message, cp,
373 				packtmp, sizeof packtmp);
374 	  if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
375 	    {
376 	      if (errno == EMSGSIZE)
377 		goto too_small;
378 
379 	      n = -1;
380 	    }
381 
382 	  if (n < 0 || !__libc_res_hnok (bp))
383 	    {
384 	      /* XXX What does this mean?  The original form from bind
385 		 returns NULL. Incrementing cp has no effect in any case.
386 		 What should I return here. ??? */
387 	      cp += n;
388 	      return NSS_STATUS_UNAVAIL;
389 	    }
390 	  cp += rdatalen;
391          if (alias_pointer + 2 < &net_data->aliases[MAX_NR_ALIASES])
392            {
393              *alias_pointer++ = bp;
394              n = strlen (bp) + 1;
395              bp += n;
396              linebuflen -= n;
397              result->n_addrtype = class == C_IN ? AF_INET : AF_UNSPEC;
398              ++have_answer;
399            }
400 	}
401       else
402 	/* Skip over unknown record data.  */
403 	cp += rdatalen;
404     }
405 
406   if (have_answer)
407     {
408       *alias_pointer = NULL;
409       switch (net_i)
410 	{
411 	case BYADDR:
412 	  result->n_name = *result->n_aliases++;
413 	  result->n_net = 0L;
414 	  return NSS_STATUS_SUCCESS;
415 
416 	case BYNAME:
417 	  {
418 	    char **ap;
419 	    for (ap = result->n_aliases; *ap != NULL; ++ap)
420 	      {
421 		/* Check each alias name for being of the forms:
422 		   4.3.2.1.in-addr.arpa		= net 1.2.3.4
423 		   3.2.1.in-addr.arpa		= net 0.1.2.3
424 		   2.1.in-addr.arpa		= net 0.0.1.2
425 		   1.in-addr.arpa		= net 0.0.0.1
426 		*/
427 		uint32_t val = 0;	/* Accumulator for n_net value.  */
428 		unsigned int shift = 0; /* Which part we are parsing now.  */
429 		const char *p = *ap; /* Consuming the string.  */
430 		do
431 		  {
432 		    /* Match the leading 0 or 0[xX] base indicator.  */
433 		    unsigned int base = 10;
434 		    if (*p == '0' && p[1] != '.')
435 		      {
436 			base = 8;
437 			++p;
438 			if (*p == 'x' || *p == 'X')
439 			  {
440 			    base = 16;
441 			    ++p;
442 			    if (*p == '.')
443 			      break; /* No digit here.  Give up on alias.  */
444 			  }
445 			if (*p == '\0')
446 			  break;
447 		      }
448 
449 		    uint32_t part = 0; /* Accumulates this part's number.  */
450 		    do
451 		      {
452 			if (isdigit (*p) && (*p - '0' < base))
453 			  part = (part * base) + (*p - '0');
454 			else if (base == 16 && isxdigit (*p))
455 			  part = (part << 4) + 10 + (tolower (*p) - 'a');
456 			++p;
457 		      } while (*p != '\0' && *p != '.');
458 
459 		    if (*p != '.')
460 		      break;	/* Bad form.  Give up on this name.  */
461 
462 		    /* Install this as the next more significant byte.  */
463 		    val |= part << shift;
464 		    shift += 8;
465 		    ++p;
466 
467 		    /* If we are out of digits now, there are two cases:
468 		       1. We are done with digits and now see "in-addr.arpa".
469 		       2. This is not the droid we are looking for.  */
470 		    if (!isdigit (*p) && !__strcasecmp (p, "in-addr.arpa"))
471 		      {
472 			result->n_net = val;
473 			return NSS_STATUS_SUCCESS;
474 		      }
475 
476 		    /* Keep going when we have seen fewer than 4 parts.  */
477 		  } while (shift < 32);
478 	      }
479 	  }
480 	  break;
481 	}
482     }
483 
484   __set_h_errno (TRY_AGAIN);
485   return NSS_STATUS_TRYAGAIN;
486 }
487