1 /* Copyright (C) 2004-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 #include <errno.h>
19 #include <netdb.h>
20 #include <resolv.h>
21 #include <stdlib.h>
22 #include <stdint.h>
23 #include <arpa/nameser.h>
24 #include <nsswitch.h>
25 #include <resolv/resolv_context.h>
26 #include <resolv/resolv-internal.h>
27 #include <nss_dns.h>
28 
29 #if PACKETSZ > 65536
30 # define MAXPACKET	PACKETSZ
31 #else
32 # define MAXPACKET	65536
33 #endif
34 
35 
36 /* We need this time later.  */
37 typedef union querybuf
38 {
39   HEADER hdr;
40   unsigned char buf[MAXPACKET];
41 } querybuf;
42 
43 
44 static const short int qtypes[] = { ns_t_a, ns_t_aaaa };
45 #define nqtypes (sizeof (qtypes) / sizeof (qtypes[0]))
46 
47 
48 enum nss_status
_nss_dns_getcanonname_r(const char * name,char * buffer,size_t buflen,char ** result,int * errnop,int * h_errnop)49 _nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen,
50 			 char **result,int *errnop, int *h_errnop)
51 {
52   /* Just an alibi buffer, res_nquery will allocate a real buffer for
53      us.  */
54   unsigned char buf[20];
55   union
56   {
57     querybuf *buf;
58     unsigned char *ptr;
59   } ansp = { .ptr = buf };
60   enum nss_status status = NSS_STATUS_UNAVAIL;
61 
62   struct resolv_context *ctx = __resolv_context_get ();
63   if (ctx == NULL)
64     {
65       *errnop = errno;
66       *h_errnop = NETDB_INTERNAL;
67       return NSS_STATUS_UNAVAIL;
68     }
69 
70   for (int i = 0; i < nqtypes; ++i)
71     {
72       int r = __res_context_query (ctx, name, ns_c_in, qtypes[i],
73 				   buf, sizeof (buf), &ansp.ptr, NULL, NULL,
74 				   NULL, NULL);
75       if (r > 0)
76 	{
77 	  /* We need to decode the response.  Just one question record.
78 	     And if we got no answers we bail out, too.  */
79 	  if (ansp.buf->hdr.qdcount != htons (1))
80 	    continue;
81 
82 	  /* Number of answers.   */
83 	  unsigned int ancount = ntohs (ansp.buf->hdr.ancount);
84 
85 	  /* Beginning and end of the buffer with query, answer, and the
86 	     rest.  */
87 	  unsigned char *ptr = &ansp.buf->buf[sizeof (HEADER)];
88 	  unsigned char *endptr = ansp.ptr + r;
89 
90 	  /* Skip over the query.  This is the name, type, and class.  */
91 	  int s = __libc_dn_skipname (ptr, endptr);
92 	  if (s < 0)
93 	    {
94 	    unavail:
95 	      status = NSS_STATUS_UNAVAIL;
96 	      break;
97 	    }
98 
99 	  /* Skip over the name and the two 16-bit values containing type
100 	     and class.  */
101 	  ptr += s + 2 * sizeof (uint16_t);
102 
103 	  while (ancount-- > 0)
104 	    {
105 	      /* Now the reply.  First again the name from the query,
106 		 then type, class, TTL, and the length of the RDATA.
107 		 We remember the name start.  */
108 	      unsigned char *namestart = ptr;
109 	      s = __libc_dn_skipname (ptr, endptr);
110 	      if (s < 0)
111 		goto unavail;
112 
113 	      ptr += s;
114 
115 	      /* Check that there are enough bytes for the RR
116 		 metadata.  */
117 	      if (endptr - ptr < 10)
118 		goto unavail;
119 
120 	      /* Check whether type and class match.  */
121 	      short int type;
122 	      NS_GET16 (type, ptr);
123 	      if (type == qtypes[i])
124 		{
125 		  /* We found the record.  */
126 		  s = __libc_dn_expand (ansp.buf->buf, endptr, namestart,
127 					buffer, buflen);
128 		  if (s < 0)
129 		    {
130 		      if (errno != EMSGSIZE)
131 			goto unavail;
132 
133 		      /* The buffer is too small.  */
134 		      *errnop = ERANGE;
135 		      status = NSS_STATUS_TRYAGAIN;
136 		      h_errno = NETDB_INTERNAL;
137 		    }
138 		  else
139 		    {
140 		      /* Success.  */
141 		      *result = buffer;
142 		      status = NSS_STATUS_SUCCESS;
143 		    }
144 
145 		  goto out;
146 		}
147 
148 	      if (type != ns_t_cname)
149 		goto unavail;
150 
151 	      uint16_t rrclass;
152 	      NS_GET16 (rrclass, ptr);
153 	      if (rrclass != ns_c_in)
154 		goto unavail;
155 
156 	      /* Skip over TTL.  */
157 	      ptr += sizeof (uint32_t);
158 
159 	      /* Skip over RDATA length and RDATA itself.  */
160 	      uint16_t rdatalen;
161 	      NS_GET16 (rdatalen, ptr);
162 
163 	      /* Not enough room for RDATA.  */
164 	      if (endptr - ptr < rdatalen)
165 		goto unavail;
166 	      ptr += rdatalen;
167 	    }
168 	}
169 
170       /* Restore original buffer before retry.  */
171       if (ansp.ptr != buf)
172 	{
173 	  free (ansp.ptr);
174 	  ansp.ptr = buf;
175 	}
176     }
177 
178  out:
179   *h_errnop = h_errno;
180 
181   if (ansp.ptr != buf)
182     free (ansp.ptr);
183   __resolv_context_put (ctx);
184   return status;
185 }
186 libc_hidden_def (_nss_dns_getcanonname_r)
187