2025-02-09 08:52:44 +01:00
|
|
|
/*
|
2025-02-09 09:05:48 +01:00
|
|
|
* Copyright 2019-2023 OARC, Inc.
|
2025-02-09 08:52:44 +01:00
|
|
|
* 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;
|
|
|
|
}
|