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