Coverage Report

Created: 2025-12-05 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/c-ares/src/lib/ares_send.c
Line
Count
Source
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
35
static unsigned short generate_unique_qid(ares_channel_t *channel)
36
0
{
37
0
  unsigned short id;
38
39
0
  do {
40
0
    id = ares_generate_new_id(channel->rand_state);
41
0
  } while (ares_htable_szvp_get(channel->queries_by_qid, id, NULL));
42
43
0
  return id;
44
0
}
45
46
/* https://datatracker.ietf.org/doc/html/draft-vixie-dnsext-dns0x20-00 */
47
static ares_status_t ares_apply_dns0x20(ares_channel_t    *channel,
48
                                        ares_dns_record_t *dnsrec)
49
0
{
50
0
  ares_status_t status = ARES_SUCCESS;
51
0
  const char   *name   = NULL;
52
0
  char          dns0x20name[256];
53
0
  unsigned char randdata[256 / 8];
54
0
  size_t        len;
55
0
  size_t        remaining_bits;
56
0
  size_t        total_bits;
57
0
  size_t        i;
58
59
0
  status = ares_dns_record_query_get(dnsrec, 0, &name, NULL, NULL);
60
0
  if (status != ARES_SUCCESS) {
61
0
    goto done;
62
0
  }
63
64
0
  len = ares_strlen(name);
65
0
  if (len == 0) {
66
0
    return ARES_SUCCESS;
67
0
  }
68
69
0
  if (len >= sizeof(dns0x20name)) {
70
0
    status = ARES_EBADNAME;
71
0
    goto done;
72
0
  }
73
74
0
  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
0
  total_bits     = ((len + 7) / 8) * 8;
79
0
  remaining_bits = total_bits;
80
0
  ares_rand_bytes(channel->rand_state, randdata, total_bits / 8);
81
82
  /* Randomly apply 0x20 to name */
83
0
  for (i = 0; i < len; i++) {
84
0
    size_t bit;
85
86
    /* Only apply 0x20 to alpha characters */
87
0
    if (!ares_isalpha(name[i])) {
88
0
      dns0x20name[i] = name[i];
89
0
      continue;
90
0
    }
91
92
    /* coin flip */
93
0
    bit = total_bits - remaining_bits;
94
0
    if (randdata[bit / 8] & (1 << (bit % 8))) {
95
0
      dns0x20name[i] = name[i] | 0x20;                          /* Set 0x20 */
96
0
    } else {
97
0
      dns0x20name[i] = (char)(((unsigned char)name[i]) & 0xDF); /* Unset 0x20 */
98
0
    }
99
0
    remaining_bits--;
100
0
  }
101
102
0
  status = ares_dns_record_query_set_name(dnsrec, 0, dns0x20name);
103
104
0
done:
105
0
  return status;
106
0
}
107
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
0
{
114
0
  ares_query_t            *query;
115
0
  ares_timeval_t           now;
116
0
  ares_status_t            status;
117
0
  unsigned short           id          = generate_unique_qid(channel);
118
0
  const ares_dns_record_t *dnsrec_resp = NULL;
119
120
0
  ares_tvnow(&now);
121
122
0
  if (ares_slist_len(channel->servers) == 0) {
123
0
    callback(arg, ARES_ENOSERVER, 0, NULL);
124
0
    return ARES_ENOSERVER;
125
0
  }
126
127
0
  if (!(flags & ARES_SEND_FLAG_NOCACHE)) {
128
    /* Check query cache */
129
0
    status = ares_qcache_fetch(channel, &now, dnsrec, &dnsrec_resp);
130
0
    if (status != ARES_ENOTFOUND) {
131
      /* ARES_SUCCESS means we retrieved the cache, anything else is a critical
132
       * failure, all result in termination */
133
0
      callback(arg, status, 0, dnsrec_resp);
134
0
      return status;
135
0
    }
136
0
  }
137
138
  /* Allocate space for query and allocated fields. */
139
0
  query = ares_malloc(sizeof(ares_query_t));
140
0
  if (!query) {
141
0
    callback(arg, ARES_ENOMEM, 0, NULL); /* LCOV_EXCL_LINE: OutOfMemory */
142
0
    return ARES_ENOMEM;                  /* LCOV_EXCL_LINE: OutOfMemory */
143
0
  }
144
0
  memset(query, 0, sizeof(*query));
145
146
0
  query->channel      = channel;
147
0
  query->qid          = id;
148
0
  query->timeout.sec  = 0;
149
0
  query->timeout.usec = 0;
150
0
  query->using_tcp =
151
0
    (channel->flags & ARES_FLAG_USEVC) ? ARES_TRUE : ARES_FALSE;
152
153
  /* Duplicate Query */
154
0
  status = ares_dns_record_duplicate_ex(&query->query, dnsrec);
155
0
  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
0
    if (status == ARES_EBADRESP) {
159
0
      status = ARES_EBADQUERY;
160
0
    }
161
0
    ares_free(query);
162
0
    callback(arg, status, 0, NULL);
163
0
    return status;
164
0
  }
