quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

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 }