ares_send.c (8665B)
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 { 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 */ 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 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 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 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 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 }