• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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