Adding upstream version 2.4.2+debian.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
0fae05cfb7
commit
153471ed4b
64 changed files with 9668 additions and 0 deletions
484
src/dns.c
Normal file
484
src/dns.c
Normal file
|
@ -0,0 +1,484 @@
|
|||
/*
|
||||
* Copyright 2019-2021 OARC, Inc.
|
||||
* Copyright 2017-2018 Akamai Technologies
|
||||
* Copyright 2006-2016 Nominum, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "dns.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "opt.h"
|
||||
#include "qtype.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef HAVE_LDNS
|
||||
#include <ldns/ldns.h>
|
||||
#endif
|
||||
|
||||
#define WHITESPACE " \t\n"
|
||||
|
||||
#define MAX_RDATA_LENGTH 65535
|
||||
#define EDNSLEN 11
|
||||
|
||||
const char* perf_dns_rcode_strings[] = {
|
||||
"NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN",
|
||||
"NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET",
|
||||
"NXRRSET", "NOTAUTH", "NOTZONE", "rcode11",
|
||||
"rcode12", "rcode13", "rcode14", "rcode15"
|
||||
};
|
||||
|
||||
perf_result_t perf_dname_fromstring(const char* str, size_t len, perf_buffer_t* target)
|
||||
{
|
||||
size_t label_len;
|
||||
const char* orig_str = str;
|
||||
|
||||
if (perf_buffer_availablelength(target) < len) {
|
||||
return PERF_R_NOSPACE;
|
||||
}
|
||||
|
||||
while (len) {
|
||||
for (label_len = 0; label_len < len; label_len++) {
|
||||
if (*(str + label_len) == '.') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!label_len) {
|
||||
// Just a dot
|
||||
if (len > 1) {
|
||||
// a dot but with labels after it
|
||||
return PERF_R_FAILURE;
|
||||
} else if (str != orig_str) {
|
||||
// a dot but with labels before it
|
||||
return PERF_R_FAILURE;
|
||||
}
|
||||
perf_buffer_putuint8(target, 0);
|
||||
break;
|
||||
}
|
||||
if (label_len > 63) {
|
||||
return PERF_R_FAILURE;
|
||||
}
|
||||
perf_buffer_putuint8(target, label_len);
|
||||
perf_buffer_putmem(target, str, label_len);
|
||||
str += label_len;
|
||||
len -= label_len;
|
||||
if (len < 2) {
|
||||
// Last label/dot
|
||||
perf_buffer_putuint8(target, 0);
|
||||
break;
|
||||
}
|
||||
// advance past dot
|
||||
str++;
|
||||
len--;
|
||||
}
|
||||
|
||||
return PERF_R_SUCCESS;
|
||||
}
|
||||
|
||||
perf_result_t perf_qtype_fromstring(const char* str, size_t len, perf_buffer_t* target)
|
||||
{
|
||||
const perf_qtype_t* q = qtype_table;
|
||||
|
||||
while (q->type) {
|
||||
if (!strncasecmp(q->type, str, len)) {
|
||||
perf_buffer_putuint16(target, q->value);
|
||||
return PERF_R_SUCCESS;
|
||||
}
|
||||
q++;
|
||||
}
|
||||
|
||||
return PERF_R_FAILURE;
|
||||
}
|
||||
|
||||
static perf_result_t build_query(const perf_region_t* line, perf_buffer_t* msg)
|
||||
{
|
||||
char * domain_str, *qtype_str;
|
||||
size_t domain_len, qtype_len;
|
||||
perf_result_t result;
|
||||
|
||||
domain_str = line->base;
|
||||
domain_len = strcspn(line->base, WHITESPACE);
|
||||
|
||||
if (!domain_len) {
|
||||
perf_log_warning("invalid query input format: %s", (char*)line->base);
|
||||
return PERF_R_FAILURE;
|
||||
}
|
||||
|
||||
qtype_str = line->base + domain_len;
|
||||
while (isspace(*qtype_str))
|
||||
qtype_str++;
|
||||
qtype_len = strcspn(qtype_str, WHITESPACE);
|
||||
|
||||
/* Create the question section */
|
||||
result = perf_dname_fromstring(domain_str, domain_len, msg);
|
||||
if (result != PERF_R_SUCCESS) {
|
||||
perf_log_warning("invalid domain name (or out of space): %.*s", (int)domain_len, domain_str);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!qtype_len) {
|
||||
perf_log_warning("invalid query input format: %s", (char*)line->base);
|
||||
return PERF_R_FAILURE;
|
||||
}
|
||||
|
||||
result = perf_qtype_fromstring(qtype_str, qtype_len, msg);
|
||||
if (result != PERF_R_SUCCESS) {
|
||||
perf_log_warning("invalid qtype: %.*s", (int)qtype_len, qtype_str);
|
||||
return result;
|
||||
}
|
||||
|
||||
perf_buffer_putuint16(msg, 1); // class IN
|
||||
|
||||
return PERF_R_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LDNS
|
||||
static bool token_equals(const perf_region_t* token, const char* str)
|
||||
{
|
||||
return (strlen(str) == token->length && strncasecmp(str, token->base, token->length) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads one line containing an individual update for a dynamic update message.
|
||||
*/
|
||||
static perf_result_t
|
||||
read_update_line(char* str, const ldns_rdf* origin,
|
||||
bool want_ttl, bool need_type, bool want_rdata, bool need_rdata,
|
||||
ldns_rr** rr, const char** errstr)
|
||||
{
|
||||
char tmp[256], *str2;
|
||||
size_t len;
|
||||
|
||||
while (isspace(*str & 0xff))
|
||||
str++;
|
||||
str2 = str;
|
||||
|
||||
/*
|
||||
* Read the owner name
|
||||
*/
|
||||
len = strcspn(str, WHITESPACE);
|
||||
if (len > sizeof(tmp) - 1) {
|
||||
*errstr = "domain name too large";
|
||||
return PERF_R_NOSPACE;
|
||||
}
|
||||
memcpy(tmp, str, len);
|
||||
tmp[len] = 0;
|
||||
|
||||
ldns_rdf* owner;
|
||||
if (!(owner = ldns_dname_new_frm_str(tmp))) {
|
||||
*errstr = "invalid name or out of memory";
|
||||
return PERF_R_FAILURE;
|
||||
}
|
||||
ldns_rr_set_owner(*rr, owner);
|
||||
if (!ldns_dname_str_absolute(tmp) && origin) {
|
||||
if (ldns_dname_cat(ldns_rr_owner(*rr), origin) != LDNS_STATUS_OK) {
|
||||
return PERF_R_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
str += len;
|
||||
while (isspace(*str & 0xff))
|
||||
str++;
|
||||
|
||||
/*
|
||||
* Read the ttl
|
||||
*/
|
||||
if (want_ttl) {
|
||||
len = strcspn(str, WHITESPACE);
|
||||
if (len > sizeof(tmp) - 1) {
|
||||
*errstr = "TTL string too large";
|
||||
return PERF_R_NOSPACE;
|
||||
}
|
||||
memcpy(tmp, str, len);
|
||||
tmp[len] = 0;
|
||||
|
||||
char* endptr = 0;
|
||||
unsigned long int u = strtoul(tmp, &endptr, 10);
|
||||
if (*endptr || u == ULONG_MAX) {
|
||||
*errstr = "TTL invalid";
|
||||
return PERF_R_INVALIDUPDATE;
|
||||
}
|
||||
|
||||
ldns_rr_set_ttl(*rr, u);
|
||||
|
||||
str += len;
|
||||
while (isspace(*str & 0xff))
|
||||
str++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the type
|
||||
*/
|
||||
len = strcspn(str, WHITESPACE);
|
||||
if (!len) {
|
||||
if (!need_type)
|
||||
return PERF_R_SUCCESS;
|
||||
|
||||
*errstr = "TYPE required";
|
||||
return PERF_R_INVALIDUPDATE;
|
||||
}
|
||||
if (len > sizeof(tmp) - 1) {
|
||||
*errstr = "TYPE string too large";
|
||||
return PERF_R_NOSPACE;
|
||||
}
|
||||
memcpy(tmp, str, len);
|
||||
tmp[len] = 0;
|
||||
|
||||
ldns_rr_type type = ldns_get_rr_type_by_name(tmp);
|
||||
if (!type) {
|
||||
*errstr = "TYPE invalid";
|
||||
return PERF_R_INVALIDUPDATE;
|
||||
}
|
||||
ldns_rr_set_type(*rr, type);
|
||||
|
||||
str += len;
|
||||
while (isspace(*str & 0xff))
|
||||
str++;
|
||||
|
||||
if (!want_rdata)
|
||||
return PERF_R_SUCCESS;
|
||||
|
||||
/*
|
||||
* Read the rdata
|
||||
*/
|
||||
if (*str == 0) {
|
||||
if (!need_rdata)
|
||||
return PERF_R_SUCCESS;
|
||||
|
||||
*errstr = "RDATA required";
|
||||
return PERF_R_INVALIDUPDATE;
|
||||
}
|
||||
|
||||
// Need to recreate ldns_rr because there is no new_frm_str function to
|
||||
// correctly parse RDATA (quotes etc) for a RDF
|
||||
ldns_rr* rr2 = 0;
|
||||
if (ldns_rr_new_frm_str(&rr2, str2, 0, origin, 0) != LDNS_STATUS_OK) {
|
||||
*errstr = "invalid RDATA or out of memory";
|
||||
return PERF_R_INVALIDUPDATE;
|
||||
}
|
||||
|
||||
// Force set TTL since if its missing in the input it will get the default
|
||||
// 3600 and not 0 as it should
|
||||
ldns_rr_set_ttl(rr2, ldns_rr_ttl(*rr));
|
||||
|
||||
ldns_rr_free(*rr);
|
||||
*rr = rr2;
|
||||
|
||||
return PERF_R_SUCCESS;
|
||||
}
|
||||
|
||||
static void compression_free(ldns_rbnode_t* node, void* arg)
|
||||
{
|
||||
(void)arg;
|
||||
ldns_rdf_deep_free((ldns_rdf*)node->key);
|
||||
LDNS_FREE(node);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a complete dynamic update message and sends it.
|
||||
*/
|
||||
static perf_result_t build_update(const perf_region_t* record, perf_buffer_t* msg)
|
||||
{
|
||||
perf_region_t input, token;
|
||||
char * msgbase, *str;
|
||||
bool is_update;
|
||||
int updates = 0;
|
||||
int prereqs = 0;
|
||||
perf_result_t result = PERF_R_FAILURE;
|
||||
ldns_rdf* origin = 0;
|
||||
ldns_rr* rr = 0;
|
||||
ldns_buffer* lmsg = 0;
|
||||
ldns_rbtree_t compression;
|
||||
const char* errstr;
|
||||
|
||||
input = *record;
|
||||
msgbase = perf_buffer_base(msg);
|
||||
ldns_rbtree_init(&compression, ldns_dname_compare_v);
|
||||
|
||||
// Fill LDNS buffer with current message (DNS headers)
|
||||
if (!(lmsg = ldns_buffer_new(perf_buffer_length(msg)))) {
|
||||
perf_log_fatal("unable to create LDNS buffer for DNS message");
|
||||
goto done; // for scan-build / sonarcloud
|
||||
}
|
||||
ldns_buffer_write(lmsg, perf_buffer_base(msg), perf_buffer_usedlength(msg));
|
||||
|
||||
if (!(origin = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, input.base))) {
|
||||
perf_log_warning("Unable to parse domain name %s", (char*)input.base);
|
||||
goto done;
|
||||
}
|
||||
if (ldns_dname2buffer_wire_compress(lmsg, origin, &compression) != LDNS_STATUS_OK) {
|
||||
perf_log_warning("Unable to write domain name %s to wire format", (char*)input.base);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ldns_buffer_write_u16(lmsg, 6); // SOA
|
||||
ldns_buffer_write_u16(lmsg, 1); // IN
|
||||
|
||||
while (true) {
|
||||
input.base += strlen(input.base) + 1;
|
||||
if (input.base >= record->base + record->length) {
|
||||
perf_log_warning("incomplete update: %s", (char*)record->base);
|
||||
result = PERF_R_FAILURE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
is_update = false;
|
||||
token.base = input.base;
|
||||
token.length = strcspn(token.base, WHITESPACE);
|
||||
str = input.base + token.length;
|
||||
errstr = 0;
|
||||
if (token_equals(&token, "send")) {
|
||||
break;
|
||||
}
|
||||
|
||||
rr = ldns_rr_new();
|
||||
ldns_rr_set_ttl(rr, 0);
|
||||
ldns_rr_set_type(rr, LDNS_RR_TYPE_ANY);
|
||||
ldns_rr_set_class(rr, LDNS_RR_CLASS_IN);
|
||||
|
||||
if (token_equals(&token, "add")) {
|
||||
result = read_update_line(str, origin, true, true, true, true, &rr, &errstr);
|
||||
ldns_rr_set_class(rr, LDNS_RR_CLASS_IN);
|
||||
is_update = true;
|
||||
} else if (token_equals(&token, "delete")) {
|
||||
result = read_update_line(str, origin, false, false, true, false, &rr, &errstr);
|
||||
if (ldns_rr_rd_count(rr)) {
|
||||
ldns_rr_set_class(rr, LDNS_RR_CLASS_NONE);
|
||||
} else {
|
||||
ldns_rr_set_class(rr, LDNS_RR_CLASS_ANY);
|
||||
}
|
||||
is_update = true;
|
||||
} else if (token_equals(&token, "require")) {
|
||||
result = read_update_line(str, origin, false, false, true, false, &rr, &errstr);
|
||||
if (ldns_rr_rd_count(rr)) {
|
||||
ldns_rr_set_class(rr, LDNS_RR_CLASS_IN);
|
||||
} else {
|
||||
ldns_rr_set_class(rr, LDNS_RR_CLASS_ANY);
|
||||
}
|
||||
is_update = false;
|
||||
} else if (token_equals(&token, "prohibit")) {
|
||||
result = read_update_line(str, origin, false, false, false, false, &rr, &errstr);
|
||||
ldns_rr_set_class(rr, LDNS_RR_CLASS_NONE);
|
||||
is_update = false;
|
||||
} else {
|
||||
perf_log_warning("invalid update command: %s", (char*)input.base);
|
||||
result = PERF_R_FAILURE;
|
||||
}
|
||||
|
||||
if (result != PERF_R_SUCCESS) {
|
||||
if (errstr) {
|
||||
perf_log_warning("invalid update command, %s: %s", errstr, (char*)input.base);
|
||||
} else if (result == PERF_R_INVALIDUPDATE) {
|
||||
perf_log_warning("invalid update command: %s", (char*)input.base);
|
||||
} else {
|
||||
perf_log_warning("error processing update command: %s", (char*)input.base);
|
||||
}
|
||||
ldns_rr_free(rr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!is_update && updates > 0) {
|
||||
perf_log_warning("prereqs must precede updates");
|
||||
result = PERF_R_FAILURE;
|
||||
ldns_rr_free(rr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ldns_rr2buffer_wire_compress(lmsg, rr, LDNS_SECTION_ANSWER, &compression) != LDNS_STATUS_OK) {
|
||||
perf_log_warning("Unable to write update message to wire format");
|
||||
ldns_rr_free(rr);
|
||||
goto done;
|
||||
}
|
||||
ldns_rr_free(rr);
|
||||
|
||||
if (is_update)
|
||||
updates++;
|
||||
else
|
||||
prereqs++;
|
||||
}
|
||||
|
||||
if (ldns_buffer_position(lmsg) - perf_buffer_usedlength(msg) > perf_buffer_availablelength(msg)) {
|
||||
perf_log_warning("out of space in message buffer");
|
||||
result = PERF_R_NOSPACE;
|
||||
goto done;
|
||||
}
|
||||
uint8_t* p = ldns_buffer_begin(lmsg) + perf_buffer_usedlength(msg);
|
||||
perf_buffer_putmem(msg, p, ldns_buffer_position(lmsg) - perf_buffer_usedlength(msg));
|
||||
|
||||
msgbase[7] = prereqs; /* ANCOUNT = number of prereqs */
|
||||
msgbase[9] = updates; /* AUCOUNT = number of updates */
|
||||
|
||||
result = PERF_R_SUCCESS;
|
||||
|
||||
done:
|
||||
ldns_buffer_free(lmsg);
|
||||
ldns_rdf_deep_free(origin);
|
||||
ldns_traverse_postorder(&compression, compression_free, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
perf_result_t perf_dns_buildrequest(const perf_region_t* record, uint16_t qid,
|
||||
bool edns, bool dnssec, bool is_update,
|
||||
perf_tsigkey_t* tsigkey, perf_ednsoption_t* edns_option,
|
||||
perf_buffer_t* msg)
|
||||
{
|
||||
unsigned int flags;
|
||||
perf_result_t result;
|
||||
|
||||
if (is_update)
|
||||
flags = 5 << 11; // opcode UPDATE
|
||||
else
|
||||
flags = 0x0100U; // flag RD
|
||||
|
||||
/* Create the DNS packet header */
|
||||
perf_buffer_putuint16(msg, qid);
|
||||
perf_buffer_putuint16(msg, flags); /* flags */
|
||||
perf_buffer_putuint16(msg, 1); /* qdcount */
|
||||
perf_buffer_putuint16(msg, 0); /* ancount */
|
||||
perf_buffer_putuint16(msg, 0); /* aucount */
|
||||
perf_buffer_putuint16(msg, 0); /* arcount */
|
||||
|
||||
if (is_update) {
|
||||
#ifdef HAVE_LDNS
|
||||
result = build_update(record, msg);
|
||||
#else
|
||||
result = PERF_R_FAILURE;
|
||||
#endif
|
||||
} else {
|
||||
result = build_query(record, msg);
|
||||
}
|
||||
|
||||
if (result == PERF_R_SUCCESS && edns) {
|
||||
result = perf_add_edns(msg, dnssec, edns_option);
|
||||
}
|
||||
|
||||
if (result == PERF_R_SUCCESS && tsigkey) {
|
||||
result = perf_add_tsig(msg, tsigkey);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue