summaryrefslogtreecommitdiff
path: root/src/utils/common/exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils/common/exec.c')
-rw-r--r--src/utils/common/exec.c671
1 files changed, 671 insertions, 0 deletions
diff --git a/src/utils/common/exec.c b/src/utils/common/exec.c
new file mode 100644
index 0000000..0a9c1a9
--- /dev/null
+++ b/src/utils/common/exec.c
@@ -0,0 +1,671 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include "utils/common/exec.h"
+
+#include <stdlib.h> // free
+#include <time.h> // localtime_r
+
+#include "libknot/libknot.h"
+#include "common/lists.h" // list
+#include "common/print.h" // txt_print
+#include "common/errcode.h" // KNOT_EOK
+#include "common/descriptor.h" // KNOT_RRTYPE_
+#include "utils/common/msg.h" // WARN
+#include "utils/common/params.h" // params_t
+#include "utils/common/netio.h" // send_msg
+
+static knot_lookup_table_t rtypes[] = {
+ { KNOT_RRTYPE_A, "has IPv4 address" },
+ { KNOT_RRTYPE_NS, "nameserver is" },
+ { KNOT_RRTYPE_CNAME, "is an alias for" },
+ { KNOT_RRTYPE_SOA, "start of authority is" },
+ { KNOT_RRTYPE_PTR, "points to" },
+ { KNOT_RRTYPE_MX, "mail is handled by" },
+ { KNOT_RRTYPE_TXT, "description is" },
+ { KNOT_RRTYPE_AAAA, "has IPv6 address" },
+ { KNOT_RRTYPE_LOC, "location is" },
+ { KNOT_RRTYPE_DS, "delegation signature is" },
+ { KNOT_RRTYPE_SSHFP, "SSH fingerprint is" },
+ { KNOT_RRTYPE_RRSIG, "RR set signature is" },
+ { KNOT_RRTYPE_DNSKEY, "DNSSEC key is" },
+ { KNOT_RRTYPE_TLSA, "has TLS certificate" },
+ { 0, NULL }
+};
+
+static void print_header(const knot_packet_t *packet, const style_t *style)
+{
+ char flags[64] = "";
+ uint8_t rcode_id, opcode_id;
+ const char *rcode_str = "NULL";
+ const char *opcode_str = "NULL";
+ knot_lookup_table_t *rcode, *opcode;
+
+ // Get codes.
+ rcode_id = knot_wire_get_rcode(packet->wireformat);
+ rcode = knot_lookup_by_id(knot_rcode_names, rcode_id);
+ if (rcode != NULL) {
+ rcode_str = rcode->name;
+ }
+
+ opcode_id = knot_wire_get_opcode(packet->wireformat);
+ opcode = knot_lookup_by_id(knot_opcode_names, opcode_id);
+ if (opcode != NULL) {
+ opcode_str = opcode->name;
+ }
+
+ // Get flags.
+ if (knot_wire_get_qr(packet->wireformat) != 0) {
+ strcat(flags, " qr");
+ }
+ if (knot_wire_get_aa(packet->wireformat) != 0) {
+ strcat(flags, " aa");
+ }
+ if (knot_wire_get_tc(packet->wireformat) != 0) {
+ strcat(flags, " tc");
+ }
+ if (knot_wire_get_rd(packet->wireformat) != 0) {
+ strcat(flags, " rd");
+ }
+ if (knot_wire_get_ra(packet->wireformat) != 0) {
+ strcat(flags, " ra");
+ }
+ if (knot_wire_get_z(packet->wireformat) != 0) {
+ strcat(flags, " z");
+ }
+ if (knot_wire_get_ad(packet->wireformat) != 0) {
+ strcat(flags, " ad");
+ }
+ if (knot_wire_get_cd(packet->wireformat) != 0) {
+ strcat(flags, " cd");
+ }
+
+ // Print formated info.
+ switch (style->format) {
+ case FORMAT_NSUPDATE:
+ printf("\n;; ->>HEADER<<- opcode: %s; status: %s; id: %u\n"
+ ";; Flags:%1s; "
+ "ZONE: %u; PREREQ: %u; UPDATE: %u; ADDITIONAL: %u\n",
+ opcode_str, rcode_str, knot_packet_id(packet),
+ flags, packet->header.qdcount, packet->header.ancount,
+ packet->header.nscount, packet->header.arcount);
+
+ break;
+ default:
+ printf("\n;; ->>HEADER<<- opcode: %s; status: %s; id: %u\n"
+ ";; Flags:%1s; "
+ "QUERY: %u; ANSWER: %u; AUTHORITY: %u; ADDITIONAL: %u\n",
+ opcode_str, rcode_str, knot_packet_id(packet),
+ flags, packet->header.qdcount, packet->header.ancount,
+ packet->header.nscount, packet->header.arcount);
+ break;
+ }
+}
+
+static void print_footer(const size_t total_len,
+ const size_t msg_count,
+ const size_t rr_count,
+ const net_t *net,
+ const float elapsed,
+ const bool incoming)
+{
+ struct tm tm;
+ char date[64];
+
+ // Get current timestamp.
+ time_t now = time(NULL);
+ localtime_r(&now, &tm);
+
+ // Create formated date-time string.
+ strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S %Z", &tm);
+
+ // Print messages statistics.
+ if (incoming) {
+ printf("\n;; Received %zu B", total_len);
+ } else {
+ printf("\n;; Sent %zu B", total_len);
+ }
+
+ // If multimessage (XFR) print additional statistics.
+ if (msg_count > 0) {
+ printf(" (%zu messages, %zu records)\n", msg_count, rr_count);
+ } else {
+ printf("\n");
+ }
+ // Print date.
+ printf(";; Time %s\n", date);
+
+ // Print connection statistics.
+ if (net != NULL) {
+ if (incoming) {
+ printf(";; From %s", net->remote_str);
+ } else {
+ printf(";; To %s", net->remote_str);
+ }
+
+ if (elapsed >= 0) {
+ printf(" in %.1f ms\n", elapsed);
+
+ } else {
+ printf("\n");
+
+ }
+ }
+}
+
+static void print_opt_section(const knot_opt_rr_t *rr)
+{
+ printf("Version: %u; flags: %s; UDP size: %u B\n",
+ knot_edns_get_version(rr),
+ (knot_edns_do(rr) != 0) ? "do" : "",
+ knot_edns_get_payload(rr));
+
+ for (int i = 0; i < rr->option_count; i++) {
+ knot_opt_option_t *opt = &(rr->options[i]);
+
+ if (opt->code == EDNS_OPTION_NSID) {
+ printf(";; NSID: ");
+ short_hex_print(opt->data, opt->length);
+ printf(";; : ");
+ txt_print(opt->data, opt->length);
+ } else {
+ printf(";; Option (%u): ", opt->code);
+ short_hex_print(opt->data, opt->length);
+ }
+ }
+}
+
+static void print_section_question(const knot_dname_t *owner,
+ const uint16_t qclass,
+ const uint16_t qtype,
+ const style_t *style)
+{
+ size_t buflen = 8192;
+ char *buf = calloc(buflen, 1);
+
+ knot_rrset_t *question = knot_rrset_new((knot_dname_t *)owner, qtype,
+ qclass, 0);
+
+ if (knot_rrset_txt_dump_header(question, 0, buf, buflen,
+ &(style->style)) < 0) {
+ WARN("can't print whole question section\n");
+ }
+
+ printf("%s\n", buf);
+
+ knot_rrset_free(&question);
+ free(buf);
+}
+
+static void print_section_full(const knot_rrset_t **rrsets,
+ const uint16_t count,
+ const style_t *style)
+{
+ size_t buflen = 8192;
+ char *buf = calloc(buflen, 1);
+
+ for (size_t i = 0; i < count; i++) {
+ if (rrsets[i]->type == KNOT_RRTYPE_OPT) {
+ continue;
+ }
+
+ while (knot_rrset_txt_dump(rrsets[i], buf, buflen,
+ &(style->style)) < 0)
+ {
+ buflen += 4096;
+ buf = realloc(buf, buflen);
+
+ // Oversize protection.
+ if (buflen > 1000000) {
+ WARN("can't print whole section\n");
+ break;
+ }
+ }
+ printf("%s", buf);
+ }
+
+ free(buf);
+}
+
+static void print_section_dig(const knot_rrset_t **rrsets,
+ const uint16_t count,
+ const style_t *style)
+{
+ size_t buflen = 8192;
+ char *buf = calloc(buflen, 1);
+
+ for (size_t i = 0; i < count; i++) {
+ const knot_rrset_t *rrset = rrsets[i];
+
+ for (size_t j = 0; j < rrset->rdata_count; j++) {
+ while (knot_rrset_txt_dump_data(rrset, j, buf, buflen,
+ &(style->style)) < 0) {
+ buflen += 4096;
+ buf = realloc(buf, buflen);
+
+ // Oversize protection.
+ if (buflen > 1000000) {
+ WARN("can't print whole section\n");
+ break;
+ }
+ }
+ printf("%s\n", buf);
+ }
+ }
+
+ free(buf);
+}
+
+static void print_section_host(const knot_rrset_t **rrsets,
+ const uint16_t count,
+ const style_t *style)
+{
+ size_t buflen = 8192;
+ char *buf = calloc(buflen, 1);
+
+ for (size_t i = 0; i < count; i++) {
+ const knot_rrset_t *rrset = rrsets[i];
+ knot_lookup_table_t *descr;
+ char type[32] = "NULL";
+ char *owner;
+
+ owner = knot_dname_to_str(rrset->owner);
+ descr = knot_lookup_by_id(rtypes, rrset->type);
+
+ for (size_t j = 0; j < rrset->rdata_count; j++) {
+ while (knot_rrset_txt_dump_data(rrset, j, buf, buflen,
+ &(style->style)) < 0) {
+ buflen += 4096;
+ buf = realloc(buf, buflen);
+
+ // Oversize protection.
+ if (buflen > 1000000) {
+ WARN("can't print whole RR set\n");
+ break;
+ }
+ }
+
+ if (descr != NULL) {
+ printf("%s %s %s\n", owner, descr->name, buf);
+ } else {
+ knot_rrtype_to_string(rrset->type, type,
+ sizeof(type));
+ printf("%s has %s record %s\n",
+ owner, type, buf);
+ }
+ }
+
+ free(owner);
+ }
+
+ free(buf);
+}
+
+static void print_error_host(const uint8_t code,
+ const knot_question_t *question)
+{
+ const char *rcode_str = "NULL";
+ char type[32] = "NULL";
+ char *owner;
+
+ knot_lookup_table_t *rcode;
+
+ owner = knot_dname_to_str(question->qname);
+ rcode = knot_lookup_by_id(knot_rcode_names, code);
+ if (rcode != NULL) {
+ rcode_str = rcode->name;
+ }
+ knot_rrtype_to_string(question->qtype, type, sizeof(type));
+
+ if (code == KNOT_RCODE_NOERROR) {
+ printf("Host %s has no %s record\n", owner, type);
+ } else {
+ printf("Host %s type %s error: %s\n", owner, type, rcode_str);
+ }
+
+ free(owner);
+}
+
+knot_packet_t* create_empty_packet(const knot_packet_prealloc_type_t type,
+ const size_t max_size)
+{
+ // Create packet skeleton.
+ knot_packet_t *packet = knot_packet_new(type);
+ if (packet == NULL) {
+ DBG_NULL;
+ return NULL;
+ }
+
+ // Set packet buffer size.
+ knot_packet_set_max_size(packet, max_size);
+
+ // Set random sequence id.
+ knot_packet_set_random_id(packet);
+
+ // Initialize query packet.
+ knot_query_init(packet);
+
+ return packet;
+}
+
+void print_header_xfr(const knot_question_t *question, const style_t *style)
+{
+ if (style == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ char xfr[16] = "AXFR";
+
+ switch (question->qtype) {
+ case KNOT_RRTYPE_AXFR:
+ break;
+ case KNOT_RRTYPE_IXFR:
+ xfr[0] = 'I';
+ break;
+ default:
+ return;
+ }
+
+ if (style->show_header) {
+ char *owner = knot_dname_to_str(question->qname);
+ if (owner != NULL) {
+ printf("\n;; %s for %s\n", xfr, owner);
+ free(owner);
+ }
+ }
+}
+
+void print_data_xfr(const knot_packet_t *packet,
+ const style_t *style)
+{
+ if (packet == NULL || style == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ switch (style->format) {
+ case FORMAT_DIG:
+ print_section_dig(packet->answer, packet->header.ancount,style);
+ break;
+ case FORMAT_HOST:
+ print_section_host(packet->answer, packet->header.ancount, style);
+ break;
+ case FORMAT_FULL:
+ print_section_full(packet->answer, packet->header.ancount, style);
+
+ // Print TSIG record if any.
+ if (style->show_additional) {
+ print_section_full(packet->additional,
+ packet->header.arcount,
+ style);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void print_footer_xfr(const size_t total_len,
+ const size_t msg_count,
+ const size_t rr_count,
+ const net_t *net,
+ const float elapsed,
+ const style_t *style)
+{
+ if (style == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ if (style->show_footer) {
+ print_footer(total_len, msg_count, rr_count, net, elapsed, true);
+ }
+}
+
+void print_packet(const knot_packet_t *packet,
+ const size_t total_len,
+ const net_t *net,
+ const float elapsed,
+ const bool incoming,
+ const style_t *style)
+{
+ if (packet == NULL || style == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ uint16_t additionals = packet->header.arcount;
+
+ // Print packet information header.
+ if (style->show_header) {
+ print_header(packet, style);
+ }
+
+ // Print EDNS section.
+ if (knot_edns_get_version(&packet->opt_rr) != EDNS_NOT_SUPPORTED) {
+ if (style->show_edns) {
+ printf("\n;; EDNS PSEUDOSECTION:\n;; ");
+ print_opt_section(&packet->opt_rr);
+ }
+
+ additionals--;
+ }
+
+ // Print DNS sections.
+ switch (style->format) {
+ case FORMAT_DIG:
+ if (packet->header.ancount > 0) {
+ print_section_dig(packet->answer, packet->header.ancount,
+ style);
+ }
+ break;
+ case FORMAT_HOST:
+ if (packet->header.ancount > 0) {
+ print_section_host(packet->answer, packet->header.ancount,
+ style);
+ } else {
+ uint8_t rcode = knot_wire_get_rcode(packet->wireformat);
+ print_error_host(rcode, &packet->question);
+ }
+ break;
+ case FORMAT_NSUPDATE:
+ if (style->show_question && packet->header.qdcount > 0) {
+ printf("\n;; ZONE SECTION:\n;; ");
+ print_section_question(packet->question.qname,
+ packet->question.qclass,
+ packet->question.qtype,
+ style);
+ }
+
+ if (style->show_answer && packet->header.ancount > 0) {
+ printf("\n;; PREREQUISITE SECTION:\n");
+ print_section_full(packet->answer,
+ packet->header.ancount,
+ style);
+ }
+
+ if (style->show_authority && packet->header.nscount > 0) {
+ printf("\n;; UPDATE SECTION:\n");
+ print_section_full(packet->authority,
+ packet->header.nscount,
+ style);
+ }
+
+ if (style->show_additional && additionals > 0) {
+ printf("\n;; ADDITIONAL DATA:\n");
+ print_section_full(packet->additional,
+ packet->header.arcount,
+ style);
+ }
+ break;
+ case FORMAT_FULL:
+ if (style->show_question && packet->header.qdcount > 0) {
+ printf("\n;; QUESTION SECTION:\n;; ");
+ print_section_question(packet->question.qname,
+ packet->question.qclass,
+ packet->question.qtype,
+ style);
+ }
+
+ if (style->show_answer && packet->header.ancount > 0) {
+ printf("\n;; ANSWER SECTION:\n");
+ print_section_full(packet->answer,
+ packet->header.ancount,
+ style);
+ }
+
+ if (style->show_authority && packet->header.nscount > 0) {
+ printf("\n;; AUTHORITY SECTION:\n");
+ print_section_full(packet->authority,
+ packet->header.nscount,
+ style);
+ }
+
+ if (style->show_additional && additionals > 0) {
+ printf("\n;; ADDITIONAL SECTION:\n");
+ print_section_full(packet->additional,
+ packet->header.arcount,
+ style);
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Print packet statistics.
+ if (style->show_footer) {
+ print_footer(total_len, 0, 0, net, elapsed, incoming);
+ }
+}
+
+void free_sign_context(sign_context_t *ctx)
+{
+ if (ctx == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ if (ctx->tsig_key.name) {
+ knot_tsig_key_free(&ctx->tsig_key);
+ }
+
+ if (ctx->dnssec_key.name) {
+ knot_dnssec_key_free(&ctx->dnssec_key);
+ }
+
+ free(ctx->digest);
+
+ memset(ctx, '\0', sizeof(sign_context_t));
+}
+
+int sign_packet(knot_packet_t *pkt,
+ sign_context_t *sign_ctx,
+ const knot_key_params_t *key_params)
+{
+ int result;
+
+ if (pkt == NULL || sign_ctx == NULL || key_params == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ uint8_t *wire = pkt->wireformat;
+ size_t *wire_size = &pkt->size;
+ size_t max_size = knot_packet_max_size(pkt);
+
+ switch (knot_get_key_type(key_params)) {
+ case KNOT_KEY_TSIG:
+ {
+ result = knot_tsig_key_from_params(key_params,
+ &sign_ctx->tsig_key);
+ if (result != KNOT_EOK) {
+ return result;
+ }
+
+ knot_tsig_key_t *key = &sign_ctx->tsig_key;
+
+ sign_ctx->digest_size = knot_tsig_digest_length(key->algorithm);
+ sign_ctx->digest = malloc(sign_ctx->digest_size);
+
+ size_t tsig_size = tsig_wire_maxsize(key);
+ knot_packet_set_tsig_size(pkt, tsig_size);
+
+ result = knot_tsig_sign(wire, wire_size, max_size, NULL, 0,
+ sign_ctx->digest, &sign_ctx->digest_size,
+ key, 0, 0);
+
+ return result;
+ }
+ case KNOT_KEY_DNSSEC:
+ {
+ result = knot_dnssec_key_from_params(key_params,
+ &sign_ctx->dnssec_key);
+ if (result != KNOT_EOK) {
+ return result;
+ }
+
+ knot_dnssec_key_t *key = &sign_ctx->dnssec_key;
+ result = knot_sig0_sign(wire, wire_size, max_size, key);
+
+ return result;
+ }
+ default:
+ return KNOT_DNSSEC_EINVALID_KEY;
+ }
+}
+
+int verify_packet(const knot_packet_t *pkt,
+ const sign_context_t *sign_ctx,
+ const knot_key_params_t *key_params)
+{
+ int result;
+
+ if (pkt == NULL || sign_ctx == NULL || key_params == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ const uint8_t *wire = pkt->wireformat;
+ const size_t *wire_size = &pkt->size;
+
+ switch (knot_get_key_type(key_params)) {
+ case KNOT_KEY_TSIG:
+ {
+ const knot_rrset_t *tsig_rr = knot_packet_tsig(pkt);
+ if (tsig_rr == NULL) {
+ return KNOT_ENOTSIG;
+ }
+
+ result = knot_tsig_client_check(tsig_rr, wire, *wire_size,
+ sign_ctx->digest,
+ sign_ctx->digest_size,
+ &sign_ctx->tsig_key, 0);
+
+ return result;
+ }
+ case KNOT_KEY_DNSSEC:
+ {
+ // Uses public key cryptography, server cannot sign the
+ // response, because the private key should be known only
+ // to the client.
+ return KNOT_EOK;
+ }
+ default:
+ return KNOT_EINVAL;
+ }
+}