summaryrefslogtreecommitdiffstats
path: root/src/edns.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/edns.c158
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;
+}