1 /* MIT License
2 *
3 * Copyright (c) 1998 Massachusetts Institute of Technology
4 * Copyright (c) The c-ares project and its contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 * SPDX-License-Identifier: MIT
26 */
27
28 #include "ares_private.h"
29
30 #ifdef HAVE_NETINET_IN_H
31 # include <netinet/in.h>
32 #endif
33 #include "ares_nameser.h"
34
generate_unique_qid(ares_channel_t * channel)35 static unsigned short generate_unique_qid(ares_channel_t *channel)
36 {
37 unsigned short id;
38
39 do {
40 id = ares_generate_new_id(channel->rand_state);
41 } while (ares_htable_szvp_get(channel->queries_by_qid, id, NULL));
42
43 return id;
44 }
45
46 /* https://datatracker.ietf.org/doc/html/draft-vixie-dnsext-dns0x20-00 */
ares_apply_dns0x20(ares_channel_t * channel,ares_dns_record_t * dnsrec)47 static ares_status_t ares_apply_dns0x20(ares_channel_t *channel,
48 ares_dns_record_t *dnsrec)
49 {
50 ares_status_t status = ARES_SUCCESS;
51 const char *name = NULL;
52 char dns0x20name[256];
53 unsigned char randdata[256 / 8];
54 size_t len;
55 size_t remaining_bits;
56 size_t total_bits;
57 size_t i;
58
59 status = ares_dns_record_query_get(dnsrec, 0, &name, NULL, NULL);
60 if (status != ARES_SUCCESS) {
61 goto done;
62 }
63
64 len = ares_strlen(name);
65 if (len == 0) {
66 return ARES_SUCCESS;
67 }
68
69 if (len >= sizeof(dns0x20name)) {
70 status = ARES_EBADNAME;
71 goto done;
72 }
73
74 memset(dns0x20name, 0, sizeof(dns0x20name));
75
76 /* Fetch the minimum amount of random data we'd need for the string, which
77 * is 1 bit per byte */
78 total_bits = ((len + 7) / 8) * 8;
79 remaining_bits = total_bits;
80 ares_rand_bytes(channel->rand_state, randdata, total_bits / 8);
81
82 /* Randomly apply 0x20 to name */
83 for (i = 0; i < len; i++) {
84 size_t bit;
85
86 /* Only apply 0x20 to alpha characters */
87 if (!ares_isalpha(name[i])) {
88 dns0x20name[i] = name[i];
89 continue;
90 }
91
92 /* coin flip */
93 bit = total_bits - remaining_bits;
94 if (randdata[bit / 8] & (1 << (bit % 8))) {
95 dns0x20name[i] = name[i] | 0x20; /* Set 0x20 */
96 } else {
97 dns0x20name[i] = (char)(((unsigned char)name[i]) & 0xDF); /* Unset 0x20 */
98 }
99 remaining_bits--;
100 }
101
102 status = ares_dns_record_query_set_name(dnsrec, 0, dns0x20name);
103
104 done:
105 return status;
106 }
107
ares_send_nolock(ares_channel_t * channel,ares_server_t * server,ares_send_flags_t flags,const ares_dns_record_t * dnsrec,ares_callback_dnsrec callback,void * arg,unsigned short * qid)108 ares_status_t ares_send_nolock(ares_channel_t *channel, ares_server_t *server,
109 ares_send_flags_t flags,
110 const ares_dns_record_t *dnsrec,
111 ares_callback_dnsrec callback, void *arg,
112 unsigned short *qid)
113 {
114 ares_query_t *query;
115 ares_timeval_t now;
116 ares_status_t status;
117 unsigned short id = generate_unique_qid(channel);
118 const ares_dns_record_t *dnsrec_resp = NULL;
119
120 ares_tvnow(&now);
121
122 if (ares_slist_len(channel->servers) == 0) {
123 callback(arg, ARES_ENOSERVER, 0, NULL);
124 return ARES_ENOSERVER;
125 }
126
127 if (!(flags & ARES_SEND_FLAG_NOCACHE)) {
128 /* Check query cache */
129 status = ares_qcache_fetch(channel, &now, dnsrec, &dnsrec_resp);
130 if (status != ARES_ENOTFOUND) {
131 /* ARES_SUCCESS means we retrieved the cache, anything else is a critical
132 * failure, all result in termination */
133 callback(arg, status, 0, dnsrec_resp);
134 return status;
135 }
136 }
137
138 /* Allocate space for query and allocated fields. */
139 query = ares_malloc(sizeof(ares_query_t));
140 if (!query) {
141 callback(arg, ARES_ENOMEM, 0, NULL); /* LCOV_EXCL_LINE: OutOfMemory */
142 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
143 }
144 memset(query, 0, sizeof(*query));
145
146 query->channel = channel;
147 query->qid = id;
148 query->timeout.sec = 0;
149 query->timeout.usec = 0;
150 query->using_tcp =
151 (channel->flags & ARES_FLAG_USEVC) ? ARES_TRUE : ARES_FALSE;
152
153 /* Duplicate Query */
154 status = ares_dns_record_duplicate_ex(&query->query, dnsrec);
155 if (status != ARES_SUCCESS) {
156 /* Sometimes we might get a EBADRESP response from duplicate due to
157 * the way it works (write and parse), rewrite it to EBADQUERY. */
158 if (status == ARES_EBADRESP) {
159 status = ARES_EBADQUERY;
160 }
161 ares_free(query);
162 callback(arg, status, 0, NULL);
163 return status;
164 }
165
166 ares_dns_record_set_id(query->query, id);
167
168 if (channel->flags & ARES_FLAG_DNS0x20 && !query->using_tcp) {
169 status = ares_apply_dns0x20(channel, query->query);
170 if (status != ARES_SUCCESS) {
171 /* LCOV_EXCL_START: OutOfMemory */
172 callback(arg, status, 0, NULL);
173 ares_free_query(query);
174 return status;
175 /* LCOV_EXCL_STOP */
176 }
177 }
178
179 /* Fill in query arguments. */
180 query->callback = callback;
181 query->arg = arg;
182
183 /* Initialize query status. */
184 query->try_count = 0;
185
186 if (flags & ARES_SEND_FLAG_NORETRY) {
187 query->no_retries = ARES_TRUE;
188 }
189
190 query->error_status = ARES_SUCCESS;
191 query->timeouts = 0;
192
193 /* Initialize our list nodes. */
194 query->node_queries_by_timeout = NULL;
195 query->node_queries_to_conn = NULL;
196
197 /* Chain the query into the list of all queries. */
198 query->node_all_queries = ares_llist_insert_last(channel->all_queries, query);
199 if (query->node_all_queries == NULL) {
200 /* LCOV_EXCL_START: OutOfMemory */
201 callback(arg, ARES_ENOMEM, 0, NULL);
202 ares_free_query(query);
203 return ARES_ENOMEM;
204 /* LCOV_EXCL_STOP */
205 }
206
207 /* Keep track of queries bucketed by qid, so we can process DNS
208 * responses quickly.
209 */
210 if (!ares_htable_szvp_insert(channel->queries_by_qid, query->qid, query)) {
211 /* LCOV_EXCL_START: OutOfMemory */
212 callback(arg, ARES_ENOMEM, 0, NULL);
213 ares_free_query(query);
214 return ARES_ENOMEM;
215 /* LCOV_EXCL_STOP */
216 }
217
218 /* Perform the first query action. */
219
220 status = ares_send_query(server, query, &now);
221 if (status == ARES_SUCCESS && qid) {
222 *qid = id;
223 }
224 return status;
225 }
226
ares_send_dnsrec(ares_channel_t * channel,const ares_dns_record_t * dnsrec,ares_callback_dnsrec callback,void * arg,unsigned short * qid)227 ares_status_t ares_send_dnsrec(ares_channel_t *channel,
228 const ares_dns_record_t *dnsrec,
229 ares_callback_dnsrec callback, void *arg,
230 unsigned short *qid)
231 {
232 ares_status_t status;
233
234 if (channel == NULL) {
235 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
236 }
237
238 ares_channel_lock(channel);
239
240 status = ares_send_nolock(channel, NULL, 0, dnsrec, callback, arg, qid);
241
242 ares_channel_unlock(channel);
243
244 return status;
245 }
246
ares_send(ares_channel_t * channel,const unsigned char * qbuf,int qlen,ares_callback callback,void * arg)247 void ares_send(ares_channel_t *channel, const unsigned char *qbuf, int qlen,
248 ares_callback callback, void *arg)
249 {
250 ares_dns_record_t *dnsrec = NULL;
251 ares_status_t status;
252 void *carg = NULL;
253
254 if (channel == NULL) {
255 return;
256 }
257
258 /* Verify that the query is at least long enough to hold the header. */
259 if (qlen < HFIXEDSZ || qlen >= (1 << 16)) {
260 callback(arg, ARES_EBADQUERY, 0, NULL, 0);
261 return;
262 }
263
264 status = ares_dns_parse(qbuf, (size_t)qlen, 0, &dnsrec);
265 if (status != ARES_SUCCESS) {
266 callback(arg, (int)status, 0, NULL, 0);
267 return;
268 }
269
270 carg = ares_dnsrec_convert_arg(callback, arg);
271 if (carg == NULL) {
272 /* LCOV_EXCL_START: OutOfMemory */
273 status = ARES_ENOMEM;
274 ares_dns_record_destroy(dnsrec);
275 callback(arg, (int)status, 0, NULL, 0);
276 return;
277 /* LCOV_EXCL_STOP */
278 }
279
280 ares_send_dnsrec(channel, dnsrec, ares_dnsrec_convert_cb, carg, NULL);
281
282 ares_dns_record_destroy(dnsrec);
283 }
284
ares_queue_active_queries(const ares_channel_t * channel)285 size_t ares_queue_active_queries(const ares_channel_t *channel)
286 {
287 size_t len;
288
289 if (channel == NULL) {
290 return 0;
291 }
292
293 ares_channel_lock(channel);
294
295 len = ares_llist_len(channel->all_queries);
296
297 ares_channel_unlock(channel);
298
299 return len;
300 }
301