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_citypedef struct {
311cb0ef41Sopenharmony_ci  char  *name;
321cb0ef41Sopenharmony_ci  size_t name_len;
331cb0ef41Sopenharmony_ci  size_t idx;
341cb0ef41Sopenharmony_ci} ares_nameoffset_t;
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_cistatic void ares__nameoffset_free(void *arg)
371cb0ef41Sopenharmony_ci{
381cb0ef41Sopenharmony_ci  ares_nameoffset_t *off = arg;
391cb0ef41Sopenharmony_ci  if (off == NULL) {
401cb0ef41Sopenharmony_ci    return;
411cb0ef41Sopenharmony_ci  }
421cb0ef41Sopenharmony_ci  ares_free(off->name);
431cb0ef41Sopenharmony_ci  ares_free(off);
441cb0ef41Sopenharmony_ci}
451cb0ef41Sopenharmony_ci
461cb0ef41Sopenharmony_cistatic ares_status_t ares__nameoffset_create(ares__llist_t **list,
471cb0ef41Sopenharmony_ci                                             const char *name, size_t idx)
481cb0ef41Sopenharmony_ci{
491cb0ef41Sopenharmony_ci  ares_status_t      status;
501cb0ef41Sopenharmony_ci  ares_nameoffset_t *off = NULL;
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ci  if (list == NULL || name == NULL || ares_strlen(name) == 0 ||
531cb0ef41Sopenharmony_ci      ares_strlen(name) > 255) {
541cb0ef41Sopenharmony_ci    return ARES_EFORMERR;
551cb0ef41Sopenharmony_ci  }
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci  if (*list == NULL) {
581cb0ef41Sopenharmony_ci    *list = ares__llist_create(ares__nameoffset_free);
591cb0ef41Sopenharmony_ci  }
601cb0ef41Sopenharmony_ci  if (*list == NULL) {
611cb0ef41Sopenharmony_ci    status = ARES_ENOMEM;
621cb0ef41Sopenharmony_ci    goto fail;
631cb0ef41Sopenharmony_ci  }
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_ci  off = ares_malloc_zero(sizeof(*off));
661cb0ef41Sopenharmony_ci  if (off == NULL) {
671cb0ef41Sopenharmony_ci    return ARES_ENOMEM;
681cb0ef41Sopenharmony_ci  }
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci  off->name     = ares_strdup(name);
711cb0ef41Sopenharmony_ci  off->name_len = ares_strlen(off->name);
721cb0ef41Sopenharmony_ci  off->idx      = idx;
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_ci  if (ares__llist_insert_last(*list, off) == NULL) {
751cb0ef41Sopenharmony_ci    status = ARES_ENOMEM;
761cb0ef41Sopenharmony_ci    goto fail;
771cb0ef41Sopenharmony_ci  }
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ci  return ARES_SUCCESS;
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_cifail:
821cb0ef41Sopenharmony_ci  ares__nameoffset_free(off);
831cb0ef41Sopenharmony_ci  return status;
841cb0ef41Sopenharmony_ci}
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_cistatic const ares_nameoffset_t *ares__nameoffset_find(ares__llist_t *list,
871cb0ef41Sopenharmony_ci                                                      const char    *name)
881cb0ef41Sopenharmony_ci{
891cb0ef41Sopenharmony_ci  size_t                   name_len = ares_strlen(name);
901cb0ef41Sopenharmony_ci  ares__llist_node_t      *node;
911cb0ef41Sopenharmony_ci  const ares_nameoffset_t *longest_match = NULL;
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_ci  if (list == NULL || name == NULL || name_len == 0) {
941cb0ef41Sopenharmony_ci    return NULL;
951cb0ef41Sopenharmony_ci  }
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci  for (node = ares__llist_node_first(list); node != NULL;
981cb0ef41Sopenharmony_ci       node = ares__llist_node_next(node)) {
991cb0ef41Sopenharmony_ci    const ares_nameoffset_t *val = ares__llist_node_val(node);
1001cb0ef41Sopenharmony_ci    size_t                   prefix_len;
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci    /* Can't be a match if the stored name is longer */
1031cb0ef41Sopenharmony_ci    if (val->name_len > name_len) {
1041cb0ef41Sopenharmony_ci      continue;
1051cb0ef41Sopenharmony_ci    }
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci    /* Can't be the longest match if our existing longest match is longer */
1081cb0ef41Sopenharmony_ci    if (longest_match != NULL && longest_match->name_len > val->name_len) {
1091cb0ef41Sopenharmony_ci      continue;
1101cb0ef41Sopenharmony_ci    }
1111cb0ef41Sopenharmony_ci
1121cb0ef41Sopenharmony_ci    prefix_len = name_len - val->name_len;
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci    if (strcasecmp(val->name, name + prefix_len) != 0) {
1151cb0ef41Sopenharmony_ci      continue;
1161cb0ef41Sopenharmony_ci    }
1171cb0ef41Sopenharmony_ci
1181cb0ef41Sopenharmony_ci    /* We need to make sure if `val->name` is "example.com" that name is
1191cb0ef41Sopenharmony_ci     * is separated by a label, e.g. "myexample.com" is not ok, however
1201cb0ef41Sopenharmony_ci     * "my.example.com" is, so we look for the preceding "." */
1211cb0ef41Sopenharmony_ci    if (prefix_len != 0 && name[prefix_len - 1] != '.') {
1221cb0ef41Sopenharmony_ci      continue;
1231cb0ef41Sopenharmony_ci    }
1241cb0ef41Sopenharmony_ci
1251cb0ef41Sopenharmony_ci    longest_match = val;
1261cb0ef41Sopenharmony_ci  }
1271cb0ef41Sopenharmony_ci
1281cb0ef41Sopenharmony_ci  return longest_match;
1291cb0ef41Sopenharmony_ci}
1301cb0ef41Sopenharmony_ci
1311cb0ef41Sopenharmony_citypedef struct {
1321cb0ef41Sopenharmony_ci  ares__buf_t **label;
1331cb0ef41Sopenharmony_ci  size_t        num;
1341cb0ef41Sopenharmony_ci} ares_dns_labels_t;
1351cb0ef41Sopenharmony_ci
1361cb0ef41Sopenharmony_cistatic void ares_dns_labels_free(ares_dns_labels_t *labels)
1371cb0ef41Sopenharmony_ci{
1381cb0ef41Sopenharmony_ci  size_t i;
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci  if (labels == NULL) {
1411cb0ef41Sopenharmony_ci    return;
1421cb0ef41Sopenharmony_ci  }
1431cb0ef41Sopenharmony_ci
1441cb0ef41Sopenharmony_ci  for (i = 0; i < labels->num; i++) {
1451cb0ef41Sopenharmony_ci    ares__buf_destroy(labels->label[i]);
1461cb0ef41Sopenharmony_ci    labels->label[i] = NULL;
1471cb0ef41Sopenharmony_ci  }
1481cb0ef41Sopenharmony_ci  ares_free(labels->label);
1491cb0ef41Sopenharmony_ci  labels->label = NULL;
1501cb0ef41Sopenharmony_ci  labels->num   = 0;
1511cb0ef41Sopenharmony_ci}
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_cistatic ares__buf_t *ares_dns_labels_add(ares_dns_labels_t *labels)
1541cb0ef41Sopenharmony_ci{
1551cb0ef41Sopenharmony_ci  void *temp;
1561cb0ef41Sopenharmony_ci
1571cb0ef41Sopenharmony_ci  if (labels == NULL) {
1581cb0ef41Sopenharmony_ci    return NULL;
1591cb0ef41Sopenharmony_ci  }
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ci  temp = ares_realloc_zero(labels->label, sizeof(*labels->label) * labels->num,
1621cb0ef41Sopenharmony_ci                           sizeof(*labels->label) * (labels->num + 1));
1631cb0ef41Sopenharmony_ci  if (temp == NULL) {
1641cb0ef41Sopenharmony_ci    return NULL;
1651cb0ef41Sopenharmony_ci  }
1661cb0ef41Sopenharmony_ci
1671cb0ef41Sopenharmony_ci  labels->label = temp;
1681cb0ef41Sopenharmony_ci
1691cb0ef41Sopenharmony_ci  labels->label[labels->num] = ares__buf_create();
1701cb0ef41Sopenharmony_ci  if (labels->label[labels->num] == NULL) {
1711cb0ef41Sopenharmony_ci    return NULL;
1721cb0ef41Sopenharmony_ci  }
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci  labels->num++;
1751cb0ef41Sopenharmony_ci  return labels->label[labels->num - 1];
1761cb0ef41Sopenharmony_ci}
1771cb0ef41Sopenharmony_ci
1781cb0ef41Sopenharmony_cistatic const ares__buf_t *
1791cb0ef41Sopenharmony_ci  ares_dns_labels_get_last(const ares_dns_labels_t *labels)
1801cb0ef41Sopenharmony_ci{
1811cb0ef41Sopenharmony_ci  if (labels == NULL || labels->num == 0) {
1821cb0ef41Sopenharmony_ci    return NULL;
1831cb0ef41Sopenharmony_ci  }
1841cb0ef41Sopenharmony_ci
1851cb0ef41Sopenharmony_ci  return labels->label[labels->num - 1];
1861cb0ef41Sopenharmony_ci}
1871cb0ef41Sopenharmony_ci
1881cb0ef41Sopenharmony_cistatic void ares_dns_name_labels_del_last(ares_dns_labels_t *labels)
1891cb0ef41Sopenharmony_ci{
1901cb0ef41Sopenharmony_ci  if (labels == NULL || labels->num == 0) {
1911cb0ef41Sopenharmony_ci    return;
1921cb0ef41Sopenharmony_ci  }
1931cb0ef41Sopenharmony_ci
1941cb0ef41Sopenharmony_ci  ares__buf_destroy(labels->label[labels->num - 1]);
1951cb0ef41Sopenharmony_ci  labels->label[labels->num - 1] = NULL;
1961cb0ef41Sopenharmony_ci  labels->num--;
1971cb0ef41Sopenharmony_ci}
1981cb0ef41Sopenharmony_ci
1991cb0ef41Sopenharmony_cistatic ares_status_t ares_parse_dns_name_escape(ares__buf_t *namebuf,
2001cb0ef41Sopenharmony_ci                                                ares__buf_t *label,
2011cb0ef41Sopenharmony_ci                                                ares_bool_t  validate_hostname)
2021cb0ef41Sopenharmony_ci{
2031cb0ef41Sopenharmony_ci  ares_status_t status;
2041cb0ef41Sopenharmony_ci  unsigned char c;
2051cb0ef41Sopenharmony_ci
2061cb0ef41Sopenharmony_ci  status = ares__buf_fetch_bytes(namebuf, &c, 1);
2071cb0ef41Sopenharmony_ci  if (status != ARES_SUCCESS) {
2081cb0ef41Sopenharmony_ci    return ARES_EBADNAME;
2091cb0ef41Sopenharmony_ci  }
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci  /* If next character is a digit, read 2 more digits */
2121cb0ef41Sopenharmony_ci  if (isdigit(c)) {
2131cb0ef41Sopenharmony_ci    size_t       i;
2141cb0ef41Sopenharmony_ci    unsigned int val = 0;
2151cb0ef41Sopenharmony_ci
2161cb0ef41Sopenharmony_ci    val = c - '0';
2171cb0ef41Sopenharmony_ci
2181cb0ef41Sopenharmony_ci    for (i = 0; i < 2; i++) {
2191cb0ef41Sopenharmony_ci      status = ares__buf_fetch_bytes(namebuf, &c, 1);
2201cb0ef41Sopenharmony_ci      if (status != ARES_SUCCESS) {
2211cb0ef41Sopenharmony_ci        return ARES_EBADNAME;
2221cb0ef41Sopenharmony_ci      }
2231cb0ef41Sopenharmony_ci
2241cb0ef41Sopenharmony_ci      if (!isdigit(c)) {
2251cb0ef41Sopenharmony_ci        return ARES_EBADNAME;
2261cb0ef41Sopenharmony_ci      }
2271cb0ef41Sopenharmony_ci      val *= 10;
2281cb0ef41Sopenharmony_ci      val += c - '0';
2291cb0ef41Sopenharmony_ci    }
2301cb0ef41Sopenharmony_ci
2311cb0ef41Sopenharmony_ci    /* Out of range */
2321cb0ef41Sopenharmony_ci    if (val > 255) {
2331cb0ef41Sopenharmony_ci      return ARES_EBADNAME;
2341cb0ef41Sopenharmony_ci    }
2351cb0ef41Sopenharmony_ci
2361cb0ef41Sopenharmony_ci    if (validate_hostname && !ares__is_hostnamech((unsigned char)val)) {
2371cb0ef41Sopenharmony_ci      return ARES_EBADNAME;
2381cb0ef41Sopenharmony_ci    }
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_ci    return ares__buf_append_byte(label, (unsigned char)val);
2411cb0ef41Sopenharmony_ci  }
2421cb0ef41Sopenharmony_ci
2431cb0ef41Sopenharmony_ci  /* We can just output the character */
2441cb0ef41Sopenharmony_ci  if (validate_hostname && !ares__is_hostnamech(c)) {
2451cb0ef41Sopenharmony_ci    return ARES_EBADNAME;
2461cb0ef41Sopenharmony_ci  }
2471cb0ef41Sopenharmony_ci
2481cb0ef41Sopenharmony_ci  return ares__buf_append_byte(label, c);
2491cb0ef41Sopenharmony_ci}
2501cb0ef41Sopenharmony_ci
2511cb0ef41Sopenharmony_cistatic ares_status_t ares_split_dns_name(ares_dns_labels_t *labels,
2521cb0ef41Sopenharmony_ci                                         ares_bool_t        validate_hostname,
2531cb0ef41Sopenharmony_ci                                         const char        *name)
2541cb0ef41Sopenharmony_ci{
2551cb0ef41Sopenharmony_ci  ares_status_t status;
2561cb0ef41Sopenharmony_ci  ares__buf_t  *label   = NULL;
2571cb0ef41Sopenharmony_ci  ares__buf_t  *namebuf = NULL;
2581cb0ef41Sopenharmony_ci  size_t        i;
2591cb0ef41Sopenharmony_ci  size_t        total_len = 0;
2601cb0ef41Sopenharmony_ci  unsigned char c;
2611cb0ef41Sopenharmony_ci
2621cb0ef41Sopenharmony_ci  if (name == NULL || labels == NULL) {
2631cb0ef41Sopenharmony_ci    return ARES_EFORMERR;
2641cb0ef41Sopenharmony_ci  }
2651cb0ef41Sopenharmony_ci
2661cb0ef41Sopenharmony_ci  /* Put name into a buffer for parsing */
2671cb0ef41Sopenharmony_ci  namebuf = ares__buf_create();
2681cb0ef41Sopenharmony_ci  if (namebuf == NULL) {
2691cb0ef41Sopenharmony_ci    status = ARES_ENOMEM;
2701cb0ef41Sopenharmony_ci    goto done;
2711cb0ef41Sopenharmony_ci  }
2721cb0ef41Sopenharmony_ci
2731cb0ef41Sopenharmony_ci  if (*name != '\0') {
2741cb0ef41Sopenharmony_ci    status =
2751cb0ef41Sopenharmony_ci      ares__buf_append(namebuf, (const unsigned char *)name, ares_strlen(name));
2761cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
2771cb0ef41Sopenharmony_ci      goto done;
2781cb0ef41Sopenharmony_ci    }
2791cb0ef41Sopenharmony_ci  }
2801cb0ef41Sopenharmony_ci
2811cb0ef41Sopenharmony_ci  /* Start with 1 label */
2821cb0ef41Sopenharmony_ci  label = ares_dns_labels_add(labels);
2831cb0ef41Sopenharmony_ci  if (label == NULL) {
2841cb0ef41Sopenharmony_ci    status = ARES_ENOMEM;
2851cb0ef41Sopenharmony_ci    goto done;
2861cb0ef41Sopenharmony_ci  }
2871cb0ef41Sopenharmony_ci
2881cb0ef41Sopenharmony_ci  while (ares__buf_fetch_bytes(namebuf, &c, 1) == ARES_SUCCESS) {
2891cb0ef41Sopenharmony_ci    /* New label */
2901cb0ef41Sopenharmony_ci    if (c == '.') {
2911cb0ef41Sopenharmony_ci      label = ares_dns_labels_add(labels);
2921cb0ef41Sopenharmony_ci      if (label == NULL) {
2931cb0ef41Sopenharmony_ci        status = ARES_ENOMEM;
2941cb0ef41Sopenharmony_ci        goto done;
2951cb0ef41Sopenharmony_ci      }
2961cb0ef41Sopenharmony_ci      continue;
2971cb0ef41Sopenharmony_ci    }
2981cb0ef41Sopenharmony_ci
2991cb0ef41Sopenharmony_ci    /* Escape */
3001cb0ef41Sopenharmony_ci    if (c == '\\') {
3011cb0ef41Sopenharmony_ci      status = ares_parse_dns_name_escape(namebuf, label, validate_hostname);
3021cb0ef41Sopenharmony_ci      if (status != ARES_SUCCESS) {
3031cb0ef41Sopenharmony_ci        goto done;
3041cb0ef41Sopenharmony_ci      }
3051cb0ef41Sopenharmony_ci      continue;
3061cb0ef41Sopenharmony_ci    }
3071cb0ef41Sopenharmony_ci
3081cb0ef41Sopenharmony_ci    /* Output direct character */
3091cb0ef41Sopenharmony_ci    if (validate_hostname && !ares__is_hostnamech(c)) {
3101cb0ef41Sopenharmony_ci      status = ARES_EBADNAME;
3111cb0ef41Sopenharmony_ci      goto done;
3121cb0ef41Sopenharmony_ci    }
3131cb0ef41Sopenharmony_ci
3141cb0ef41Sopenharmony_ci    status = ares__buf_append_byte(label, c);
3151cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
3161cb0ef41Sopenharmony_ci      goto done;
3171cb0ef41Sopenharmony_ci    }
3181cb0ef41Sopenharmony_ci  }
3191cb0ef41Sopenharmony_ci
3201cb0ef41Sopenharmony_ci  /* Remove trailing blank label */
3211cb0ef41Sopenharmony_ci  if (ares__buf_len(ares_dns_labels_get_last(labels)) == 0) {
3221cb0ef41Sopenharmony_ci    ares_dns_name_labels_del_last(labels);
3231cb0ef41Sopenharmony_ci  }
3241cb0ef41Sopenharmony_ci
3251cb0ef41Sopenharmony_ci  /* If someone passed in "." there could have been 2 blank labels, check for
3261cb0ef41Sopenharmony_ci   * that */
3271cb0ef41Sopenharmony_ci  if (labels->num == 1 &&
3281cb0ef41Sopenharmony_ci      ares__buf_len(ares_dns_labels_get_last(labels)) == 0) {
3291cb0ef41Sopenharmony_ci    ares_dns_name_labels_del_last(labels);
3301cb0ef41Sopenharmony_ci  }
3311cb0ef41Sopenharmony_ci
3321cb0ef41Sopenharmony_ci  /* Scan to make sure label lengths are valid */
3331cb0ef41Sopenharmony_ci  for (i = 0; i < labels->num; i++) {
3341cb0ef41Sopenharmony_ci    size_t len = ares__buf_len(labels->label[i]);
3351cb0ef41Sopenharmony_ci    /* No 0-length labels, and no labels over 63 bytes */
3361cb0ef41Sopenharmony_ci    if (len == 0 || len > 63) {
3371cb0ef41Sopenharmony_ci      status = ARES_EBADNAME;
3381cb0ef41Sopenharmony_ci      goto done;
3391cb0ef41Sopenharmony_ci    }
3401cb0ef41Sopenharmony_ci    total_len += len;
3411cb0ef41Sopenharmony_ci  }
3421cb0ef41Sopenharmony_ci
3431cb0ef41Sopenharmony_ci  /* Can't exceed maximum (unescaped) length */
3441cb0ef41Sopenharmony_ci  if (labels->num && total_len + labels->num - 1 > 255) {
3451cb0ef41Sopenharmony_ci    status = ARES_EBADNAME;
3461cb0ef41Sopenharmony_ci    goto done;
3471cb0ef41Sopenharmony_ci  }
3481cb0ef41Sopenharmony_ci
3491cb0ef41Sopenharmony_ci  status = ARES_SUCCESS;
3501cb0ef41Sopenharmony_ci
3511cb0ef41Sopenharmony_cidone:
3521cb0ef41Sopenharmony_ci  ares__buf_destroy(namebuf);
3531cb0ef41Sopenharmony_ci  if (status != ARES_SUCCESS) {
3541cb0ef41Sopenharmony_ci    ares_dns_labels_free(labels);
3551cb0ef41Sopenharmony_ci  }
3561cb0ef41Sopenharmony_ci  return status;
3571cb0ef41Sopenharmony_ci}
3581cb0ef41Sopenharmony_ci
3591cb0ef41Sopenharmony_ciares_status_t ares__dns_name_write(ares__buf_t *buf, ares__llist_t **list,
3601cb0ef41Sopenharmony_ci                                   ares_bool_t validate_hostname,
3611cb0ef41Sopenharmony_ci                                   const char *name)
3621cb0ef41Sopenharmony_ci{
3631cb0ef41Sopenharmony_ci  const ares_nameoffset_t *off = NULL;
3641cb0ef41Sopenharmony_ci  size_t                   name_len;
3651cb0ef41Sopenharmony_ci  size_t                   pos = ares__buf_len(buf);
3661cb0ef41Sopenharmony_ci  ares_dns_labels_t        labels;
3671cb0ef41Sopenharmony_ci  char                     name_copy[512];
3681cb0ef41Sopenharmony_ci  ares_status_t            status;
3691cb0ef41Sopenharmony_ci
3701cb0ef41Sopenharmony_ci  if (buf == NULL || name == NULL) {
3711cb0ef41Sopenharmony_ci    return ARES_EFORMERR;
3721cb0ef41Sopenharmony_ci  }
3731cb0ef41Sopenharmony_ci
3741cb0ef41Sopenharmony_ci  memset(&labels, 0, sizeof(labels));
3751cb0ef41Sopenharmony_ci
3761cb0ef41Sopenharmony_ci  /* NOTE: due to possible escaping, name_copy buffer is > 256 to allow for
3771cb0ef41Sopenharmony_ci   *       this */
3781cb0ef41Sopenharmony_ci  name_len = ares_strcpy(name_copy, name, sizeof(name_copy));
3791cb0ef41Sopenharmony_ci
3801cb0ef41Sopenharmony_ci  /* Find longest match */
3811cb0ef41Sopenharmony_ci  if (list != NULL) {
3821cb0ef41Sopenharmony_ci    off = ares__nameoffset_find(*list, name_copy);
3831cb0ef41Sopenharmony_ci    if (off != NULL && off->name_len != name_len) {
3841cb0ef41Sopenharmony_ci      /* truncate */
3851cb0ef41Sopenharmony_ci      name_len            -= (off->name_len + 1);
3861cb0ef41Sopenharmony_ci      name_copy[name_len]  = 0;
3871cb0ef41Sopenharmony_ci    }
3881cb0ef41Sopenharmony_ci  }
3891cb0ef41Sopenharmony_ci
3901cb0ef41Sopenharmony_ci  /* Output labels */
3911cb0ef41Sopenharmony_ci  if (off == NULL || off->name_len != name_len) {
3921cb0ef41Sopenharmony_ci    size_t i;
3931cb0ef41Sopenharmony_ci
3941cb0ef41Sopenharmony_ci    status = ares_split_dns_name(&labels, validate_hostname, name_copy);
3951cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
3961cb0ef41Sopenharmony_ci      goto done;
3971cb0ef41Sopenharmony_ci    }
3981cb0ef41Sopenharmony_ci
3991cb0ef41Sopenharmony_ci    for (i = 0; i < labels.num; i++) {
4001cb0ef41Sopenharmony_ci      size_t               len = 0;
4011cb0ef41Sopenharmony_ci      const unsigned char *ptr = ares__buf_peek(labels.label[i], &len);
4021cb0ef41Sopenharmony_ci
4031cb0ef41Sopenharmony_ci      status = ares__buf_append_byte(buf, (unsigned char)(len & 0xFF));
4041cb0ef41Sopenharmony_ci      if (status != ARES_SUCCESS) {
4051cb0ef41Sopenharmony_ci        goto done;
4061cb0ef41Sopenharmony_ci      }
4071cb0ef41Sopenharmony_ci
4081cb0ef41Sopenharmony_ci      status = ares__buf_append(buf, ptr, len);
4091cb0ef41Sopenharmony_ci      if (status != ARES_SUCCESS) {
4101cb0ef41Sopenharmony_ci        goto done;
4111cb0ef41Sopenharmony_ci      }
4121cb0ef41Sopenharmony_ci    }
4131cb0ef41Sopenharmony_ci
4141cb0ef41Sopenharmony_ci    /* If we are NOT jumping to another label, output terminator */
4151cb0ef41Sopenharmony_ci    if (off == NULL) {
4161cb0ef41Sopenharmony_ci      status = ares__buf_append_byte(buf, 0);
4171cb0ef41Sopenharmony_ci      if (status != ARES_SUCCESS) {
4181cb0ef41Sopenharmony_ci        goto done;
4191cb0ef41Sopenharmony_ci      }
4201cb0ef41Sopenharmony_ci    }
4211cb0ef41Sopenharmony_ci  }
4221cb0ef41Sopenharmony_ci
4231cb0ef41Sopenharmony_ci  /* Output name compression offset jump */
4241cb0ef41Sopenharmony_ci  if (off != NULL) {
4251cb0ef41Sopenharmony_ci    unsigned short u16 =
4261cb0ef41Sopenharmony_ci      (unsigned short)0xC000 | (unsigned short)(off->idx & 0x3FFF);
4271cb0ef41Sopenharmony_ci    status = ares__buf_append_be16(buf, u16);
4281cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
4291cb0ef41Sopenharmony_ci      goto done;
4301cb0ef41Sopenharmony_ci    }
4311cb0ef41Sopenharmony_ci  }
4321cb0ef41Sopenharmony_ci
4331cb0ef41Sopenharmony_ci  /* Store pointer for future jumps as long as its not an exact match for
4341cb0ef41Sopenharmony_ci   * a prior entry */
4351cb0ef41Sopenharmony_ci  if (list != NULL && (off == NULL || off->name_len != name_len) &&
4361cb0ef41Sopenharmony_ci      name_len > 0) {
4371cb0ef41Sopenharmony_ci    status = ares__nameoffset_create(list, name /* not truncated copy! */, pos);
4381cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
4391cb0ef41Sopenharmony_ci      goto done;
4401cb0ef41Sopenharmony_ci    }
4411cb0ef41Sopenharmony_ci  }
4421cb0ef41Sopenharmony_ci
4431cb0ef41Sopenharmony_ci  status = ARES_SUCCESS;
4441cb0ef41Sopenharmony_ci
4451cb0ef41Sopenharmony_cidone:
4461cb0ef41Sopenharmony_ci  ares_dns_labels_free(&labels);
4471cb0ef41Sopenharmony_ci  return status;
4481cb0ef41Sopenharmony_ci}
4491cb0ef41Sopenharmony_ci
4501cb0ef41Sopenharmony_ci/* Reserved characters for names that need to be escaped */
4511cb0ef41Sopenharmony_cistatic ares_bool_t is_reservedch(int ch)
4521cb0ef41Sopenharmony_ci{
4531cb0ef41Sopenharmony_ci  switch (ch) {
4541cb0ef41Sopenharmony_ci    case '"':
4551cb0ef41Sopenharmony_ci    case '.':
4561cb0ef41Sopenharmony_ci    case ';':
4571cb0ef41Sopenharmony_ci    case '\\':
4581cb0ef41Sopenharmony_ci    case '(':
4591cb0ef41Sopenharmony_ci    case ')':
4601cb0ef41Sopenharmony_ci    case '@':
4611cb0ef41Sopenharmony_ci    case '$':
4621cb0ef41Sopenharmony_ci      return ARES_TRUE;
4631cb0ef41Sopenharmony_ci    default:
4641cb0ef41Sopenharmony_ci      break;
4651cb0ef41Sopenharmony_ci  }
4661cb0ef41Sopenharmony_ci
4671cb0ef41Sopenharmony_ci  return ARES_FALSE;
4681cb0ef41Sopenharmony_ci}
4691cb0ef41Sopenharmony_ci
4701cb0ef41Sopenharmony_cistatic ares_status_t ares__fetch_dnsname_into_buf(ares__buf_t *buf,
4711cb0ef41Sopenharmony_ci                                                  ares__buf_t *dest, size_t len,
4721cb0ef41Sopenharmony_ci                                                  ares_bool_t is_hostname)
4731cb0ef41Sopenharmony_ci{
4741cb0ef41Sopenharmony_ci  size_t               remaining_len;
4751cb0ef41Sopenharmony_ci  const unsigned char *ptr = ares__buf_peek(buf, &remaining_len);
4761cb0ef41Sopenharmony_ci  ares_status_t        status;
4771cb0ef41Sopenharmony_ci  size_t               i;
4781cb0ef41Sopenharmony_ci
4791cb0ef41Sopenharmony_ci  if (buf == NULL || len == 0 || remaining_len < len) {
4801cb0ef41Sopenharmony_ci    return ARES_EBADRESP;
4811cb0ef41Sopenharmony_ci  }
4821cb0ef41Sopenharmony_ci
4831cb0ef41Sopenharmony_ci  for (i = 0; i < len; i++) {
4841cb0ef41Sopenharmony_ci    unsigned char c = ptr[i];
4851cb0ef41Sopenharmony_ci
4861cb0ef41Sopenharmony_ci    /* Hostnames have a very specific allowed character set.  Anything outside
4871cb0ef41Sopenharmony_ci     * of that (non-printable and reserved included) are disallowed */
4881cb0ef41Sopenharmony_ci    if (is_hostname && !ares__is_hostnamech(c)) {
4891cb0ef41Sopenharmony_ci      status = ARES_EBADRESP;
4901cb0ef41Sopenharmony_ci      goto fail;
4911cb0ef41Sopenharmony_ci    }
4921cb0ef41Sopenharmony_ci
4931cb0ef41Sopenharmony_ci    /* NOTE: dest may be NULL if the user is trying to skip the name. validation
4941cb0ef41Sopenharmony_ci     *       still occurs above. */
4951cb0ef41Sopenharmony_ci    if (dest == NULL) {
4961cb0ef41Sopenharmony_ci      continue;
4971cb0ef41Sopenharmony_ci    }
4981cb0ef41Sopenharmony_ci
4991cb0ef41Sopenharmony_ci    /* Non-printable characters need to be output as \DDD */
5001cb0ef41Sopenharmony_ci    if (!ares__isprint(c)) {
5011cb0ef41Sopenharmony_ci      unsigned char escape[4];
5021cb0ef41Sopenharmony_ci
5031cb0ef41Sopenharmony_ci      escape[0] = '\\';
5041cb0ef41Sopenharmony_ci      escape[1] = '0' + (c / 100);
5051cb0ef41Sopenharmony_ci      escape[2] = '0' + ((c % 100) / 10);
5061cb0ef41Sopenharmony_ci      escape[3] = '0' + (c % 10);
5071cb0ef41Sopenharmony_ci
5081cb0ef41Sopenharmony_ci      status = ares__buf_append(dest, escape, sizeof(escape));
5091cb0ef41Sopenharmony_ci      if (status != ARES_SUCCESS) {
5101cb0ef41Sopenharmony_ci        goto fail;
5111cb0ef41Sopenharmony_ci      }
5121cb0ef41Sopenharmony_ci
5131cb0ef41Sopenharmony_ci      continue;
5141cb0ef41Sopenharmony_ci    }
5151cb0ef41Sopenharmony_ci
5161cb0ef41Sopenharmony_ci    /* Reserved characters need to be escaped, otherwise normal */
5171cb0ef41Sopenharmony_ci    if (is_reservedch(c)) {
5181cb0ef41Sopenharmony_ci      status = ares__buf_append_byte(dest, '\\');
5191cb0ef41Sopenharmony_ci      if (status != ARES_SUCCESS) {
5201cb0ef41Sopenharmony_ci        goto fail;
5211cb0ef41Sopenharmony_ci      }
5221cb0ef41Sopenharmony_ci    }
5231cb0ef41Sopenharmony_ci
5241cb0ef41Sopenharmony_ci    status = ares__buf_append_byte(dest, c);
5251cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
5261cb0ef41Sopenharmony_ci      return status;
5271cb0ef41Sopenharmony_ci    }
5281cb0ef41Sopenharmony_ci  }
5291cb0ef41Sopenharmony_ci
5301cb0ef41Sopenharmony_ci  return ares__buf_consume(buf, len);
5311cb0ef41Sopenharmony_ci
5321cb0ef41Sopenharmony_cifail:
5331cb0ef41Sopenharmony_ci  return status;
5341cb0ef41Sopenharmony_ci}
5351cb0ef41Sopenharmony_ci
5361cb0ef41Sopenharmony_ciares_status_t ares__dns_name_parse(ares__buf_t *buf, char **name,
5371cb0ef41Sopenharmony_ci                                   ares_bool_t is_hostname)
5381cb0ef41Sopenharmony_ci{
5391cb0ef41Sopenharmony_ci  size_t        save_offset = 0;
5401cb0ef41Sopenharmony_ci  unsigned char c;
5411cb0ef41Sopenharmony_ci  ares_status_t status;
5421cb0ef41Sopenharmony_ci  ares__buf_t  *namebuf     = NULL;
5431cb0ef41Sopenharmony_ci  size_t        label_start = ares__buf_get_position(buf);
5441cb0ef41Sopenharmony_ci
5451cb0ef41Sopenharmony_ci  if (buf == NULL) {
5461cb0ef41Sopenharmony_ci    return ARES_EFORMERR;
5471cb0ef41Sopenharmony_ci  }
5481cb0ef41Sopenharmony_ci
5491cb0ef41Sopenharmony_ci  if (name != NULL) {
5501cb0ef41Sopenharmony_ci    namebuf = ares__buf_create();
5511cb0ef41Sopenharmony_ci    if (namebuf == NULL) {
5521cb0ef41Sopenharmony_ci      status = ARES_ENOMEM;
5531cb0ef41Sopenharmony_ci      goto fail;
5541cb0ef41Sopenharmony_ci    }
5551cb0ef41Sopenharmony_ci  }
5561cb0ef41Sopenharmony_ci
5571cb0ef41Sopenharmony_ci  /* The compression scheme allows a domain name in a message to be
5581cb0ef41Sopenharmony_ci   * represented as either:
5591cb0ef41Sopenharmony_ci   *
5601cb0ef41Sopenharmony_ci   * - a sequence of labels ending in a zero octet
5611cb0ef41Sopenharmony_ci   * - a pointer
5621cb0ef41Sopenharmony_ci   * - a sequence of labels ending with a pointer
5631cb0ef41Sopenharmony_ci   */
5641cb0ef41Sopenharmony_ci  while (1) {
5651cb0ef41Sopenharmony_ci    /* Keep track of the minimum label starting position to prevent forward
5661cb0ef41Sopenharmony_ci     * jumping */
5671cb0ef41Sopenharmony_ci    if (label_start > ares__buf_get_position(buf)) {
5681cb0ef41Sopenharmony_ci      label_start = ares__buf_get_position(buf);
5691cb0ef41Sopenharmony_ci    }
5701cb0ef41Sopenharmony_ci
5711cb0ef41Sopenharmony_ci    status = ares__buf_fetch_bytes(buf, &c, 1);
5721cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
5731cb0ef41Sopenharmony_ci      goto fail;
5741cb0ef41Sopenharmony_ci    }
5751cb0ef41Sopenharmony_ci
5761cb0ef41Sopenharmony_ci    /* Pointer/Redirect */
5771cb0ef41Sopenharmony_ci    if ((c & 0xc0) == 0xc0) {
5781cb0ef41Sopenharmony_ci      /* The pointer takes the form of a two octet sequence:
5791cb0ef41Sopenharmony_ci       *
5801cb0ef41Sopenharmony_ci       *   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
5811cb0ef41Sopenharmony_ci       *   | 1  1|                OFFSET                   |
5821cb0ef41Sopenharmony_ci       *   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
5831cb0ef41Sopenharmony_ci       *
5841cb0ef41Sopenharmony_ci       * The first two bits are ones.  This allows a pointer to be distinguished
5851cb0ef41Sopenharmony_ci       * from a label, since the label must begin with two zero bits because
5861cb0ef41Sopenharmony_ci       * labels are restricted to 63 octets or less.  (The 10 and 01
5871cb0ef41Sopenharmony_ci       * combinations are reserved for future use.)  The OFFSET field specifies
5881cb0ef41Sopenharmony_ci       * an offset from the start of the message (i.e., the first octet of the
5891cb0ef41Sopenharmony_ci       * ID field in the domain header).  A zero offset specifies the first byte
5901cb0ef41Sopenharmony_ci       * of the ID field, etc.
5911cb0ef41Sopenharmony_ci       */
5921cb0ef41Sopenharmony_ci      size_t offset = (size_t)((c & 0x3F) << 8);
5931cb0ef41Sopenharmony_ci
5941cb0ef41Sopenharmony_ci      /* Fetch second byte of the redirect length */
5951cb0ef41Sopenharmony_ci      status = ares__buf_fetch_bytes(buf, &c, 1);
5961cb0ef41Sopenharmony_ci      if (status != ARES_SUCCESS) {
5971cb0ef41Sopenharmony_ci        goto fail;
5981cb0ef41Sopenharmony_ci      }
5991cb0ef41Sopenharmony_ci
6001cb0ef41Sopenharmony_ci      offset |= ((size_t)c);
6011cb0ef41Sopenharmony_ci
6021cb0ef41Sopenharmony_ci      /* According to RFC 1035 4.1.4:
6031cb0ef41Sopenharmony_ci       *    In this scheme, an entire domain name or a list of labels at
6041cb0ef41Sopenharmony_ci       *    the end of a domain name is replaced with a pointer to a prior
6051cb0ef41Sopenharmony_ci       *    occurrence of the same name.
6061cb0ef41Sopenharmony_ci       * Note the word "prior", meaning it must go backwards.  This was
6071cb0ef41Sopenharmony_ci       * confirmed via the ISC BIND code that it also prevents forward
6081cb0ef41Sopenharmony_ci       * pointers.
6091cb0ef41Sopenharmony_ci       */
6101cb0ef41Sopenharmony_ci      if (offset >= label_start) {
6111cb0ef41Sopenharmony_ci        status = ARES_EBADNAME;
6121cb0ef41Sopenharmony_ci        goto fail;
6131cb0ef41Sopenharmony_ci      }
6141cb0ef41Sopenharmony_ci
6151cb0ef41Sopenharmony_ci      /* First time we make a jump, save the current position */
6161cb0ef41Sopenharmony_ci      if (save_offset == 0) {
6171cb0ef41Sopenharmony_ci        save_offset = ares__buf_get_position(buf);
6181cb0ef41Sopenharmony_ci      }
6191cb0ef41Sopenharmony_ci
6201cb0ef41Sopenharmony_ci      status = ares__buf_set_position(buf, offset);
6211cb0ef41Sopenharmony_ci      if (status != ARES_SUCCESS) {
6221cb0ef41Sopenharmony_ci        status = ARES_EBADNAME;
6231cb0ef41Sopenharmony_ci        goto fail;
6241cb0ef41Sopenharmony_ci      }
6251cb0ef41Sopenharmony_ci
6261cb0ef41Sopenharmony_ci      continue;
6271cb0ef41Sopenharmony_ci    } else if ((c & 0xc0) != 0) {
6281cb0ef41Sopenharmony_ci      /* 10 and 01 are reserved */
6291cb0ef41Sopenharmony_ci      status = ARES_EBADNAME;
6301cb0ef41Sopenharmony_ci      goto fail;
6311cb0ef41Sopenharmony_ci    } else if (c == 0) {
6321cb0ef41Sopenharmony_ci      /* termination via zero octet*/
6331cb0ef41Sopenharmony_ci      break;
6341cb0ef41Sopenharmony_ci    }
6351cb0ef41Sopenharmony_ci
6361cb0ef41Sopenharmony_ci    /* New label */
6371cb0ef41Sopenharmony_ci
6381cb0ef41Sopenharmony_ci    /* Labels are separated by periods */
6391cb0ef41Sopenharmony_ci    if (ares__buf_len(namebuf) != 0 && name != NULL) {
6401cb0ef41Sopenharmony_ci      status = ares__buf_append_byte(namebuf, '.');
6411cb0ef41Sopenharmony_ci      if (status != ARES_SUCCESS) {
6421cb0ef41Sopenharmony_ci        goto fail;
6431cb0ef41Sopenharmony_ci      }
6441cb0ef41Sopenharmony_ci    }
6451cb0ef41Sopenharmony_ci
6461cb0ef41Sopenharmony_ci    status = ares__fetch_dnsname_into_buf(buf, namebuf, c, is_hostname);
6471cb0ef41Sopenharmony_ci    if (status != ARES_SUCCESS) {
6481cb0ef41Sopenharmony_ci      goto fail;
6491cb0ef41Sopenharmony_ci    }
6501cb0ef41Sopenharmony_ci  }
6511cb0ef41Sopenharmony_ci
6521cb0ef41Sopenharmony_ci  /* Restore offset read after first redirect/pointer as this is where the DNS
6531cb0ef41Sopenharmony_ci   * message continues */
6541cb0ef41Sopenharmony_ci  if (save_offset) {
6551cb0ef41Sopenharmony_ci    ares__buf_set_position(buf, save_offset);
6561cb0ef41Sopenharmony_ci  }
6571cb0ef41Sopenharmony_ci
6581cb0ef41Sopenharmony_ci  if (name != NULL) {
6591cb0ef41Sopenharmony_ci    *name = ares__buf_finish_str(namebuf, NULL);
6601cb0ef41Sopenharmony_ci    if (*name == NULL) {
6611cb0ef41Sopenharmony_ci      status = ARES_ENOMEM;
6621cb0ef41Sopenharmony_ci      goto fail;
6631cb0ef41Sopenharmony_ci    }
6641cb0ef41Sopenharmony_ci  }
6651cb0ef41Sopenharmony_ci
6661cb0ef41Sopenharmony_ci  return ARES_SUCCESS;
6671cb0ef41Sopenharmony_ci
6681cb0ef41Sopenharmony_cifail:
6691cb0ef41Sopenharmony_ci  /* We want badname response if we couldn't parse */
6701cb0ef41Sopenharmony_ci  if (status == ARES_EBADRESP) {
6711cb0ef41Sopenharmony_ci    status = ARES_EBADNAME;
6721cb0ef41Sopenharmony_ci  }
6731cb0ef41Sopenharmony_ci
6741cb0ef41Sopenharmony_ci  ares__buf_destroy(namebuf);
6751cb0ef41Sopenharmony_ci  return status;
6761cb0ef41Sopenharmony_ci}
677