quickjs-tart

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

ares_dns_multistring.c (7825B)


      1 /* MIT License
      2  *
      3  * Copyright (c) 2024 Brad House
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining a copy
      6  * of this software and associated documentation files (the "Software"), to deal
      7  * in the Software without restriction, including without limitation the rights
      8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      9  * copies of the Software, and to permit persons to whom the Software is
     10  * furnished to do so, subject to the following conditions:
     11  *
     12  * The above copyright notice and this permission notice (including the next
     13  * paragraph) shall be included in all copies or substantial portions of the
     14  * Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     22  * SOFTWARE.
     23  *
     24  * SPDX-License-Identifier: MIT
     25  */
     26 #include "ares_private.h"
     27 #include "ares_dns_private.h"
     28 
     29 typedef struct {
     30   unsigned char *data;
     31   size_t         len;
     32 } multistring_data_t;
     33 
     34 struct ares_dns_multistring {
     35   /*! whether or not cached concatenated string is valid */
     36   ares_bool_t    cache_invalidated;
     37   /*! combined/concatenated string cache */
     38   unsigned char *cache_str;
     39   /*! length of combined/concatenated string */
     40   size_t         cache_str_len;
     41   /*! Data making up strings */
     42   ares_array_t  *strs; /*!< multistring_data_t type */
     43 };
     44 
     45 static void ares_dns_multistring_free_cb(void *arg)
     46 {
     47   multistring_data_t *data = arg;
     48   if (data == NULL) {
     49     return;
     50   }
     51   ares_free(data->data);
     52 }
     53 
     54 ares_dns_multistring_t *ares_dns_multistring_create(void)
     55 {
     56   ares_dns_multistring_t *strs = ares_malloc_zero(sizeof(*strs));
     57   if (strs == NULL) {
     58     return NULL;
     59   }
     60 
     61   strs->strs =
     62     ares_array_create(sizeof(multistring_data_t), ares_dns_multistring_free_cb);
     63   if (strs->strs == NULL) {
     64     ares_free(strs);
     65     return NULL;
     66   }
     67 
     68   return strs;
     69 }
     70 
     71 void ares_dns_multistring_clear(ares_dns_multistring_t *strs)
     72 {
     73   if (strs == NULL) {
     74     return;
     75   }
     76 
     77   while (ares_array_len(strs->strs)) {
     78     ares_array_remove_last(strs->strs);
     79   }
     80 }
     81 
     82 void ares_dns_multistring_destroy(ares_dns_multistring_t *strs)
     83 {
     84   if (strs == NULL) {
     85     return;
     86   }
     87   ares_dns_multistring_clear(strs);
     88   ares_array_destroy(strs->strs);
     89   ares_free(strs->cache_str);
     90   ares_free(strs);
     91 }
     92 
     93 ares_status_t ares_dns_multistring_swap_own(ares_dns_multistring_t *strs,
     94                                             size_t idx, unsigned char *str,
     95                                             size_t len)
     96 {
     97   multistring_data_t *data;
     98 
     99   if (strs == NULL || str == NULL || len == 0) {
    100     return ARES_EFORMERR;
    101   }
    102 
    103   strs->cache_invalidated = ARES_TRUE;
    104 
    105   data = ares_array_at(strs->strs, idx);
    106   if (data == NULL) {
    107     return ARES_EFORMERR;
    108   }
    109 
    110   ares_free(data->data);
    111   data->data = str;
    112   data->len  = len;
    113   return ARES_SUCCESS;
    114 }
    115 
    116 ares_status_t ares_dns_multistring_del(ares_dns_multistring_t *strs, size_t idx)
    117 {
    118   if (strs == NULL) {
    119     return ARES_EFORMERR;
    120   }
    121 
    122   strs->cache_invalidated = ARES_TRUE;
    123 
    124   return ares_array_remove_at(strs->strs, idx);
    125 }
    126 
    127 ares_status_t ares_dns_multistring_add_own(ares_dns_multistring_t *strs,
    128                                            unsigned char *str, size_t len)
    129 {
    130   multistring_data_t *data;
    131   ares_status_t       status;
    132 
    133   if (strs == NULL) {
    134     return ARES_EFORMERR;
    135   }
    136 
    137   strs->cache_invalidated = ARES_TRUE;
    138 
    139   /* NOTE: its ok to have an empty string added */
    140   if (str == NULL && len != 0) {
    141     return ARES_EFORMERR;
    142   }
    143 
    144   status = ares_array_insert_last((void **)&data, strs->strs);
    145   if (status != ARES_SUCCESS) {
    146     return status;
    147   }
    148 
    149   /* Issue #921, ares_dns_multistring_get() doesn't have a way to indicate
    150    * success or fail on a zero-length string which is actually valid.  So we
    151    * are going to allocate a 1-byte buffer to use as a placeholder in this
    152    * case */
    153   if (str == NULL) {
    154     str = ares_malloc_zero(1);
    155     if (str == NULL) {
    156       ares_array_remove_last(strs->strs);
    157       return ARES_ENOMEM;
    158     }
    159   }
    160 
    161   data->data = str;
    162   data->len  = len;
    163 
    164   return ARES_SUCCESS;
    165 }
    166 
    167 size_t ares_dns_multistring_cnt(const ares_dns_multistring_t *strs)
    168 {
    169   if (strs == NULL) {
    170     return 0;
    171   }
    172   return ares_array_len(strs->strs);
    173 }
    174 
    175 const unsigned char *
    176   ares_dns_multistring_get(const ares_dns_multistring_t *strs, size_t idx,
    177                            size_t *len)
    178 {
    179   const multistring_data_t *data;
    180 
    181   if (strs == NULL || len == NULL) {
    182     return NULL;
    183   }
    184 
    185   data = ares_array_at_const(strs->strs, idx);
    186   if (data == NULL) {
    187     return NULL;
    188   }
    189 
    190   *len = data->len;
    191   return data->data;
    192 }
    193 
    194 const unsigned char *ares_dns_multistring_combined(ares_dns_multistring_t *strs,
    195                                                    size_t                 *len)
    196 {
    197   ares_buf_t *buf = NULL;
    198   size_t      i;
    199 
    200   if (strs == NULL || len == NULL) {
    201     return NULL;
    202   }
    203 
    204   *len = 0;
    205 
    206   /* Return cache if possible */
    207   if (!strs->cache_invalidated) {
    208     *len = strs->cache_str_len;
    209     return strs->cache_str;
    210   }
    211 
    212   /* Clear cache */
    213   ares_free(strs->cache_str);
    214   strs->cache_str     = NULL;
    215   strs->cache_str_len = 0;
    216 
    217   buf = ares_buf_create();
    218 
    219   for (i = 0; i < ares_array_len(strs->strs); i++) {
    220     const multistring_data_t *data = ares_array_at_const(strs->strs, i);
    221     if (data == NULL ||
    222         ares_buf_append(buf, data->data, data->len) != ARES_SUCCESS) {
    223       ares_buf_destroy(buf);
    224       return NULL;
    225     }
    226   }
    227 
    228   strs->cache_str =
    229     (unsigned char *)ares_buf_finish_str(buf, &strs->cache_str_len);
    230   if (strs->cache_str != NULL) {
    231     strs->cache_invalidated = ARES_FALSE;
    232   }
    233   *len = strs->cache_str_len;
    234   return strs->cache_str;
    235 }
    236 
    237 ares_status_t ares_dns_multistring_parse_buf(ares_buf_t *buf,
    238                                              size_t      remaining_len,
    239                                              ares_dns_multistring_t **strs,
    240                                              ares_bool_t validate_printable)
    241 {
    242   unsigned char len;
    243   ares_status_t status   = ARES_EBADRESP;
    244   size_t        orig_len = ares_buf_len(buf);
    245 
    246   if (buf == NULL) {
    247     return ARES_EFORMERR;
    248   }
    249 
    250   if (remaining_len == 0) {
    251     return ARES_EBADRESP;
    252   }
    253 
    254   if (strs != NULL) {
    255     *strs = ares_dns_multistring_create();
    256     if (*strs == NULL) {
    257       return ARES_ENOMEM;
    258     }
    259   }
    260 
    261   while (orig_len - ares_buf_len(buf) < remaining_len) {
    262     status = ares_buf_fetch_bytes(buf, &len, 1);
    263     if (status != ARES_SUCCESS) {
    264       break; /* LCOV_EXCL_LINE: DefensiveCoding */
    265     }
    266 
    267 
    268     /* When used by the _str() parser, it really needs to be validated to
    269      * be a valid printable ascii string.  Do that here */
    270     if (len && validate_printable && ares_buf_len(buf) >= len) {
    271       size_t      mylen;
    272       const char *data = (const char *)ares_buf_peek(buf, &mylen);
    273       if (!ares_str_isprint(data, len)) {
    274         status = ARES_EBADSTR;
    275         break;
    276       }
    277     }
    278 
    279     if (strs != NULL) {
    280       unsigned char *data = NULL;
    281       if (len) {
    282         status = ares_buf_fetch_bytes_dup(buf, len, ARES_TRUE, &data);
    283         if (status != ARES_SUCCESS) {
    284           break;
    285         }
    286       }
    287       status = ares_dns_multistring_add_own(*strs, data, len);
    288       if (status != ARES_SUCCESS) {
    289         ares_free(data);
    290         break;
    291       }
    292     } else {
    293       status = ares_buf_consume(buf, len);
    294       if (status != ARES_SUCCESS) {
    295         break;
    296       }
    297     }
    298 
    299   }
    300 
    301   if (status != ARES_SUCCESS && strs != NULL) {
    302     ares_dns_multistring_destroy(*strs);
    303     *strs = NULL;
    304   }
    305 
    306   return status;
    307 }