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