You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
920 lines
25 KiB
920 lines
25 KiB
/* 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 "ares_dns_record.h" |
|
#include <limits.h> |
|
#ifdef HAVE_STDINT_H |
|
# include <stdint.h> |
|
#endif |
|
|
|
|
|
static ares_status_t ares_dns_write_header(const ares_dns_record_t *dnsrec, |
|
ares__buf_t *buf) |
|
{ |
|
unsigned short u16; |
|
unsigned short opcode; |
|
unsigned short rcode; |
|
|
|
ares_status_t status; |
|
|
|
/* ID */ |
|
status = ares__buf_append_be16(buf, dnsrec->id); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Flags */ |
|
u16 = 0; |
|
|
|
/* QR */ |
|
if (dnsrec->flags & ARES_FLAG_QR) { |
|
u16 |= 0x8000; |
|
} |
|
|
|
/* OPCODE */ |
|
opcode = (unsigned short)(dnsrec->opcode & 0xF); |
|
opcode <<= 11; |
|
u16 |= opcode; |
|
|
|
/* AA */ |
|
if (dnsrec->flags & ARES_FLAG_AA) { |
|
u16 |= 0x400; |
|
} |
|
|
|
/* TC */ |
|
if (dnsrec->flags & ARES_FLAG_TC) { |
|
u16 |= 0x200; |
|
} |
|
|
|
/* RD */ |
|
if (dnsrec->flags & ARES_FLAG_RD) { |
|
u16 |= 0x100; |
|
} |
|
|
|
/* RA */ |
|
if (dnsrec->flags & ARES_FLAG_RA) { |
|
u16 |= 0x80; |
|
} |
|
|
|
/* Z -- unused */ |
|
|
|
/* AD */ |
|
if (dnsrec->flags & ARES_FLAG_AD) { |
|
u16 |= 0x20; |
|
} |
|
|
|
/* CD */ |
|
if (dnsrec->flags & ARES_FLAG_CD) { |
|
u16 |= 0x10; |
|
} |
|
|
|
/* RCODE */ |
|
rcode = (unsigned short)(dnsrec->rcode & 0xF); |
|
u16 |= rcode; |
|
|
|
status = ares__buf_append_be16(buf, u16); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* QDCOUNT */ |
|
status = ares__buf_append_be16(buf, (unsigned short)dnsrec->qdcount); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* ANCOUNT */ |
|
status = ares__buf_append_be16(buf, (unsigned short)dnsrec->ancount); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* NSCOUNT */ |
|
status = ares__buf_append_be16(buf, (unsigned short)dnsrec->nscount); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* ARCOUNT */ |
|
status = ares__buf_append_be16(buf, (unsigned short)dnsrec->arcount); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
return ARES_SUCCESS; |
|
} |
|
|
|
static ares_status_t ares_dns_write_questions(const ares_dns_record_t *dnsrec, |
|
ares__llist_t **namelist, |
|
ares__buf_t *buf) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; i < ares_dns_record_query_cnt(dnsrec); i++) { |
|
ares_status_t status; |
|
const char *name = NULL; |
|
ares_dns_rec_type_t qtype; |
|
ares_dns_class_t qclass; |
|
|
|
status = ares_dns_record_query_get(dnsrec, i, &name, &qtype, &qclass); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Name */ |
|
status = ares__dns_name_write(buf, namelist, ARES_TRUE, name); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Type */ |
|
status = ares__buf_append_be16(buf, (unsigned short)qtype); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Class */ |
|
status = ares__buf_append_be16(buf, (unsigned short)qclass); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
} |
|
|
|
return ARES_SUCCESS; |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_name(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist, |
|
ares_bool_t validate_hostname, |
|
ares_dns_rr_key_t key) |
|
{ |
|
const char *name; |
|
|
|
name = ares_dns_rr_get_str(rr, key); |
|
if (name == NULL) { |
|
return ARES_EFORMERR; |
|
} |
|
|
|
return ares__dns_name_write(buf, namelist, validate_hostname, name); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_str(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares_dns_rr_key_t key) |
|
{ |
|
const char *str; |
|
size_t len; |
|
ares_status_t status; |
|
|
|
str = ares_dns_rr_get_str(rr, key); |
|
if (str == NULL || (len = ares_strlen(str)) > 255) { |
|
return ARES_EFORMERR; |
|
} |
|
|
|
/* Write 1 byte length */ |
|
status = ares__buf_append_byte(buf, (unsigned char)(len & 0xFF)); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
if (len == 0) { |
|
return ARES_SUCCESS; |
|
} |
|
|
|
/* Write string */ |
|
return ares__buf_append(buf, (const unsigned char *)str, len); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_binstrs(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares_dns_rr_key_t key) |
|
{ |
|
const unsigned char *bin; |
|
const unsigned char *ptr; |
|
size_t bin_len; |
|
size_t ptr_len; |
|
ares_status_t status; |
|
|
|
bin = ares_dns_rr_get_bin(rr, key, &bin_len); |
|
if (bin == NULL) { |
|
return ARES_EFORMERR; |
|
} |
|
/* split into possible multiple 255-byte or less length strings */ |
|
ptr = bin; |
|
ptr_len = bin_len; |
|
do { |
|
size_t len = ptr_len; |
|
if (len > 255) { |
|
len = 255; |
|
} |
|
|
|
/* Length */ |
|
status = ares__buf_append_byte(buf, (unsigned char)(len & 0xFF)); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* String */ |
|
if (len) { |
|
status = ares__buf_append(buf, ptr, len); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
} |
|
|
|
ptr += len; |
|
ptr_len -= len; |
|
} while (ptr_len > 0); |
|
|
|
return ARES_SUCCESS; |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_be32(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares_dns_rr_key_t key) |
|
{ |
|
if (ares_dns_rr_key_datatype(key) != ARES_DATATYPE_U32) { |
|
return ARES_EFORMERR; |
|
} |
|
return ares__buf_append_be32(buf, ares_dns_rr_get_u32(rr, key)); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_be16(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares_dns_rr_key_t key) |
|
{ |
|
if (ares_dns_rr_key_datatype(key) != ARES_DATATYPE_U16) { |
|
return ARES_EFORMERR; |
|
} |
|
return ares__buf_append_be16(buf, ares_dns_rr_get_u16(rr, key)); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_u8(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares_dns_rr_key_t key) |
|
{ |
|
if (ares_dns_rr_key_datatype(key) != ARES_DATATYPE_U8) { |
|
return ARES_EFORMERR; |
|
} |
|
return ares__buf_append_byte(buf, ares_dns_rr_get_u8(rr, key)); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_a(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
const struct in_addr *addr; |
|
(void)namelist; |
|
|
|
addr = ares_dns_rr_get_addr(rr, ARES_RR_A_ADDR); |
|
if (addr == NULL) { |
|
return ARES_EFORMERR; |
|
} |
|
|
|
return ares__buf_append(buf, (const unsigned char *)addr, sizeof(*addr)); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_ns(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
return ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE, |
|
ARES_RR_NS_NSDNAME); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_cname(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
return ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE, |
|
ARES_RR_CNAME_CNAME); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_soa(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
ares_status_t status; |
|
|
|
/* MNAME */ |
|
status = |
|
ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE, ARES_RR_SOA_MNAME); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* RNAME */ |
|
status = |
|
ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE, ARES_RR_SOA_RNAME); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* SERIAL */ |
|
status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SOA_SERIAL); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* REFRESH */ |
|
status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SOA_REFRESH); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* RETRY */ |
|
status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SOA_RETRY); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* EXPIRE */ |
|
status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SOA_EXPIRE); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* MINIMUM */ |
|
return ares_dns_write_rr_be32(buf, rr, ARES_RR_SOA_MINIMUM); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_ptr(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
return ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE, |
|
ARES_RR_PTR_DNAME); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_hinfo(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
ares_status_t status; |
|
|
|
(void)namelist; |
|
|
|
/* CPU */ |
|
status = ares_dns_write_rr_str(buf, rr, ARES_RR_HINFO_CPU); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* OS */ |
|
return ares_dns_write_rr_str(buf, rr, ARES_RR_HINFO_OS); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_mx(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
ares_status_t status; |
|
|
|
/* PREFERENCE */ |
|
status = ares_dns_write_rr_be16(buf, rr, ARES_RR_MX_PREFERENCE); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* EXCHANGE */ |
|
return ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE, |
|
ARES_RR_MX_EXCHANGE); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_txt(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
(void)namelist; |
|
return ares_dns_write_rr_binstrs(buf, rr, ARES_RR_TXT_DATA); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_aaaa(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
const struct ares_in6_addr *addr; |
|
(void)namelist; |
|
|
|
addr = ares_dns_rr_get_addr6(rr, ARES_RR_AAAA_ADDR); |
|
if (addr == NULL) { |
|
return ARES_EFORMERR; |
|
} |
|
|
|
return ares__buf_append(buf, (const unsigned char *)addr, sizeof(*addr)); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_srv(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
ares_status_t status; |
|
|
|
/* PRIORITY */ |
|
status = ares_dns_write_rr_be16(buf, rr, ARES_RR_SRV_PRIORITY); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* WEIGHT */ |
|
status = ares_dns_write_rr_be16(buf, rr, ARES_RR_SRV_WEIGHT); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* PORT */ |
|
status = ares_dns_write_rr_be16(buf, rr, ARES_RR_SRV_PORT); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* TARGET */ |
|
return ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE, |
|
ARES_RR_SRV_TARGET); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_naptr(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
ares_status_t status; |
|
|
|
/* ORDER */ |
|
status = ares_dns_write_rr_be16(buf, rr, ARES_RR_NAPTR_ORDER); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* PREFERENCE */ |
|
status = ares_dns_write_rr_be16(buf, rr, ARES_RR_NAPTR_PREFERENCE); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* FLAGS */ |
|
status = ares_dns_write_rr_str(buf, rr, ARES_RR_NAPTR_FLAGS); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* SERVICES */ |
|
status = ares_dns_write_rr_str(buf, rr, ARES_RR_NAPTR_SERVICES); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* REGEXP */ |
|
status = ares_dns_write_rr_str(buf, rr, ARES_RR_NAPTR_REGEXP); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* REPLACEMENT */ |
|
return ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE, |
|
ARES_RR_NAPTR_REPLACEMENT); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_opt(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
size_t len = ares__buf_len(buf); |
|
ares_status_t status; |
|
unsigned int ttl = 0; |
|
size_t i; |
|
|
|
(void)namelist; |
|
|
|
/* We need to go back and overwrite the class and ttl that were emitted as |
|
* the OPT record overloads them for its own use (yes, very strange!) */ |
|
status = ares__buf_set_length(buf, len - 2 /* RDLENGTH */ |
|
- 4 /* TTL */ |
|
- 2 /* CLASS */); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Class -> UDP Size */ |
|
status = ares_dns_write_rr_be16(buf, rr, ARES_RR_OPT_UDP_SIZE); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* TTL -> rcode (u8) << 24 | version (u8) << 16 | flags (u16) */ |
|
ttl |= (unsigned int)ares_dns_rr_get_u8(rr, ARES_RR_OPT_EXT_RCODE) << 24; |
|
ttl |= (unsigned int)ares_dns_rr_get_u8(rr, ARES_RR_OPT_VERSION) << 16; |
|
ttl |= (unsigned int)ares_dns_rr_get_u16(rr, ARES_RR_OPT_FLAGS); |
|
|
|
status = ares__buf_append_be32(buf, ttl); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Now go back to real end */ |
|
status = ares__buf_set_length(buf, len); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Append Options */ |
|
for (i=0; i<ares_dns_rr_get_opt_cnt(rr, ARES_RR_OPT_OPTIONS); i++) { |
|
unsigned short opt; |
|
size_t val_len; |
|
const unsigned char *val; |
|
|
|
opt = ares_dns_rr_get_opt(rr, ARES_RR_OPT_OPTIONS, i, &val, &val_len); |
|
|
|
/* BE16 option */ |
|
status = ares__buf_append_be16(buf, opt); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* BE16 length */ |
|
status = ares__buf_append_be16(buf, (unsigned short)(val_len & 0xFFFF)); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Value */ |
|
if (val && val_len) { |
|
status = ares__buf_append(buf, val, val_len); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
} |
|
} |
|
|
|
return ARES_SUCCESS; |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_tlsa(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
ares_status_t status; |
|
const unsigned char *data; |
|
size_t len = 0; |
|
|
|
(void)namelist; |
|
|
|
/* CERT_USAGE */ |
|
status = ares_dns_write_rr_u8(buf, rr, ARES_RR_TLSA_CERT_USAGE); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* SELECTOR */ |
|
status = ares_dns_write_rr_u8(buf, rr, ARES_RR_TLSA_SELECTOR); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* MATCH */ |
|
status = ares_dns_write_rr_u8(buf, rr, ARES_RR_TLSA_MATCH); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* DATA -- binary, rest of buffer, required to be non-zero length */ |
|
data = ares_dns_rr_get_bin(rr, ARES_RR_TLSA_DATA, &len); |
|
if (data == NULL || len == 0) { |
|
return ARES_EFORMERR; |
|
} |
|
|
|
return ares__buf_append(buf, data, len); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_uri(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
ares_status_t status; |
|
const char *target; |
|
|
|
(void)namelist; |
|
|
|
/* PRIORITY */ |
|
status = ares_dns_write_rr_be16(buf, rr, ARES_RR_URI_PRIORITY); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* WEIGHT */ |
|
status = ares_dns_write_rr_be16(buf, rr, ARES_RR_URI_WEIGHT); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* TARGET -- not in DNS string format, rest of buffer, required to be |
|
* non-zero length */ |
|
target = ares_dns_rr_get_str(rr, ARES_RR_URI_TARGET); |
|
if (target == NULL || ares_strlen(target) == 0) { |
|
return ARES_EFORMERR; |
|
} |
|
|
|
return ares__buf_append(buf, (const unsigned char *)target, |
|
ares_strlen(target)); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_caa(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
const unsigned char *data = NULL; |
|
size_t data_len = 0; |
|
ares_status_t status; |
|
|
|
(void)namelist; |
|
|
|
/* CRITICAL */ |
|
status = ares_dns_write_rr_u8(buf, rr, ARES_RR_CAA_CRITICAL); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Tag */ |
|
status = ares_dns_write_rr_str(buf, rr, ARES_RR_CAA_TAG); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Value - binary! (remaining buffer */ |
|
data = ares_dns_rr_get_bin(rr, ARES_RR_CAA_VALUE, &data_len); |
|
if (data == NULL || data_len == 0) { |
|
return ARES_EFORMERR; |
|
} |
|
|
|
return ares__buf_append(buf, data, data_len); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr_raw_rr(ares__buf_t *buf, |
|
const ares_dns_rr_t *rr, |
|
ares__llist_t **namelist) |
|
{ |
|
size_t len = ares__buf_len(buf); |
|
ares_status_t status; |
|
const unsigned char *data = NULL; |
|
size_t data_len = 0; |
|
|
|
(void)namelist; |
|
|
|
/* We need to go back and overwrite the type that was emitted by the parent |
|
* function */ |
|
status = ares__buf_set_length(buf, len - 2 /* RDLENGTH */ |
|
- 4 /* TTL */ |
|
- 2 /* CLASS */ |
|
- 2 /* TYPE */); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
status = ares_dns_write_rr_be16(buf, rr, ARES_RR_RAW_RR_TYPE); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Now go back to real end */ |
|
status = ares__buf_set_length(buf, len); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Output raw data */ |
|
data = ares_dns_rr_get_bin(rr, ARES_RR_RAW_RR_DATA, &data_len); |
|
if (data == NULL) { |
|
return ARES_EFORMERR; |
|
} |
|
|
|
if (data_len == 0) { |
|
return ARES_SUCCESS; |
|
} |
|
|
|
return ares__buf_append(buf, data, data_len); |
|
} |
|
|
|
static ares_status_t ares_dns_write_rr(ares_dns_record_t *dnsrec, |
|
ares__llist_t **namelist, |
|
ares_dns_section_t section, |
|
ares__buf_t *buf) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; i < ares_dns_record_rr_cnt(dnsrec, section); i++) { |
|
const ares_dns_rr_t *rr; |
|
ares_dns_rec_type_t type; |
|
ares_bool_t allow_compress; |
|
ares__llist_t **namelistptr = NULL; |
|
size_t pos_len; |
|
ares_status_t status; |
|
size_t rdlength; |
|
size_t end_length; |
|
|
|
rr = ares_dns_record_rr_get(dnsrec, section, i); |
|
if (rr == NULL) { |
|
return ARES_EFORMERR; |
|
} |
|
|
|
type = ares_dns_rr_get_type(rr); |
|
allow_compress = ares_dns_rec_type_allow_name_compression(type); |
|
if (allow_compress) { |
|
namelistptr = namelist; |
|
} |
|
|
|
/* Name */ |
|
status = |
|
ares__dns_name_write(buf, namelist, ARES_TRUE, ares_dns_rr_get_name(rr)); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Type */ |
|
status = ares__buf_append_be16(buf, (unsigned short)type); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Class */ |
|
status = |
|
ares__buf_append_be16(buf, (unsigned short)ares_dns_rr_get_class(rr)); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* TTL */ |
|
status = ares__buf_append_be32(buf, ares_dns_rr_get_ttl(rr)); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Length */ |
|
pos_len = ares__buf_len(buf); /* Save to write real length later */ |
|
status = ares__buf_append_be16(buf, 0); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Data */ |
|
switch (type) { |
|
case ARES_REC_TYPE_A: |
|
status = ares_dns_write_rr_a(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_NS: |
|
status = ares_dns_write_rr_ns(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_CNAME: |
|
status = ares_dns_write_rr_cname(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_SOA: |
|
status = ares_dns_write_rr_soa(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_PTR: |
|
status = ares_dns_write_rr_ptr(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_HINFO: |
|
status = ares_dns_write_rr_hinfo(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_MX: |
|
status = ares_dns_write_rr_mx(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_TXT: |
|
status = ares_dns_write_rr_txt(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_AAAA: |
|
status = ares_dns_write_rr_aaaa(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_SRV: |
|
status = ares_dns_write_rr_srv(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_NAPTR: |
|
status = ares_dns_write_rr_naptr(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_ANY: |
|
status = ARES_EFORMERR; |
|
break; |
|
case ARES_REC_TYPE_OPT: |
|
status = ares_dns_write_rr_opt(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_TLSA: |
|
status = ares_dns_write_rr_tlsa(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_URI: |
|
status = ares_dns_write_rr_uri(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_CAA: |
|
status = ares_dns_write_rr_caa(buf, rr, namelistptr); |
|
break; |
|
case ARES_REC_TYPE_RAW_RR: |
|
status = ares_dns_write_rr_raw_rr(buf, rr, namelistptr); |
|
break; |
|
} |
|
|
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
/* Back off write pointer, write real length, then go back to proper |
|
* position */ |
|
end_length = ares__buf_len(buf); |
|
rdlength = end_length - pos_len - 2; |
|
|
|
status = ares__buf_set_length(buf, pos_len); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
status = ares__buf_append_be16(buf, (unsigned short)(rdlength & 0xFFFF)); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
|
|
status = ares__buf_set_length(buf, end_length); |
|
if (status != ARES_SUCCESS) { |
|
return status; |
|
} |
|
} |
|
|
|
return ARES_SUCCESS; |
|
} |
|
|
|
ares_status_t ares_dns_write(ares_dns_record_t *dnsrec, unsigned char **buf, |
|
size_t *buf_len) |
|
{ |
|
ares__buf_t *b = NULL; |
|
ares_status_t status; |
|
ares__llist_t *namelist = NULL; |
|
|
|
if (buf == NULL || buf_len == NULL || dnsrec == NULL) { |
|
return ARES_EFORMERR; |
|
} |
|
|
|
*buf = NULL; |
|
*buf_len = 0; |
|
|
|
b = ares__buf_create(); |
|
if (b == NULL) { |
|
return ARES_ENOMEM; |
|
} |
|
|
|
status = ares_dns_write_header(dnsrec, b); |
|
if (status != ARES_SUCCESS) { |
|
goto done; |
|
} |
|
|
|
status = ares_dns_write_questions(dnsrec, &namelist, b); |
|
if (status != ARES_SUCCESS) { |
|
goto done; |
|
} |
|
|
|
status = ares_dns_write_rr(dnsrec, &namelist, ARES_SECTION_ANSWER, b); |
|
if (status != ARES_SUCCESS) { |
|
goto done; |
|
} |
|
|
|
status = ares_dns_write_rr(dnsrec, &namelist, ARES_SECTION_AUTHORITY, b); |
|
if (status != ARES_SUCCESS) { |
|
goto done; |
|
} |
|
|
|
status = ares_dns_write_rr(dnsrec, &namelist, ARES_SECTION_ADDITIONAL, b); |
|
if (status != ARES_SUCCESS) { |
|
goto done; |
|
} |
|
|
|
done: |
|
ares__llist_destroy(namelist); |
|
|
|
if (status != ARES_SUCCESS) { |
|
ares__buf_destroy(b); |
|
return status; |
|
} |
|
|
|
*buf = ares__buf_finish_bin(b, buf_len); |
|
return status; |
|
}
|
|
|