1
0
Fork 0
dnsperf/src/edns.c

159 lines
4.3 KiB
C
Raw Normal View History

/*
* Copyright 2019-2023 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 "edns.h"
#include "log.h"
#include "opt.h"
#include <stdint.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#define EDNSLEN 11
perf_ednsoption_t* perf_edns_parseoption(const char* arg)
{
char * copy, *sep, *value, *endptr, hex[3];
perf_ednsoption_t* option;
size_t data_len;
unsigned long int u;
perf_buffer_t save;
copy = strdup(arg);
if (!copy) {
perf_log_fatal("out of memory");
return 0; // fix clang scan-build
}
sep = strchr(copy, ':');
if (!sep) {
perf_log_warning("invalid EDNS Option, must be code:value");
perf_opt_usage();
exit(1);
}
*sep = '\0';
value = sep + 1;
data_len = strlen(value);
if (!data_len) {
perf_log_warning("invalid EDNS Option, value is empty");
perf_opt_usage();
exit(1);
}
if (data_len & 1) {
perf_log_warning("invalid EDNS Option, value must hex string (even number of characters)");
perf_opt_usage();
exit(1);
}
data_len /= 2;
data_len += 4; // code, len, data...
option = calloc(1, sizeof(perf_ednsoption_t) + data_len);
if (!option) {
perf_log_fatal("out of memory");
free(copy); // fix clang scan-build
return 0; // fix clang scan-build
}
perf_buffer_init(&option->buffer, &option->data[0], data_len);
endptr = 0;
u = strtoul(copy, &endptr, 10);
if (*endptr || u == ULONG_MAX) {
perf_log_warning("invalid EDNS Option code '%s'", copy);
perf_opt_usage();
exit(1);
}
perf_buffer_putuint16(&option->buffer, u & 0xffff);
save = option->buffer;
perf_buffer_add(&option->buffer, 2);
hex[2] = 0;
while (*value) {
memcpy(hex, value, 2);
endptr = 0;
u = strtoul(hex, &endptr, 16);
if (*endptr || u == ULONG_MAX) {
perf_log_warning("invalid EDNS Option hex value '%.*s'", 2, value);
perf_opt_usage();
exit(1);
}
perf_buffer_putuint8(&option->buffer, u & 0xff);
value += 2;
}
perf_buffer_putuint16(&save, perf_buffer_usedlength(&option->buffer) - 4);
free(copy);
return option;
}
void perf_edns_destroyoption(perf_ednsoption_t** optionp)
{
assert(optionp);
assert(*optionp);
free(*optionp);
*optionp = 0;
}
/*
* Appends an OPT record to the packet.
*/
perf_result_t perf_add_edns(perf_buffer_t* packet, bool dnssec, perf_ednsoption_t* option)
{
unsigned char* base;
size_t option_length = 0, total_length;
if (option) {
option_length = perf_buffer_usedlength(&option->buffer);
}
total_length = EDNSLEN + option_length;
if (perf_buffer_availablelength(packet) < total_length) {
perf_log_warning("failed to add OPT to query packet");
return PERF_R_NOSPACE;
}
base = perf_buffer_base(packet);
perf_buffer_putuint8(packet, 0); /* root name */
perf_buffer_putuint16(packet, 41); /* OPT record */
perf_buffer_putuint16(packet, MAX_EDNS_PACKET); /* class */
perf_buffer_putuint8(packet, 0); /* xrcode */
perf_buffer_putuint8(packet, 0); /* version */
if (dnssec) {
/* flags */
perf_buffer_putuint16(packet, 0x8000);
} else {
perf_buffer_putuint16(packet, 0);
}
perf_buffer_putuint16(packet, option_length); /* rdlen */
if (option) {
perf_buffer_putmem(packet, perf_buffer_base(&option->buffer), option_length);
}
base[11]++; /* increment additional record count */
return PERF_R_SUCCESS;
}