diff options
Diffstat (limited to 'src/edns.c')
-rw-r--r-- | src/edns.c | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/src/edns.c b/src/edns.c new file mode 100644 index 0000000..9c04f9b --- /dev/null +++ b/src/edns.c @@ -0,0 +1,158 @@ +/* + * 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 "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; +} |