11cb0ef41Sopenharmony_ci/* MIT License
21cb0ef41Sopenharmony_ci *
31cb0ef41Sopenharmony_ci * Copyright (c) 2023 Brad House
41cb0ef41Sopenharmony_ci *
51cb0ef41Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
61cb0ef41Sopenharmony_ci * of this software and associated documentation files (the "Software"), to deal
71cb0ef41Sopenharmony_ci * in the Software without restriction, including without limitation the rights
81cb0ef41Sopenharmony_ci * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
91cb0ef41Sopenharmony_ci * copies of the Software, and to permit persons to whom the Software is
101cb0ef41Sopenharmony_ci * furnished to do so, subject to the following conditions:
111cb0ef41Sopenharmony_ci *
121cb0ef41Sopenharmony_ci * The above copyright notice and this permission notice (including the next
131cb0ef41Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
141cb0ef41Sopenharmony_ci * Software.
151cb0ef41Sopenharmony_ci *
161cb0ef41Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
171cb0ef41Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
181cb0ef41Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
191cb0ef41Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
201cb0ef41Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
211cb0ef41Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
221cb0ef41Sopenharmony_ci * SOFTWARE.
231cb0ef41Sopenharmony_ci *
241cb0ef41Sopenharmony_ci * SPDX-License-Identifier: MIT
251cb0ef41Sopenharmony_ci */
261cb0ef41Sopenharmony_ci#include "ares_setup.h"
271cb0ef41Sopenharmony_ci#include "ares.h"
281cb0ef41Sopenharmony_ci#include "ares_private.h"
291cb0ef41Sopenharmony_ci
301cb0ef41Sopenharmony_cistruct ares__qcache {
311cb0ef41Sopenharmony_ci  ares__htable_strvp_t *cache;
321cb0ef41Sopenharmony_ci  ares__slist_t        *expire;
331cb0ef41Sopenharmony_ci  unsigned int          max_ttl;
341cb0ef41Sopenharmony_ci};
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_citypedef struct {
371cb0ef41Sopenharmony_ci  char              *key;
381cb0ef41Sopenharmony_ci  ares_dns_record_t *dnsrec;
391cb0ef41Sopenharmony_ci  time_t             expire_ts;
401cb0ef41Sopenharmony_ci  time_t             insert_ts;
411cb0ef41Sopenharmony_ci} ares__qcache_entry_t;
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_cistatic char *ares__qcache_calc_key(const ares_dns_record_t *dnsrec)
441cb0ef41Sopenharmony_ci{
451cb0ef41Sopenharmony_ci  ares__buf_t     *buf = ares__buf_create();
461cb0ef41Sopenharmony_ci  size_t           i;
471cb0ef41Sopenharmony_ci  ares_status_t    status;
481cb0ef41Sopenharmony_ci  ares_dns_flags_t flags;
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ci  if (dnsrec == NULL || buf == NULL) {
511cb0ef41Sopenharmony_ci    return NULL;
521cb0ef41Sopenharmony_ci  }
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_ci  /* Format is OPCODE|FLAGS[|QTYPE1|QCLASS1|QNAME1]... */
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_ci  status = ares__buf_append_str(
571cb0ef41Sopenharmony_ci    buf, ares_dns_opcode_tostr(ares_dns_record_get_opcode(dnsrec)));
581cb0ef41Sopenharmony_ci  if (status != ARES_SUCCESS) {
591cb0ef41Sopenharmony_ci    goto fail;
601cb0ef41Sopenharmony_ci  }
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci  status = ares__buf_append_byte(buf, '|');
631cb0ef41Sopenharmony_ci  if (status != ARES_SUCCESS) {
641cb0ef41Sopenharmony_ci    goto fail;
651cb0ef41Sopenharmony_ci  }
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_ci  flags = ares_dns_record_get_flags(dnsrec);
681cb0ef41Sopenharmony_ci  /* Only care about RD and CD */
691cb0ef41Sopenharmony_ci  if (flags & ARES_FLAG_RD) {
701cb0ef41Sopenharmony_ci    status = ares__buf_append_str(buf, "rd");
711cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
721cb0ef41Sopenharmony_ci      goto fail;
731cb0ef41Sopenharmony_ci    }
741cb0ef41Sopenharmony_ci  }
751cb0ef41Sopenharmony_ci  if (flags & ARES_FLAG_CD) {
761cb0ef41Sopenharmony_ci    status = ares__buf_append_str(buf, "cd");
771cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
781cb0ef41Sopenharmony_ci      goto fail;
791cb0ef41Sopenharmony_ci    }
801cb0ef41Sopenharmony_ci  }
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci  for (i = 0; i < ares_dns_record_query_cnt(dnsrec); i++) {
831cb0ef41Sopenharmony_ci    const char         *name;
841cb0ef41Sopenharmony_ci    ares_dns_rec_type_t qtype;
851cb0ef41Sopenharmony_ci    ares_dns_class_t    qclass;
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci    status = ares_dns_record_query_get(dnsrec, i, &name, &qtype, &qclass);
881cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
891cb0ef41Sopenharmony_ci      goto fail;
901cb0ef41Sopenharmony_ci    }
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci    status = ares__buf_append_byte(buf, '|');
931cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
941cb0ef41Sopenharmony_ci      goto fail;
951cb0ef41Sopenharmony_ci    }
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci    status = ares__buf_append_str(buf, ares_dns_rec_type_tostr(qtype));
981cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
991cb0ef41Sopenharmony_ci      goto fail;
1001cb0ef41Sopenharmony_ci    }
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci    status = ares__buf_append_byte(buf, '|');
1031cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
1041cb0ef41Sopenharmony_ci      goto fail;
1051cb0ef41Sopenharmony_ci    }
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci    status = ares__buf_append_str(buf, ares_dns_class_tostr(qclass));
1081cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
1091cb0ef41Sopenharmony_ci      goto fail;
1101cb0ef41Sopenharmony_ci    }
1111cb0ef41Sopenharmony_ci
1121cb0ef41Sopenharmony_ci    status = ares__buf_append_byte(buf, '|');
1131cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
1141cb0ef41Sopenharmony_ci      goto fail;
1151cb0ef41Sopenharmony_ci    }
1161cb0ef41Sopenharmony_ci
1171cb0ef41Sopenharmony_ci    status = ares__buf_append_str(buf, name);
1181cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
1191cb0ef41Sopenharmony_ci      goto fail;
1201cb0ef41Sopenharmony_ci    }
1211cb0ef41Sopenharmony_ci  }
1221cb0ef41Sopenharmony_ci
1231cb0ef41Sopenharmony_ci  return ares__buf_finish_str(buf, NULL);
1241cb0ef41Sopenharmony_ci
1251cb0ef41Sopenharmony_cifail:
1261cb0ef41Sopenharmony_ci  ares__buf_destroy(buf);
1271cb0ef41Sopenharmony_ci  return NULL;
1281cb0ef41Sopenharmony_ci}
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_cistatic void ares__qcache_expire(ares__qcache_t       *cache,
1311cb0ef41Sopenharmony_ci                                const struct timeval *now)
1321cb0ef41Sopenharmony_ci{
1331cb0ef41Sopenharmony_ci  ares__slist_node_t *node;
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ci  if (cache == NULL) {
1361cb0ef41Sopenharmony_ci    return;
1371cb0ef41Sopenharmony_ci  }
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci  while ((node = ares__slist_node_first(cache->expire)) != NULL) {
1401cb0ef41Sopenharmony_ci    const ares__qcache_entry_t *entry = ares__slist_node_val(node);
1411cb0ef41Sopenharmony_ci    if (entry->expire_ts > now->tv_sec) {
1421cb0ef41Sopenharmony_ci      break;
1431cb0ef41Sopenharmony_ci    }
1441cb0ef41Sopenharmony_ci
1451cb0ef41Sopenharmony_ci    ares__htable_strvp_remove(cache->cache, entry->key);
1461cb0ef41Sopenharmony_ci    ares__slist_node_destroy(node);
1471cb0ef41Sopenharmony_ci  }
1481cb0ef41Sopenharmony_ci}
1491cb0ef41Sopenharmony_ci
1501cb0ef41Sopenharmony_civoid ares__qcache_flush(ares__qcache_t *cache)
1511cb0ef41Sopenharmony_ci{
1521cb0ef41Sopenharmony_ci  struct timeval now;
1531cb0ef41Sopenharmony_ci  memset(&now, 0, sizeof(now));
1541cb0ef41Sopenharmony_ci  ares__qcache_expire(cache, &now);
1551cb0ef41Sopenharmony_ci}
1561cb0ef41Sopenharmony_ci
1571cb0ef41Sopenharmony_civoid ares__qcache_destroy(ares__qcache_t *cache)
1581cb0ef41Sopenharmony_ci{
1591cb0ef41Sopenharmony_ci  if (cache == NULL) {
1601cb0ef41Sopenharmony_ci    return;
1611cb0ef41Sopenharmony_ci  }
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_ci  ares__htable_strvp_destroy(cache->cache);
1641cb0ef41Sopenharmony_ci  ares__slist_destroy(cache->expire);
1651cb0ef41Sopenharmony_ci  ares_free(cache);
1661cb0ef41Sopenharmony_ci}
1671cb0ef41Sopenharmony_ci
1681cb0ef41Sopenharmony_cistatic int ares__qcache_entry_sort_cb(const void *arg1, const void *arg2)
1691cb0ef41Sopenharmony_ci{
1701cb0ef41Sopenharmony_ci  const ares__qcache_entry_t *entry1 = arg1;
1711cb0ef41Sopenharmony_ci  const ares__qcache_entry_t *entry2 = arg2;
1721cb0ef41Sopenharmony_ci
1731cb0ef41Sopenharmony_ci  if (entry1->expire_ts > entry2->expire_ts) {
1741cb0ef41Sopenharmony_ci    return 1;
1751cb0ef41Sopenharmony_ci  }
1761cb0ef41Sopenharmony_ci
1771cb0ef41Sopenharmony_ci  if (entry1->expire_ts < entry2->expire_ts) {
1781cb0ef41Sopenharmony_ci    return -1;
1791cb0ef41Sopenharmony_ci  }
1801cb0ef41Sopenharmony_ci
1811cb0ef41Sopenharmony_ci  return 0;
1821cb0ef41Sopenharmony_ci}
1831cb0ef41Sopenharmony_ci
1841cb0ef41Sopenharmony_cistatic void ares__qcache_entry_destroy_cb(void *arg)
1851cb0ef41Sopenharmony_ci{
1861cb0ef41Sopenharmony_ci  ares__qcache_entry_t *entry = arg;
1871cb0ef41Sopenharmony_ci  if (entry == NULL) {
1881cb0ef41Sopenharmony_ci    return;
1891cb0ef41Sopenharmony_ci  }
1901cb0ef41Sopenharmony_ci
1911cb0ef41Sopenharmony_ci  ares_free(entry->key);
1921cb0ef41Sopenharmony_ci  ares_dns_record_destroy(entry->dnsrec);
1931cb0ef41Sopenharmony_ci  ares_free(entry);
1941cb0ef41Sopenharmony_ci}
1951cb0ef41Sopenharmony_ci
1961cb0ef41Sopenharmony_ciares_status_t ares__qcache_create(ares_rand_state *rand_state,
1971cb0ef41Sopenharmony_ci                                  unsigned int     max_ttl,
1981cb0ef41Sopenharmony_ci                                  ares__qcache_t **cache_out)
1991cb0ef41Sopenharmony_ci{
2001cb0ef41Sopenharmony_ci  ares_status_t   status = ARES_SUCCESS;
2011cb0ef41Sopenharmony_ci  ares__qcache_t *cache;
2021cb0ef41Sopenharmony_ci
2031cb0ef41Sopenharmony_ci  cache = ares_malloc_zero(sizeof(*cache));
2041cb0ef41Sopenharmony_ci  if (cache == NULL) {
2051cb0ef41Sopenharmony_ci    status = ARES_ENOMEM;
2061cb0ef41Sopenharmony_ci    goto done;
2071cb0ef41Sopenharmony_ci  }
2081cb0ef41Sopenharmony_ci
2091cb0ef41Sopenharmony_ci  cache->cache = ares__htable_strvp_create(NULL);
2101cb0ef41Sopenharmony_ci  if (cache->cache == NULL) {
2111cb0ef41Sopenharmony_ci    status = ARES_ENOMEM;
2121cb0ef41Sopenharmony_ci    goto done;
2131cb0ef41Sopenharmony_ci  }
2141cb0ef41Sopenharmony_ci
2151cb0ef41Sopenharmony_ci  cache->expire = ares__slist_create(rand_state, ares__qcache_entry_sort_cb,
2161cb0ef41Sopenharmony_ci                                     ares__qcache_entry_destroy_cb);
2171cb0ef41Sopenharmony_ci  if (cache->expire == NULL) {
2181cb0ef41Sopenharmony_ci    status = ARES_ENOMEM;
2191cb0ef41Sopenharmony_ci    goto done;
2201cb0ef41Sopenharmony_ci  }
2211cb0ef41Sopenharmony_ci
2221cb0ef41Sopenharmony_ci  cache->max_ttl = max_ttl;
2231cb0ef41Sopenharmony_ci
2241cb0ef41Sopenharmony_cidone:
2251cb0ef41Sopenharmony_ci  if (status != ARES_SUCCESS) {
2261cb0ef41Sopenharmony_ci    *cache_out = NULL;
2271cb0ef41Sopenharmony_ci    ares__qcache_destroy(cache);
2281cb0ef41Sopenharmony_ci    return status;
2291cb0ef41Sopenharmony_ci  }
2301cb0ef41Sopenharmony_ci
2311cb0ef41Sopenharmony_ci  *cache_out = cache;
2321cb0ef41Sopenharmony_ci  return status;
2331cb0ef41Sopenharmony_ci}
2341cb0ef41Sopenharmony_ci
2351cb0ef41Sopenharmony_cistatic unsigned int ares__qcache_calc_minttl(ares_dns_record_t *dnsrec)
2361cb0ef41Sopenharmony_ci{
2371cb0ef41Sopenharmony_ci  unsigned int minttl = 0xFFFFFFFF;
2381cb0ef41Sopenharmony_ci  size_t       sect;
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_ci  for (sect = ARES_SECTION_ANSWER; sect <= ARES_SECTION_ADDITIONAL; sect++) {
2411cb0ef41Sopenharmony_ci    size_t i;
2421cb0ef41Sopenharmony_ci    for (i = 0; i < ares_dns_record_rr_cnt(dnsrec, (ares_dns_section_t)sect);
2431cb0ef41Sopenharmony_ci         i++) {
2441cb0ef41Sopenharmony_ci      const ares_dns_rr_t *rr =
2451cb0ef41Sopenharmony_ci        ares_dns_record_rr_get(dnsrec, (ares_dns_section_t)sect, i);
2461cb0ef41Sopenharmony_ci      ares_dns_rec_type_t type = ares_dns_rr_get_type(rr);
2471cb0ef41Sopenharmony_ci      unsigned int        ttl  = ares_dns_rr_get_ttl(rr);
2481cb0ef41Sopenharmony_ci      if (type == ARES_REC_TYPE_OPT || type == ARES_REC_TYPE_SOA) {
2491cb0ef41Sopenharmony_ci        continue;
2501cb0ef41Sopenharmony_ci      }
2511cb0ef41Sopenharmony_ci
2521cb0ef41Sopenharmony_ci      if (ttl < minttl) {
2531cb0ef41Sopenharmony_ci        minttl = ttl;
2541cb0ef41Sopenharmony_ci      }
2551cb0ef41Sopenharmony_ci    }
2561cb0ef41Sopenharmony_ci  }
2571cb0ef41Sopenharmony_ci
2581cb0ef41Sopenharmony_ci  return minttl;
2591cb0ef41Sopenharmony_ci}
2601cb0ef41Sopenharmony_ci
2611cb0ef41Sopenharmony_cistatic unsigned int ares__qcache_soa_minimum(ares_dns_record_t *dnsrec)
2621cb0ef41Sopenharmony_ci{
2631cb0ef41Sopenharmony_ci  size_t i;
2641cb0ef41Sopenharmony_ci
2651cb0ef41Sopenharmony_ci  /* RFC 2308 Section 5 says its the minimum of MINIMUM and the TTL of the
2661cb0ef41Sopenharmony_ci   * record. */
2671cb0ef41Sopenharmony_ci  for (i = 0; i < ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_AUTHORITY); i++) {
2681cb0ef41Sopenharmony_ci    const ares_dns_rr_t *rr =
2691cb0ef41Sopenharmony_ci      ares_dns_record_rr_get(dnsrec, ARES_SECTION_AUTHORITY, i);
2701cb0ef41Sopenharmony_ci    ares_dns_rec_type_t type = ares_dns_rr_get_type(rr);
2711cb0ef41Sopenharmony_ci    unsigned int        ttl;
2721cb0ef41Sopenharmony_ci    unsigned int        minimum;
2731cb0ef41Sopenharmony_ci
2741cb0ef41Sopenharmony_ci    if (type != ARES_REC_TYPE_SOA) {
2751cb0ef41Sopenharmony_ci      continue;
2761cb0ef41Sopenharmony_ci    }
2771cb0ef41Sopenharmony_ci
2781cb0ef41Sopenharmony_ci    minimum = ares_dns_rr_get_u32(rr, ARES_RR_SOA_MINIMUM);
2791cb0ef41Sopenharmony_ci    ttl     = ares_dns_rr_get_ttl(rr);
2801cb0ef41Sopenharmony_ci
2811cb0ef41Sopenharmony_ci    if (ttl > minimum) {
2821cb0ef41Sopenharmony_ci      return minimum;
2831cb0ef41Sopenharmony_ci    }
2841cb0ef41Sopenharmony_ci    return ttl;
2851cb0ef41Sopenharmony_ci  }
2861cb0ef41Sopenharmony_ci
2871cb0ef41Sopenharmony_ci  return 0;
2881cb0ef41Sopenharmony_ci}
2891cb0ef41Sopenharmony_ci
2901cb0ef41Sopenharmony_cistatic char *ares__qcache_calc_key_frombuf(const unsigned char *qbuf,
2911cb0ef41Sopenharmony_ci                                           size_t               qlen)
2921cb0ef41Sopenharmony_ci{
2931cb0ef41Sopenharmony_ci  ares_status_t      status;
2941cb0ef41Sopenharmony_ci  ares_dns_record_t *dnsrec = NULL;
2951cb0ef41Sopenharmony_ci  char              *key    = NULL;
2961cb0ef41Sopenharmony_ci
2971cb0ef41Sopenharmony_ci  status = ares_dns_parse(qbuf, qlen, 0, &dnsrec);
2981cb0ef41Sopenharmony_ci  if (status != ARES_SUCCESS) {
2991cb0ef41Sopenharmony_ci    goto done;
3001cb0ef41Sopenharmony_ci  }
3011cb0ef41Sopenharmony_ci
3021cb0ef41Sopenharmony_ci  key = ares__qcache_calc_key(dnsrec);
3031cb0ef41Sopenharmony_ci
3041cb0ef41Sopenharmony_cidone:
3051cb0ef41Sopenharmony_ci  ares_dns_record_destroy(dnsrec);
3061cb0ef41Sopenharmony_ci  return key;
3071cb0ef41Sopenharmony_ci}
3081cb0ef41Sopenharmony_ci
3091cb0ef41Sopenharmony_ci/* On success, takes ownership of dnsrec */
3101cb0ef41Sopenharmony_cistatic ares_status_t ares__qcache_insert(ares__qcache_t      *qcache,
3111cb0ef41Sopenharmony_ci                                         ares_dns_record_t   *dnsrec,
3121cb0ef41Sopenharmony_ci                                         const unsigned char *qbuf, size_t qlen,
3131cb0ef41Sopenharmony_ci                                         const struct timeval *now)
3141cb0ef41Sopenharmony_ci{
3151cb0ef41Sopenharmony_ci  ares__qcache_entry_t *entry;
3161cb0ef41Sopenharmony_ci  unsigned int          ttl;
3171cb0ef41Sopenharmony_ci  ares_dns_rcode_t      rcode = ares_dns_record_get_rcode(dnsrec);
3181cb0ef41Sopenharmony_ci  ares_dns_flags_t      flags = ares_dns_record_get_flags(dnsrec);
3191cb0ef41Sopenharmony_ci
3201cb0ef41Sopenharmony_ci  if (qcache == NULL || dnsrec == NULL) {
3211cb0ef41Sopenharmony_ci    return ARES_EFORMERR;
3221cb0ef41Sopenharmony_ci  }
3231cb0ef41Sopenharmony_ci
3241cb0ef41Sopenharmony_ci  /* Only save NOERROR or NXDOMAIN */
3251cb0ef41Sopenharmony_ci  if (rcode != ARES_RCODE_NOERROR && rcode != ARES_RCODE_NXDOMAIN) {
3261cb0ef41Sopenharmony_ci    return ARES_ENOTIMP;
3271cb0ef41Sopenharmony_ci  }
3281cb0ef41Sopenharmony_ci
3291cb0ef41Sopenharmony_ci  /* Don't save truncated queries */
3301cb0ef41Sopenharmony_ci  if (flags & ARES_FLAG_TC) {
3311cb0ef41Sopenharmony_ci    return ARES_ENOTIMP;
3321cb0ef41Sopenharmony_ci  }
3331cb0ef41Sopenharmony_ci
3341cb0ef41Sopenharmony_ci  /* Look at SOA for NXDOMAIN for minimum */
3351cb0ef41Sopenharmony_ci  if (rcode == ARES_RCODE_NXDOMAIN) {
3361cb0ef41Sopenharmony_ci    ttl = ares__qcache_soa_minimum(dnsrec);
3371cb0ef41Sopenharmony_ci  } else {
3381cb0ef41Sopenharmony_ci    ttl = ares__qcache_calc_minttl(dnsrec);
3391cb0ef41Sopenharmony_ci  }
3401cb0ef41Sopenharmony_ci
3411cb0ef41Sopenharmony_ci  /* Don't cache something that is already expired */
3421cb0ef41Sopenharmony_ci  if (ttl == 0) {
3431cb0ef41Sopenharmony_ci    return ARES_EREFUSED;
3441cb0ef41Sopenharmony_ci  }
3451cb0ef41Sopenharmony_ci
3461cb0ef41Sopenharmony_ci  if (ttl > qcache->max_ttl) {
3471cb0ef41Sopenharmony_ci    ttl = qcache->max_ttl;
3481cb0ef41Sopenharmony_ci  }
3491cb0ef41Sopenharmony_ci
3501cb0ef41Sopenharmony_ci  entry = ares_malloc_zero(sizeof(*entry));
3511cb0ef41Sopenharmony_ci  if (entry == NULL) {
3521cb0ef41Sopenharmony_ci    goto fail;
3531cb0ef41Sopenharmony_ci  }
3541cb0ef41Sopenharmony_ci
3551cb0ef41Sopenharmony_ci  entry->dnsrec    = dnsrec;
3561cb0ef41Sopenharmony_ci  entry->expire_ts = now->tv_sec + (time_t)ttl;
3571cb0ef41Sopenharmony_ci  entry->insert_ts = now->tv_sec;
3581cb0ef41Sopenharmony_ci
3591cb0ef41Sopenharmony_ci  /* We can't guarantee the server responded with the same flags as the
3601cb0ef41Sopenharmony_ci   * request had, so we have to re-parse the request in order to generate the
3611cb0ef41Sopenharmony_ci   * key for caching, but we'll only do this once we know for sure we really
3621cb0ef41Sopenharmony_ci   * want to cache it */
3631cb0ef41Sopenharmony_ci  entry->key = ares__qcache_calc_key_frombuf(qbuf, qlen);
3641cb0ef41Sopenharmony_ci  if (entry->key == NULL) {
3651cb0ef41Sopenharmony_ci    goto fail;
3661cb0ef41Sopenharmony_ci  }
3671cb0ef41Sopenharmony_ci
3681cb0ef41Sopenharmony_ci  if (!ares__htable_strvp_insert(qcache->cache, entry->key, entry)) {
3691cb0ef41Sopenharmony_ci    goto fail;
3701cb0ef41Sopenharmony_ci  }
3711cb0ef41Sopenharmony_ci
3721cb0ef41Sopenharmony_ci  if (ares__slist_insert(qcache->expire, entry) == NULL) {
3731cb0ef41Sopenharmony_ci    goto fail;
3741cb0ef41Sopenharmony_ci  }
3751cb0ef41Sopenharmony_ci
3761cb0ef41Sopenharmony_ci  return ARES_SUCCESS;
3771cb0ef41Sopenharmony_ci
3781cb0ef41Sopenharmony_cifail:
3791cb0ef41Sopenharmony_ci  if (entry != NULL && entry->key != NULL) {
3801cb0ef41Sopenharmony_ci    ares__htable_strvp_remove(qcache->cache, entry->key);
3811cb0ef41Sopenharmony_ci    ares_free(entry->key);
3821cb0ef41Sopenharmony_ci    ares_free(entry);
3831cb0ef41Sopenharmony_ci  }
3841cb0ef41Sopenharmony_ci  return ARES_ENOMEM;
3851cb0ef41Sopenharmony_ci}
3861cb0ef41Sopenharmony_ci
3871cb0ef41Sopenharmony_cistatic ares_status_t ares__qcache_fetch(ares__qcache_t          *qcache,
3881cb0ef41Sopenharmony_ci                                        const ares_dns_record_t *dnsrec,
3891cb0ef41Sopenharmony_ci                                        const struct timeval    *now,
3901cb0ef41Sopenharmony_ci                                        unsigned char **buf, size_t *buf_len)
3911cb0ef41Sopenharmony_ci{
3921cb0ef41Sopenharmony_ci  char                 *key = NULL;
3931cb0ef41Sopenharmony_ci  ares__qcache_entry_t *entry;
3941cb0ef41Sopenharmony_ci  ares_status_t         status;
3951cb0ef41Sopenharmony_ci
3961cb0ef41Sopenharmony_ci  if (qcache == NULL || dnsrec == NULL) {
3971cb0ef41Sopenharmony_ci    return ARES_EFORMERR;
3981cb0ef41Sopenharmony_ci  }
3991cb0ef41Sopenharmony_ci
4001cb0ef41Sopenharmony_ci  ares__qcache_expire(qcache, now);
4011cb0ef41Sopenharmony_ci
4021cb0ef41Sopenharmony_ci  key = ares__qcache_calc_key(dnsrec);
4031cb0ef41Sopenharmony_ci  if (key == NULL) {
4041cb0ef41Sopenharmony_ci    status = ARES_ENOMEM;
4051cb0ef41Sopenharmony_ci    goto done;
4061cb0ef41Sopenharmony_ci  }
4071cb0ef41Sopenharmony_ci
4081cb0ef41Sopenharmony_ci  entry = ares__htable_strvp_get_direct(qcache->cache, key);
4091cb0ef41Sopenharmony_ci  if (entry == NULL) {
4101cb0ef41Sopenharmony_ci    status = ARES_ENOTFOUND;
4111cb0ef41Sopenharmony_ci    goto done;
4121cb0ef41Sopenharmony_ci  }
4131cb0ef41Sopenharmony_ci
4141cb0ef41Sopenharmony_ci  ares_dns_record_write_ttl_decrement(
4151cb0ef41Sopenharmony_ci    entry->dnsrec, (unsigned int)(now->tv_sec - entry->insert_ts));
4161cb0ef41Sopenharmony_ci
4171cb0ef41Sopenharmony_ci  status = ares_dns_write(entry->dnsrec, buf, buf_len);
4181cb0ef41Sopenharmony_ci
4191cb0ef41Sopenharmony_cidone:
4201cb0ef41Sopenharmony_ci  ares_free(key);
4211cb0ef41Sopenharmony_ci  return status;
4221cb0ef41Sopenharmony_ci}
4231cb0ef41Sopenharmony_ci
4241cb0ef41Sopenharmony_ciares_status_t ares_qcache_insert(ares_channel_t       *channel,
4251cb0ef41Sopenharmony_ci                                 const struct timeval *now,
4261cb0ef41Sopenharmony_ci                                 const struct query   *query,
4271cb0ef41Sopenharmony_ci                                 ares_dns_record_t    *dnsrec)
4281cb0ef41Sopenharmony_ci{
4291cb0ef41Sopenharmony_ci  return ares__qcache_insert(channel->qcache, dnsrec, query->qbuf, query->qlen,
4301cb0ef41Sopenharmony_ci                             now);
4311cb0ef41Sopenharmony_ci}
4321cb0ef41Sopenharmony_ci
4331cb0ef41Sopenharmony_ciares_status_t ares_qcache_fetch(ares_channel_t       *channel,
4341cb0ef41Sopenharmony_ci                                const struct timeval *now,
4351cb0ef41Sopenharmony_ci                                const unsigned char *qbuf, size_t qlen,
4361cb0ef41Sopenharmony_ci                                unsigned char **abuf, size_t *alen)
4371cb0ef41Sopenharmony_ci{
4381cb0ef41Sopenharmony_ci  ares_status_t      status;
4391cb0ef41Sopenharmony_ci  ares_dns_record_t *dnsrec = NULL;
4401cb0ef41Sopenharmony_ci
4411cb0ef41Sopenharmony_ci  if (channel->qcache == NULL) {
4421cb0ef41Sopenharmony_ci    return ARES_ENOTFOUND;
4431cb0ef41Sopenharmony_ci  }
4441cb0ef41Sopenharmony_ci
4451cb0ef41Sopenharmony_ci  status = ares_dns_parse(qbuf, qlen, 0, &dnsrec);
4461cb0ef41Sopenharmony_ci  if (status != ARES_SUCCESS) {
4471cb0ef41Sopenharmony_ci    goto done;
4481cb0ef41Sopenharmony_ci  }
4491cb0ef41Sopenharmony_ci
4501cb0ef41Sopenharmony_ci  status = ares__qcache_fetch(channel->qcache, dnsrec, now, abuf, alen);
4511cb0ef41Sopenharmony_ci
4521cb0ef41Sopenharmony_cidone:
4531cb0ef41Sopenharmony_ci  ares_dns_record_destroy(dnsrec);
4541cb0ef41Sopenharmony_ci  return status;
4551cb0ef41Sopenharmony_ci}
456