%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/cares/src/lib/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/cares/src/lib/ares_dns_parse.c |
/* MIT License
*
* Copyright (c) 2023 Brad House
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* SPDX-License-Identifier: MIT
*/
#include "ares_setup.h"
#include "ares.h"
#include "ares_private.h"
#include <limits.h>
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
static size_t ares_dns_rr_remaining_len(const ares__buf_t *buf, size_t orig_len,
size_t rdlength)
{
size_t used_len = orig_len - ares__buf_len(buf);
if (used_len >= rdlength) {
return 0;
}
return rdlength - used_len;
}
static ares_status_t ares_dns_parse_and_set_dns_name(ares__buf_t *buf,
ares_bool_t is_hostname,
ares_dns_rr_t *rr,
ares_dns_rr_key_t key)
{
ares_status_t status;
char *name = NULL;
status = ares__dns_name_parse(buf, &name, is_hostname);
if (status != ARES_SUCCESS) {
return status;
}
status = ares_dns_rr_set_str_own(rr, key, name);
if (status != ARES_SUCCESS) {
ares_free(name);
return status;
}
return ARES_SUCCESS;
}
static ares_status_t ares_dns_parse_and_set_dns_str(
ares__buf_t *buf, size_t max_len, ares_bool_t allow_multiple,
ares_dns_rr_t *rr, ares_dns_rr_key_t key, ares_bool_t blank_allowed)
{
ares_status_t status;
char *str = NULL;
status = ares__buf_parse_dns_str(buf, max_len, &str, allow_multiple);
if (status != ARES_SUCCESS) {
return status;
}
if (!blank_allowed && ares_strlen(str) == 0) {
ares_free(str);
return ARES_EBADRESP;
}
status = ares_dns_rr_set_str_own(rr, key, str);
if (status != ARES_SUCCESS) {
ares_free(str);
return status;
}
return ARES_SUCCESS;
}
static ares_status_t
ares_dns_parse_and_set_dns_binstr(ares__buf_t *buf, size_t max_len,
ares_bool_t allow_multiple,
ares_dns_rr_t *rr, ares_dns_rr_key_t key)
{
ares_status_t status;
unsigned char *bin = NULL;
size_t bin_len = 0;
status =
ares__buf_parse_dns_binstr(buf, max_len, &bin, &bin_len, allow_multiple);
if (status != ARES_SUCCESS) {
return status;
}
status = ares_dns_rr_set_bin_own(rr, key, bin, bin_len);
if (status != ARES_SUCCESS) {
ares_free(bin);
return status;
}
return ARES_SUCCESS;
}
static ares_status_t ares_dns_parse_and_set_be32(ares__buf_t *buf,
ares_dns_rr_t *rr,
ares_dns_rr_key_t key)
{
ares_status_t status;
unsigned int u32;
status = ares__buf_fetch_be32(buf, &u32);
if (status != ARES_SUCCESS) {
return status;
}
return ares_dns_rr_set_u32(rr, key, u32);
}
static ares_status_t ares_dns_parse_and_set_be16(ares__buf_t *buf,
ares_dns_rr_t *rr,
ares_dns_rr_key_t key)
{
ares_status_t status;
unsigned short u16;
status = ares__buf_fetch_be16(buf, &u16);
if (status != ARES_SUCCESS) {
return status;
}
return ares_dns_rr_set_u16(rr, key, u16);
}
static ares_status_t ares_dns_parse_and_set_u8(ares__buf_t *buf,
ares_dns_rr_t *rr,
ares_dns_rr_key_t key)
{
ares_status_t status;
unsigned char u8;
status = ares__buf_fetch_bytes(buf, &u8, 1);
if (status != ARES_SUCCESS) {
return status;
}
return ares_dns_rr_set_u8(rr, key, u8);
}
static ares_status_t ares_dns_parse_rr_a(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
struct in_addr addr;
ares_status_t status;
(void)rdlength; /* Not needed */
status = ares__buf_fetch_bytes(buf, (unsigned char *)&addr, sizeof(addr));
if (status != ARES_SUCCESS) {
return status;
}
return ares_dns_rr_set_addr(rr, ARES_RR_A_ADDR, &addr);
}
static ares_status_t ares_dns_parse_rr_ns(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
(void)rdlength; /* Not needed */
return ares_dns_parse_and_set_dns_name(buf, ARES_FALSE, rr,
ARES_RR_NS_NSDNAME);
}
static ares_status_t ares_dns_parse_rr_cname(ares__buf_t *buf,
ares_dns_rr_t *rr, size_t rdlength)
{
(void)rdlength; /* Not needed */
return ares_dns_parse_and_set_dns_name(buf, ARES_FALSE, rr,
ARES_RR_CNAME_CNAME);
}
static ares_status_t ares_dns_parse_rr_soa(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
ares_status_t status;
(void)rdlength; /* Not needed */
/* MNAME */
status =
ares_dns_parse_and_set_dns_name(buf, ARES_FALSE, rr, ARES_RR_SOA_MNAME);
if (status != ARES_SUCCESS) {
return status;
}
/* RNAME */
status =
ares_dns_parse_and_set_dns_name(buf, ARES_FALSE, rr, ARES_RR_SOA_RNAME);
if (status != ARES_SUCCESS) {
return status;
}
/* SERIAL */
status = ares_dns_parse_and_set_be32(buf, rr, ARES_RR_SOA_SERIAL);
if (status != ARES_SUCCESS) {
return status;
}
/* REFRESH */
status = ares_dns_parse_and_set_be32(buf, rr, ARES_RR_SOA_REFRESH);
if (status != ARES_SUCCESS) {
return status;
}
/* RETRY */
status = ares_dns_parse_and_set_be32(buf, rr, ARES_RR_SOA_RETRY);
if (status != ARES_SUCCESS) {
return status;
}
/* EXPIRE */
status = ares_dns_parse_and_set_be32(buf, rr, ARES_RR_SOA_EXPIRE);
if (status != ARES_SUCCESS) {
return status;
}
/* MINIMUM */
return ares_dns_parse_and_set_be32(buf, rr, ARES_RR_SOA_MINIMUM);
}
static ares_status_t ares_dns_parse_rr_ptr(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
(void)rdlength; /* Not needed */
return ares_dns_parse_and_set_dns_name(buf, ARES_FALSE, rr,
ARES_RR_PTR_DNAME);
}
static ares_status_t ares_dns_parse_rr_hinfo(ares__buf_t *buf,
ares_dns_rr_t *rr, size_t rdlength)
{
ares_status_t status;
size_t orig_len = ares__buf_len(buf);
(void)rdlength; /* Not needed */
/* CPU */
status = ares_dns_parse_and_set_dns_str(
buf, ares_dns_rr_remaining_len(buf, orig_len, rdlength), ARES_FALSE, rr,
ARES_RR_HINFO_CPU, ARES_TRUE);
if (status != ARES_SUCCESS) {
return status;
}
/* OS */
status = ares_dns_parse_and_set_dns_str(
buf, ares_dns_rr_remaining_len(buf, orig_len, rdlength), ARES_FALSE, rr,
ARES_RR_HINFO_OS, ARES_TRUE);
return status;
}
static ares_status_t ares_dns_parse_rr_mx(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
ares_status_t status;
(void)rdlength; /* Not needed */
/* PREFERENCE */
status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_MX_PREFERENCE);
if (status != ARES_SUCCESS) {
return status;
}
/* EXCHANGE */
return ares_dns_parse_and_set_dns_name(buf, ARES_FALSE, rr,
ARES_RR_MX_EXCHANGE);
}
static ares_status_t ares_dns_parse_rr_txt(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
return ares_dns_parse_and_set_dns_binstr(buf, rdlength, ARES_TRUE, rr,
ARES_RR_TXT_DATA);
}
static ares_status_t ares_dns_parse_rr_aaaa(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
struct ares_in6_addr addr;
ares_status_t status;
(void)rdlength; /* Not needed */
status = ares__buf_fetch_bytes(buf, (unsigned char *)&addr, sizeof(addr));
if (status != ARES_SUCCESS) {
return status;
}
return ares_dns_rr_set_addr6(rr, ARES_RR_AAAA_ADDR, &addr);
}
static ares_status_t ares_dns_parse_rr_srv(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
ares_status_t status;
(void)rdlength; /* Not needed */
/* PRIORITY */
status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_SRV_PRIORITY);
if (status != ARES_SUCCESS) {
return status;
}
/* WEIGHT */
status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_SRV_WEIGHT);
if (status != ARES_SUCCESS) {
return status;
}
/* PORT */
status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_SRV_PORT);
if (status != ARES_SUCCESS) {
return status;
}
/* TARGET */
return ares_dns_parse_and_set_dns_name(buf, ARES_FALSE, rr,
ARES_RR_SRV_TARGET);
}
static ares_status_t ares_dns_parse_rr_naptr(ares__buf_t *buf,
ares_dns_rr_t *rr, size_t rdlength)
{
ares_status_t status;
size_t orig_len = ares__buf_len(buf);
/* ORDER */
status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_NAPTR_ORDER);
if (status != ARES_SUCCESS) {
return status;
}
/* PREFERENCE */
status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_NAPTR_PREFERENCE);
if (status != ARES_SUCCESS) {
return status;
}
/* FLAGS */
status = ares_dns_parse_and_set_dns_str(
buf, ares_dns_rr_remaining_len(buf, orig_len, rdlength), ARES_FALSE, rr,
ARES_RR_NAPTR_FLAGS, ARES_TRUE);
if (status != ARES_SUCCESS) {
return status;
}
/* SERVICES */
status = ares_dns_parse_and_set_dns_str(
buf, ares_dns_rr_remaining_len(buf, orig_len, rdlength), ARES_FALSE, rr,
ARES_RR_NAPTR_SERVICES, ARES_TRUE);
if (status != ARES_SUCCESS) {
return status;
}
/* REGEXP */
status = ares_dns_parse_and_set_dns_str(
buf, ares_dns_rr_remaining_len(buf, orig_len, rdlength), ARES_FALSE, rr,
ARES_RR_NAPTR_REGEXP, ARES_TRUE);
if (status != ARES_SUCCESS) {
return status;
}
/* REPLACEMENT */
return ares_dns_parse_and_set_dns_name(buf, ARES_FALSE, rr,
ARES_RR_NAPTR_REPLACEMENT);
}
static ares_status_t ares_dns_parse_rr_opt(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength,
unsigned short raw_class,
unsigned int raw_ttl)
{
ares_status_t status;
size_t orig_len = ares__buf_len(buf);
unsigned short rcode_high;
status = ares_dns_rr_set_u16(rr, ARES_RR_OPT_UDP_SIZE, raw_class);
if (status != ARES_SUCCESS) {
return status;
}
/* First 8 bits of TTL are an extended RCODE, and they go in the higher order
* after the original 4-bit rcode */
rcode_high = (unsigned short)((raw_ttl >> 20) & 0x0FF0);
rr->parent->raw_rcode |= rcode_high;
status = ares_dns_rr_set_u8(rr, ARES_RR_OPT_VERSION,
(unsigned char)(raw_ttl >> 16) & 0xFF);
if (status != ARES_SUCCESS) {
return status;
}
status = ares_dns_rr_set_u16(rr, ARES_RR_OPT_FLAGS,
(unsigned short)(raw_ttl & 0xFFFF));
if (status != ARES_SUCCESS) {
return status;
}
/* Parse options */
while (ares_dns_rr_remaining_len(buf, orig_len, rdlength)) {
unsigned short opt = 0;
unsigned short len = 0;
unsigned char *val = NULL;
/* Fetch be16 option */
status = ares__buf_fetch_be16(buf, &opt);
if (status != ARES_SUCCESS) {
return status;
}
/* Fetch be16 length */
status = ares__buf_fetch_be16(buf, &len);
if (status != ARES_SUCCESS) {
return status;
}
if (len) {
status = ares__buf_fetch_bytes_dup(buf, len, ARES_TRUE, &val);
if (status != ARES_SUCCESS) {
return status;
}
}
status = ares_dns_rr_set_opt_own(rr, ARES_RR_OPT_OPTIONS, opt, val, len);
if (status != ARES_SUCCESS) {
return status;
}
}
return ARES_SUCCESS;
}
static ares_status_t ares_dns_parse_rr_tlsa(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
ares_status_t status;
size_t orig_len = ares__buf_len(buf);
size_t len;
unsigned char *data;
status = ares_dns_parse_and_set_u8(buf, rr, ARES_RR_TLSA_CERT_USAGE);
if (status != ARES_SUCCESS) {
return status;
}
status = ares_dns_parse_and_set_u8(buf, rr, ARES_RR_TLSA_SELECTOR);
if (status != ARES_SUCCESS) {
return status;
}
status = ares_dns_parse_and_set_u8(buf, rr, ARES_RR_TLSA_MATCH);
if (status != ARES_SUCCESS) {
return status;
}
len = ares_dns_rr_remaining_len(buf, orig_len, rdlength);
if (len == 0) {
return ARES_EBADRESP;
}
status = ares__buf_fetch_bytes_dup(buf, len, ARES_FALSE, &data);
if (status != ARES_SUCCESS) {
return status;
}
status = ares_dns_rr_set_bin_own(rr, ARES_RR_TLSA_DATA, data, len);
if (status != ARES_SUCCESS) {
ares_free(data);
return status;
}
return ARES_SUCCESS;
}
static ares_status_t ares_dns_parse_rr_svcb(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
ares_status_t status;
size_t orig_len = ares__buf_len(buf);
status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_SVCB_PRIORITY);
if (status != ARES_SUCCESS) {
return status;
}
status =
ares_dns_parse_and_set_dns_name(buf, ARES_FALSE, rr, ARES_RR_SVCB_TARGET);
if (status != ARES_SUCCESS) {
return status;
}
/* Parse params */
while (ares_dns_rr_remaining_len(buf, orig_len, rdlength)) {
unsigned short opt = 0;
unsigned short len = 0;
unsigned char *val = NULL;
/* Fetch be16 option */
status = ares__buf_fetch_be16(buf, &opt);
if (status != ARES_SUCCESS) {
return status;
}
/* Fetch be16 length */
status = ares__buf_fetch_be16(buf, &len);
if (status != ARES_SUCCESS) {
return status;
}
if (len) {
status = ares__buf_fetch_bytes_dup(buf, len, ARES_TRUE, &val);
if (status != ARES_SUCCESS) {
return status;
}
}
status = ares_dns_rr_set_opt_own(rr, ARES_RR_SVCB_PARAMS, opt, val, len);
if (status != ARES_SUCCESS) {
return status;
}
}
return ARES_SUCCESS;
}
static ares_status_t ares_dns_parse_rr_https(ares__buf_t *buf,
ares_dns_rr_t *rr, size_t rdlength)
{
ares_status_t status;
size_t orig_len = ares__buf_len(buf);
status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_HTTPS_PRIORITY);
if (status != ARES_SUCCESS) {
return status;
}
status =
ares_dns_parse_and_set_dns_name(buf, ARES_FALSE, rr, ARES_RR_HTTPS_TARGET);
if (status != ARES_SUCCESS) {
return status;
}
/* Parse params */
while (ares_dns_rr_remaining_len(buf, orig_len, rdlength)) {
unsigned short opt = 0;
unsigned short len = 0;
unsigned char *val = NULL;
/* Fetch be16 option */
status = ares__buf_fetch_be16(buf, &opt);
if (status != ARES_SUCCESS) {
return status;
}
/* Fetch be16 length */
status = ares__buf_fetch_be16(buf, &len);
if (status != ARES_SUCCESS) {
return status;
}
if (len) {
status = ares__buf_fetch_bytes_dup(buf, len, ARES_TRUE, &val);
if (status != ARES_SUCCESS) {
return status;
}
}
status = ares_dns_rr_set_opt_own(rr, ARES_RR_HTTPS_PARAMS, opt, val, len);
if (status != ARES_SUCCESS) {
return status;
}
}
return ARES_SUCCESS;
}
static ares_status_t ares_dns_parse_rr_uri(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
char *name = NULL;
ares_status_t status;
size_t orig_len = ares__buf_len(buf);
size_t remaining_len;
/* PRIORITY */
status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_URI_PRIORITY);
if (status != ARES_SUCCESS) {
return status;
}
/* WEIGHT */
status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_URI_WEIGHT);
if (status != ARES_SUCCESS) {
return status;
}
/* TARGET -- not in string format, rest of buffer, required to be
* non-zero length */
remaining_len = ares_dns_rr_remaining_len(buf, orig_len, rdlength);
if (remaining_len == 0) {
status = ARES_EBADRESP;
return status;
}
/* NOTE: Not in DNS string format */
status = ares__buf_fetch_str_dup(buf, remaining_len, &name);
if (status != ARES_SUCCESS) {
return status;
}
status = ares_dns_rr_set_str_own(rr, ARES_RR_URI_TARGET, name);
if (status != ARES_SUCCESS) {
ares_free(name);
return status;
}
name = NULL;
return ARES_SUCCESS;
}
static ares_status_t ares_dns_parse_rr_caa(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
unsigned char *data = NULL;
size_t data_len = 0;
ares_status_t status;
size_t orig_len = ares__buf_len(buf);
/* CRITICAL */
status = ares_dns_parse_and_set_u8(buf, rr, ARES_RR_CAA_CRITICAL);
if (status != ARES_SUCCESS) {
return status;
}
/* Tag */
status = ares_dns_parse_and_set_dns_str(
buf, ares_dns_rr_remaining_len(buf, orig_len, rdlength), ARES_FALSE, rr,
ARES_RR_CAA_TAG, ARES_FALSE);
if (status != ARES_SUCCESS) {
return status;
}
/* Value - binary! (remaining buffer */
data_len = ares_dns_rr_remaining_len(buf, orig_len, rdlength);
if (data_len == 0) {
status = ARES_EBADRESP;
return status;
}
status = ares__buf_fetch_bytes_dup(buf, data_len, ARES_TRUE, &data);
if (status != ARES_SUCCESS) {
return status;
}
status = ares_dns_rr_set_bin_own(rr, ARES_RR_CAA_VALUE, data, data_len);
if (status != ARES_SUCCESS) {
ares_free(data);
return status;
}
data = NULL;
return ARES_SUCCESS;
}
static ares_status_t ares_dns_parse_rr_raw_rr(ares__buf_t *buf,
ares_dns_rr_t *rr,
size_t rdlength,
unsigned short raw_type)
{
ares_status_t status;
unsigned char *bytes = NULL;
if (rdlength == 0) {
return ARES_SUCCESS;
}
status = ares__buf_fetch_bytes_dup(buf, rdlength, ARES_FALSE, &bytes);
if (status != ARES_SUCCESS) {
return status;
}
/* Can't fail */
status = ares_dns_rr_set_u16(rr, ARES_RR_RAW_RR_TYPE, raw_type);
if (status != ARES_SUCCESS) {
ares_free(bytes);
return status;
}
status = ares_dns_rr_set_bin_own(rr, ARES_RR_RAW_RR_DATA, bytes, rdlength);
if (status != ARES_SUCCESS) {
ares_free(bytes);
return status;
}
return ARES_SUCCESS;
}
static ares_status_t ares_dns_parse_header(ares__buf_t *buf, unsigned int flags,
ares_dns_record_t **dnsrec,
unsigned short *qdcount,
unsigned short *ancount,
unsigned short *nscount,
unsigned short *arcount)
{
ares_status_t status = ARES_EBADRESP;
unsigned short u16;
unsigned short id;
unsigned short dns_flags = 0;
ares_dns_opcode_t opcode;
unsigned short rcode;
(void)flags; /* currently unused */
if (buf == NULL || dnsrec == NULL || qdcount == NULL || ancount == NULL ||
nscount == NULL || arcount == NULL) {
return ARES_EFORMERR;
}
*dnsrec = NULL;
/*
* RFC 1035 4.1.1. Header section format.
* and Updated by RFC 2065 to add AD and CD bits.
* 1 1 1 1 1 1
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | ID |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* |QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | QDCOUNT |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | ANCOUNT |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | NSCOUNT |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | ARCOUNT |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
/* ID */
status = ares__buf_fetch_be16(buf, &id);
if (status != ARES_SUCCESS) {
goto fail;
}
/* Flags */
status = ares__buf_fetch_be16(buf, &u16);
if (status != ARES_SUCCESS) {
goto fail;
}
/* QR */
if (u16 & 0x8000) {
dns_flags |= ARES_FLAG_QR;
}
/* OPCODE */
opcode = (u16 >> 11) & 0xf;
/* AA */
if (u16 & 0x400) {
dns_flags |= ARES_FLAG_AA;
}
/* TC */
if (u16 & 0x200) {
dns_flags |= ARES_FLAG_TC;
}
/* RD */
if (u16 & 0x100) {
dns_flags |= ARES_FLAG_RD;
}
/* RA */
if (u16 & 0x80) {
dns_flags |= ARES_FLAG_RA;
}
/* Z -- unused */
/* AD */
if (u16 & 0x20) {
dns_flags |= ARES_FLAG_AD;
}
/* CD */
if (u16 & 0x10) {
dns_flags |= ARES_FLAG_CD;
}
/* RCODE */
rcode = u16 & 0xf;
/* QDCOUNT */
status = ares__buf_fetch_be16(buf, qdcount);
if (status != ARES_SUCCESS) {
goto fail;
}
/* ANCOUNT */
status = ares__buf_fetch_be16(buf, ancount);
if (status != ARES_SUCCESS) {
goto fail;
}
/* NSCOUNT */
status = ares__buf_fetch_be16(buf, nscount);
if (status != ARES_SUCCESS) {
goto fail;
}
/* ARCOUNT */
status = ares__buf_fetch_be16(buf, arcount);
if (status != ARES_SUCCESS) {
goto fail;
}
status = ares_dns_record_create(dnsrec, id, dns_flags, opcode,
ARES_RCODE_NOERROR /* Temporary */);
if (status != ARES_SUCCESS) {
goto fail;
}
(*dnsrec)->raw_rcode = rcode;
if (*ancount > 0) {
status =
ares_dns_record_rr_prealloc(*dnsrec, ARES_SECTION_ANSWER, *ancount);
if (status != ARES_SUCCESS) {
goto fail;
}
}
if (*nscount > 0) {
status =
ares_dns_record_rr_prealloc(*dnsrec, ARES_SECTION_AUTHORITY, *nscount);
if (status != ARES_SUCCESS) {
goto fail;
}
}
if (*arcount > 0) {
status =
ares_dns_record_rr_prealloc(*dnsrec, ARES_SECTION_ADDITIONAL, *arcount);
if (status != ARES_SUCCESS) {
goto fail;
}
}
return ARES_SUCCESS;
fail:
ares_dns_record_destroy(*dnsrec);
*dnsrec = NULL;
*qdcount = 0;
*ancount = 0;
*nscount = 0;
*arcount = 0;
return status;
}
static ares_status_t
ares_dns_parse_rr_data(ares__buf_t *buf, size_t rdlength, ares_dns_rr_t *rr,
ares_dns_rec_type_t type, unsigned short raw_type,
unsigned short raw_class, unsigned int raw_ttl)
{
switch (type) {
case ARES_REC_TYPE_A:
return ares_dns_parse_rr_a(buf, rr, rdlength);
case ARES_REC_TYPE_NS:
return ares_dns_parse_rr_ns(buf, rr, rdlength);
case ARES_REC_TYPE_CNAME:
return ares_dns_parse_rr_cname(buf, rr, rdlength);
case ARES_REC_TYPE_SOA:
return ares_dns_parse_rr_soa(buf, rr, rdlength);
case ARES_REC_TYPE_PTR:
return ares_dns_parse_rr_ptr(buf, rr, rdlength);
case ARES_REC_TYPE_HINFO:
return ares_dns_parse_rr_hinfo(buf, rr, rdlength);
case ARES_REC_TYPE_MX:
return ares_dns_parse_rr_mx(buf, rr, rdlength);
case ARES_REC_TYPE_TXT:
return ares_dns_parse_rr_txt(buf, rr, rdlength);
case ARES_REC_TYPE_AAAA:
return ares_dns_parse_rr_aaaa(buf, rr, rdlength);
case ARES_REC_TYPE_SRV:
return ares_dns_parse_rr_srv(buf, rr, rdlength);
case ARES_REC_TYPE_NAPTR:
return ares_dns_parse_rr_naptr(buf, rr, rdlength);
case ARES_REC_TYPE_ANY:
return ARES_EBADRESP;
case ARES_REC_TYPE_OPT:
return ares_dns_parse_rr_opt(buf, rr, rdlength, raw_class, raw_ttl);
case ARES_REC_TYPE_TLSA:
return ares_dns_parse_rr_tlsa(buf, rr, rdlength);
case ARES_REC_TYPE_SVCB:
return ares_dns_parse_rr_svcb(buf, rr, rdlength);
case ARES_REC_TYPE_HTTPS:
return ares_dns_parse_rr_https(buf, rr, rdlength);
case ARES_REC_TYPE_URI:
return ares_dns_parse_rr_uri(buf, rr, rdlength);
case ARES_REC_TYPE_CAA:
return ares_dns_parse_rr_caa(buf, rr, rdlength);
case ARES_REC_TYPE_RAW_RR:
return ares_dns_parse_rr_raw_rr(buf, rr, rdlength, raw_type);
}
return ARES_EFORMERR;
}
static ares_status_t ares_dns_parse_qd(ares__buf_t *buf,
ares_dns_record_t *dnsrec)
{
char *name = NULL;
unsigned short u16;
ares_status_t status;
ares_dns_rec_type_t type;
ares_dns_class_t qclass;
/* The question section is used to carry the "question" in most queries,
* i.e., the parameters that define what is being asked. The section
* contains QDCOUNT (usually 1) entries, each of the following format:
* 1 1 1 1 1 1
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | |
* / QNAME /
* / /
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | QTYPE |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | QCLASS |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
/* Name */
status = ares__dns_name_parse(buf, &name, ARES_FALSE);
if (status != ARES_SUCCESS) {
goto done;
}
/* Type */
status = ares__buf_fetch_be16(buf, &u16);
if (status != ARES_SUCCESS) {
goto done;
}
type = u16;
/* Class */
status = ares__buf_fetch_be16(buf, &u16);
if (status != ARES_SUCCESS) {
goto done;
}
qclass = u16;
/* Add question */
status = ares_dns_record_query_add(dnsrec, name, type, qclass);
if (status != ARES_SUCCESS) {
goto done;
}
done:
ares_free(name);
return status;
}
static ares_status_t ares_dns_parse_rr(ares__buf_t *buf, unsigned int flags,
ares_dns_section_t sect,
ares_dns_record_t *dnsrec)
{
char *name = NULL;
unsigned short u16;
unsigned short raw_type;
ares_status_t status;
ares_dns_rec_type_t type;
ares_dns_class_t qclass;
unsigned int ttl;
size_t rdlength;
ares_dns_rr_t *rr = NULL;
size_t remaining_len = 0;
size_t processed_len = 0;
ares_bool_t namecomp;
/* All RRs have the same top level format shown below:
* 1 1 1 1 1 1
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | |
* / /
* / NAME /
* | |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | TYPE |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | CLASS |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | TTL |
* | |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | RDLENGTH |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
* / RDATA /
* / /
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
/* Name */
status = ares__dns_name_parse(buf, &name, ARES_FALSE);
if (status != ARES_SUCCESS) {
goto done;
}
/* Type */
status = ares__buf_fetch_be16(buf, &u16);
if (status != ARES_SUCCESS) {
goto done;
}
type = u16;
raw_type = u16; /* Only used for raw rr data */
/* Class */
status = ares__buf_fetch_be16(buf, &u16);
if (status != ARES_SUCCESS) {
goto done;
}
qclass = u16;
/* TTL */
status = ares__buf_fetch_be32(buf, &ttl);
if (status != ARES_SUCCESS) {
goto done;
}
/* Length */
status = ares__buf_fetch_be16(buf, &u16);
if (status != ARES_SUCCESS) {
goto done;
}
rdlength = u16;
if (!ares_dns_rec_type_isvalid(type, ARES_FALSE)) {
type = ARES_REC_TYPE_RAW_RR;
}
namecomp = ares_dns_rec_type_allow_name_compression(type);
if (sect == ARES_SECTION_ANSWER &&
(flags &
(namecomp ? ARES_DNS_PARSE_AN_BASE_RAW : ARES_DNS_PARSE_AN_EXT_RAW))) {
type = ARES_REC_TYPE_RAW_RR;
}
if (sect == ARES_SECTION_AUTHORITY &&
(flags &
(namecomp ? ARES_DNS_PARSE_NS_BASE_RAW : ARES_DNS_PARSE_NS_EXT_RAW))) {
type = ARES_REC_TYPE_RAW_RR;
}
if (sect == ARES_SECTION_ADDITIONAL &&
(flags &
(namecomp ? ARES_DNS_PARSE_AR_BASE_RAW : ARES_DNS_PARSE_AR_EXT_RAW))) {
type = ARES_REC_TYPE_RAW_RR;
}
/* Pull into another buffer for safety */
if (rdlength > ares__buf_len(buf)) {
status = ARES_EBADRESP;
goto done;
}
/* Add the base rr */
status =
ares_dns_record_rr_add(&rr, dnsrec, sect, name, type,
type == ARES_REC_TYPE_OPT ? ARES_CLASS_IN : qclass,
type == ARES_REC_TYPE_OPT ? 0 : ttl);
if (status != ARES_SUCCESS) {
goto done;
}
/* Record the current remaining length in the buffer so we can tell how
* much was processed */
remaining_len = ares__buf_len(buf);
/* Fill in the data for the rr */
status = ares_dns_parse_rr_data(buf, rdlength, rr, type, raw_type,
(unsigned short)qclass, ttl);
if (status != ARES_SUCCESS) {
goto done;
}
/* Determine how many bytes were processed */
processed_len = remaining_len - ares__buf_len(buf);
/* If too many bytes were processed, error! */
if (processed_len > rdlength) {
status = ARES_EBADRESP;
goto done;
}
/* If too few bytes were processed, consume the unprocessed data for this
* record as the parser may not have wanted/needed to use it */
if (processed_len < rdlength) {
ares__buf_consume(buf, rdlength - processed_len);
}
done:
ares_free(name);
return status;
}
static ares_status_t ares_dns_parse_buf(ares__buf_t *buf, unsigned int flags,
ares_dns_record_t **dnsrec)
{
ares_status_t status;
unsigned short qdcount;
unsigned short ancount;
unsigned short nscount;
unsigned short arcount;
unsigned short i;
if (buf == NULL || dnsrec == NULL) {
return ARES_EFORMERR;
}
/* Maximum DNS packet size is 64k, even over TCP */
if (ares__buf_len(buf) > 0xFFFF) {
return ARES_EFORMERR;
}
/* All communications inside of the domain protocol are carried in a single
* format called a message. The top level format of message is divided
* into 5 sections (some of which are empty in certain cases) shown below:
*
* +---------------------+
* | Header |
* +---------------------+
* | Question | the question for the name server
* +---------------------+
* | Answer | RRs answering the question
* +---------------------+
* | Authority | RRs pointing toward an authority
* +---------------------+
* | Additional | RRs holding additional information
* +---------------------+
*/
/* Parse header */
status = ares_dns_parse_header(buf, flags, dnsrec, &qdcount, &ancount,
&nscount, &arcount);
if (status != ARES_SUCCESS) {
goto fail;
}
/* Must have questions */
if (qdcount == 0) {
status = ARES_EBADRESP;
goto fail;
}
/* XXX: this should be controlled by a flag in case we want to allow
* multiple questions. I think mDNS allows this */
if (qdcount > 1) {
status = ARES_EBADRESP;
goto fail;
}
/* Parse questions */
for (i = 0; i < qdcount; i++) {
status = ares_dns_parse_qd(buf, *dnsrec);
if (status != ARES_SUCCESS) {
goto fail;
}
}
/* Parse Answers */
for (i = 0; i < ancount; i++) {
status = ares_dns_parse_rr(buf, flags, ARES_SECTION_ANSWER, *dnsrec);
if (status != ARES_SUCCESS) {
goto fail;
}
}
/* Parse Authority */
for (i = 0; i < nscount; i++) {
status = ares_dns_parse_rr(buf, flags, ARES_SECTION_AUTHORITY, *dnsrec);
if (status != ARES_SUCCESS) {
goto fail;
}
}
/* Parse Additional */
for (i = 0; i < arcount; i++) {
status = ares_dns_parse_rr(buf, flags, ARES_SECTION_ADDITIONAL, *dnsrec);
if (status != ARES_SUCCESS) {
goto fail;
}
}
/* Finalize rcode now that if we have OPT it is processed */
if (!ares_dns_rcode_isvalid((*dnsrec)->raw_rcode)) {
(*dnsrec)->rcode = ARES_RCODE_SERVFAIL;
} else {
(*dnsrec)->rcode = (ares_dns_rcode_t)(*dnsrec)->raw_rcode;
}
return ARES_SUCCESS;
fail:
ares_dns_record_destroy(*dnsrec);
*dnsrec = NULL;
return status;
}
ares_status_t ares_dns_parse(const unsigned char *buf, size_t buf_len,
unsigned int flags, ares_dns_record_t **dnsrec)
{
ares__buf_t *parser = NULL;
ares_status_t status;
if (buf == NULL || buf_len == 0 || dnsrec == NULL) {
return ARES_EFORMERR;
}
parser = ares__buf_create_const(buf, buf_len);
if (parser == NULL) {
return ARES_ENOMEM;
}
status = ares_dns_parse_buf(parser, flags, dnsrec);
ares__buf_destroy(parser);
return status;
}