165
166
0
  ares_dns_record_set_id(query->query, id);
167
168
0
  if (channel->flags & ARES_FLAG_DNS0x20 && !query->using_tcp) {
169
0
    status = ares_apply_dns0x20(channel, query->query);
170
0
    if (status != ARES_SUCCESS) {
171
      /* LCOV_EXCL_START: OutOfMemory */
172
0
      callback(arg, status, 0, NULL);
173
0
      ares_free_query(query);
174
0
      return status;
175
      /* LCOV_EXCL_STOP */
176
0
    }
177
0
  }
178
179
  /* Fill in query arguments. */
180
0
  query->callback = callback;
181
0
  query->arg      = arg;
182
183
  /* Initialize query status. */
184
0
  query->try_count = 0;
185
186
0
  if (flags & ARES_SEND_FLAG_NORETRY) {
187
0
    query->no_retries = ARES_TRUE;
188
0
  }
189
190
0
  query->error_status = ARES_SUCCESS;
191
0
  query->timeouts     = 0;
192
193
  /* Initialize our list nodes. */
194
0
  query->node_queries_by_timeout = NULL;
195
0
  query->node_queries_to_conn    = NULL;
196
197
  /* Chain the query into the list of all queries. */
198
0
  query->node_all_queries = ares_llist_insert_last(channel->all_queries, query);
199
0
  if (query->node_all_queries == NULL) {
200
    /* LCOV_EXCL_START: OutOfMemory */
201
0
    callback(arg, ARES_ENOMEM, 0, NULL);
202
0
    ares_free_query(query);
203
0
    return ARES_ENOMEM;
204
    /* LCOV_EXCL_STOP */
205
0
  }
206
207
  /* Keep track of queries bucketed by qid, so we can process DNS
208
   * responses quickly.
209
   */
210
0
  if (!ares_htable_szvp_insert(channel->queries_by_qid, query->qid, query)) {
211
    /* LCOV_EXCL_START: OutOfMemory */
212
0
    callback(arg, ARES_ENOMEM, 0, NULL);
213
0
    ares_free_query(query);
214
0
    return ARES_ENOMEM;
215
    /* LCOV_EXCL_STOP */
216
0
  }
217
218
  /* Perform the first query action. */
219
220
0
  status = ares_send_query(server, query, &now);
221
0
  if (status == ARES_SUCCESS && qid) {
222
0
    *qid = id;
223
0
  }
224
0
  return status;
225
0
}
226
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
0
{
232
0
  ares_status_t status;
233
234
0
  if (channel == NULL) {
235
0
    return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
236
0
  }
237
238
0
  ares_channel_lock(channel);
239
240
0
  status = ares_send_nolock(channel, NULL, 0, dnsrec, callback, arg, qid);
241
242
0
  ares_channel_unlock(channel);
243
244
0
  return status;
245
0
}
246
247
void ares_send(ares_channel_t *channel, const unsigned char *qbuf, int qlen,
248
               ares_callback callback, void *arg)
249
0
{
250
0
  ares_dns_record_t *dnsrec = NULL;
251
0
  ares_status_t      status;
252
0
  void              *carg = NULL;
253
254
0
  if (channel == NULL) {
255
0
    return;
256
0
  }
257
258
  /* Verify that the query is at least long enough to hold the header. */
259
0
  if (qlen < HFIXEDSZ || qlen >= (1 << 16)) {
260
0
    callback(arg, ARES_EBADQUERY, 0, NULL, 0);
261
0
    return;
262
0
  }
263
264
0
  status = ares_dns_parse(qbuf, (size_t)qlen, 0, &dnsrec);
265
0
  if (status != ARES_SUCCESS) {
266
0
    callback(arg, (int)status, 0, NULL, 0);
267
0
    return;
268
0
  }
269
270
0
  carg = ares_dnsrec_convert_arg(callback, arg);
271
0
  if (carg == NULL) {
272
    /* LCOV_EXCL_START: OutOfMemory */
273
0
    status = ARES_ENOMEM;
274
0
    ares_dns_record_destroy(dnsrec);
275
0
    callback(arg, (int)status, 0, NULL, 0);
276
0
    return;
277
    /* LCOV_EXCL_STOP */
278
0
  }
279
280
0
  ares_send_dnsrec(channel, dnsrec, ares_dnsrec_convert_cb, carg, NULL);
281
282
0
  ares_dns_record_destroy(dnsrec);
283
0
}
284
285
size_t ares_queue_active_queries(const ares_channel_t *channel)
286
0
{
287
0
  size_t len;
288
289
0
  if (channel == NULL) {
290
0
    return 0;
291
0
  }
292
293
0
  ares_channel_lock(channel);
294
295
0
  len = ares_llist_len(channel->all_queries);
296
297
0
  ares_channel_unlock(channel);
298
299
0
  return len;
300
0
}