summaryrefslogtreecommitdiff
path: root/src/utils/dig
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils/dig')
-rw-r--r--src/utils/dig/dig_exec.c732
-rw-r--r--src/utils/dig/dig_exec.h37
-rw-r--r--src/utils/dig/dig_main.c39
-rw-r--r--src/utils/dig/dig_params.c976
-rw-r--r--src/utils/dig/dig_params.h133
5 files changed, 1917 insertions, 0 deletions
diff --git a/src/utils/dig/dig_exec.c b/src/utils/dig/dig_exec.c
new file mode 100644
index 0000000..8a13e96
--- /dev/null
+++ b/src/utils/dig/dig_exec.c
@@ -0,0 +1,732 @@
+/* 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/dig/dig_exec.h"
+
+#include <stdlib.h> // free
+#include <sys/time.h> // gettimeofday
+#include <sys/socket.h> // AF_INET
+#include <netinet/in.h> // sockaddr_in (BSD)
+
+#include "libknot/libknot.h"
+#include "common/lists.h" // list
+#include "common/print.h" // time_diff
+#include "common/errcode.h" // KNOT_EOK
+#include "common/descriptor.h" // KNOT_RRTYPE_
+#include "utils/common/msg.h" // WARN
+#include "utils/common/netio.h" // get_socktype
+#include "utils/common/exec.h" // print_packet
+
+static knot_packet_t* create_query_packet(const query_t *query,
+ uint8_t **data,
+ size_t *data_len)
+{
+ knot_question_t q;
+ knot_packet_t *packet;
+
+ // Set packet buffer size.
+ int max_size = query->udp_size;
+ if (max_size < 0) {
+ if (get_socktype(query->protocol, query->type_num)
+ == SOCK_STREAM) {
+ max_size = MAX_PACKET_SIZE;
+ } else if (query->flags.do_flag || query->nsid) {
+ max_size = DEFAULT_EDNS_SIZE;
+ } else {
+ max_size = DEFAULT_UDP_SIZE;
+ }
+ }
+
+ // Create packet skeleton.
+ packet = create_empty_packet(KNOT_PACKET_PREALLOC_NONE, max_size);
+ if (packet == NULL) {
+ return NULL;
+ }
+
+ // Set flags to wireformat.
+ if (query->flags.aa_flag == true) {
+ knot_wire_set_aa(packet->wireformat);
+ }
+ if (query->flags.tc_flag == true) {
+ knot_wire_set_tc(packet->wireformat);
+ }
+ if (query->flags.rd_flag == true) {
+ knot_wire_set_rd(packet->wireformat);
+ }
+ if (query->flags.ra_flag == true) {
+ knot_wire_set_ra(packet->wireformat);
+ }
+ if (query->flags.z_flag == true) {
+ knot_wire_set_z(packet->wireformat);
+ }
+ if (query->flags.ad_flag == true) {
+ knot_wire_set_ad(packet->wireformat);
+ }
+ if (query->flags.cd_flag == true) {
+ knot_wire_set_cd(packet->wireformat);
+ }
+
+ // Fill auxiliary question structure.
+ q.qclass = query->class_num;
+ q.qtype = query->type_num;
+ q.qname = knot_dname_new_from_str(query->owner, strlen(query->owner), 0);
+ if (q.qname == NULL) {
+ knot_packet_free(&packet);
+ return NULL;
+ }
+
+ // Set packet question.
+ if (knot_query_set_question(packet, &q) != KNOT_EOK) {
+ // It's necessary to release q.qname by hand as it isn't
+ // connected with the packet yet.
+ knot_dname_release(q.qname);
+ knot_packet_free(&packet);
+ return NULL;
+ }
+
+ // For IXFR query add authority section.
+ if (query->type_num == KNOT_RRTYPE_IXFR) {
+ // SOA rdata in wireformat.
+ uint8_t wire[22] = { 0x0 };
+ size_t pos = 0;
+ int ret;
+
+ // Create rrset with SOA record.
+ knot_rrset_t *soa = knot_rrset_new(q.qname,
+ KNOT_RRTYPE_SOA,
+ query->class_num,
+ 0);
+ if (soa == NULL) {
+ knot_packet_free(&packet);
+ return NULL;
+ }
+
+ // Fill in blank SOA rdata to rrset.
+ ret = knot_rrset_rdata_from_wire_one(soa, wire, &pos,
+ sizeof(wire), sizeof(wire));
+ if (ret != KNOT_EOK) {
+ free(soa);
+ knot_packet_free(&packet);
+ return NULL;
+ }
+
+ // Set SOA serial.
+ knot_rrset_rdata_soa_serial_set(soa, query->xfr_serial);
+
+ // Add authority section.
+ ret = knot_query_add_rrset_authority(packet, soa);
+ if (ret != KNOT_EOK) {
+ free(soa);
+ knot_packet_free(&packet);
+ return NULL;
+ }
+ }
+
+ // Create EDNS section if required.
+ if (query->udp_size > 0 || query->flags.do_flag || query->nsid) {
+ knot_opt_rr_t *opt_rr = knot_edns_new();
+ if (opt_rr == NULL) {
+ ERR("can't create EDNS section\n");
+ knot_edns_free(&opt_rr);
+ knot_packet_free(&packet);
+ return NULL;
+ }
+
+ knot_edns_set_version(opt_rr, 0);
+ knot_edns_set_payload(opt_rr, max_size);
+
+ if (knot_response_add_opt(packet, opt_rr, 0, 0) != KNOT_EOK) {
+ ERR("can't set EDNS section\n");
+ knot_edns_free(&opt_rr);
+ knot_packet_free(&packet);
+ return NULL;
+ }
+
+ // Set DO flag to EDNS section.
+ if (query->flags.do_flag) {
+ knot_edns_set_do(&packet->opt_rr);
+ }
+
+ if (query->nsid) {
+ if (knot_edns_add_option(&packet->opt_rr,
+ EDNS_OPTION_NSID,
+ 0, NULL) != KNOT_EOK) {
+ ERR("can't set NSID query\n");
+ knot_edns_free(&opt_rr);
+ knot_packet_free(&packet);
+ return NULL;
+ }
+ }
+
+ knot_edns_free(&opt_rr);
+ }
+
+ // Create wire query.
+ if (knot_packet_to_wire(packet, data, data_len) != KNOT_EOK) {
+ ERR("can't create wire query packet\n");
+ knot_packet_free(&packet);
+ return NULL;
+ }
+
+ // Sign the packet if a key was specified.
+ if (query->key_params.name != NULL) {
+ int ret = sign_packet(packet, (sign_context_t *)&query->sign_ctx,
+ &query->key_params);
+ if (ret != KNOT_EOK) {
+ ERR("failed to sign query packet (%s)\n",
+ knot_strerror(ret));
+ knot_packet_free(&packet);
+ return NULL;
+ }
+
+ *data_len = packet->size;
+ }
+
+ return packet;
+}
+
+static bool check_reply_id(const knot_packet_t *reply,
+ const knot_packet_t *query)
+{
+ uint16_t query_id = knot_wire_get_id(query->wireformat);
+ uint16_t reply_id = knot_wire_get_id(reply->wireformat);
+
+ if (reply_id != query_id) {
+ WARN("reply ID (%u) is different from query ID (%u)\n",
+ reply_id, query_id);
+ return false;
+ }
+
+ return true;
+}
+
+static void check_reply_question(const knot_packet_t *reply,
+ const knot_packet_t *query)
+{
+ if (reply->header.qdcount < 1) {
+ WARN("response doesn't have question section\n");
+ return;
+ }
+
+ int name_diff = knot_dname_compare_cs(reply->question.qname,
+ query->question.qname);
+
+ if (reply->question.qclass != query->question.qclass ||
+ reply->question.qtype != query->question.qtype ||
+ name_diff != 0) {
+ WARN("query/response question sections are different\n");
+ return;
+ }
+}
+
+static int64_t first_serial_check(const knot_packet_t *reply)
+{
+ if (reply->header.ancount <= 0) {
+ return -1;
+ }
+
+ const knot_rrset_t *first = *(reply->answer);
+
+ if (first->type != KNOT_RRTYPE_SOA) {
+ return -1;
+ } else {
+ return knot_rrset_rdata_soa_serial(first);
+ }
+}
+
+static bool last_serial_check(const uint32_t serial, const knot_packet_t *reply)
+{
+ if (reply->header.ancount <= 0) {
+ return false;
+ }
+
+ const knot_rrset_t *last = *(reply->answer + reply->header.ancount - 1);
+
+ if (last->type != KNOT_RRTYPE_SOA) {
+ return false;
+ } else {
+ int64_t last_serial = knot_rrset_rdata_soa_serial(last);
+
+ if (last_serial == serial) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+static int process_query_packet(const knot_packet_t *query,
+ net_t *net,
+ const bool ignore_tc,
+ const sign_context_t *sign_ctx,
+ const knot_key_params_t *key_params,
+ const style_t *style)
+{
+ struct timeval t_start, t_query, t_end;
+ knot_packet_t *reply;
+ uint8_t in[MAX_PACKET_SIZE];
+ int in_len;
+ int ret;
+
+ // Get start query time.
+ gettimeofday(&t_start, NULL);
+
+ // Connect to the server.
+ ret = net_connect(net);
+ if (ret != KNOT_EOK) {
+ return -1;
+ }
+
+ // Send query packet.
+ ret = net_send(net, query->wireformat, query->size);
+ if (ret != KNOT_EOK) {
+ net_close(net);
+ return -1;
+ }
+
+ // Get stop query time and start reply time.
+ gettimeofday(&t_query, NULL);
+
+ // Print query packet if required.
+ if (style->show_query) {
+ print_packet(query, query->size, net,
+ time_diff(&t_start, &t_query),
+ false, style);
+ }
+
+ // Loop over incoming messages, unless reply id is correct or timeout.
+ while (true) {
+ // Receive a reply message.
+ in_len = net_receive(net, in, sizeof(in));
+ if (in_len <= 0) {
+ net_close(net);
+ return -1;
+ }
+
+ // Get stop reply time.
+ gettimeofday(&t_end, NULL);
+
+ // Create reply packet structure to fill up.
+ reply = knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+ if (reply == NULL) {
+ net_close(net);
+ return -1;
+ }
+
+ // Parse reply to the packet structure.
+ if (knot_packet_parse_from_wire(reply, in, in_len, 0,
+ KNOT_PACKET_DUPL_NO_MERGE)
+ != KNOT_EOK) {
+ ERR("malformed reply packet from %s\n", net->remote_str);
+ knot_packet_free(&reply);
+ net_close(net);
+ return -1;
+ }
+
+ // Compare reply header id.
+ if (check_reply_id(reply, query)) {
+ break;
+ // Check for timeout.
+ } else if (time_diff(&t_query, &t_end) > 1000 * net->wait) {
+ knot_packet_free(&reply);
+ net_close(net);
+ return -1;
+ }
+
+ knot_packet_free(&reply);
+ }
+
+ // Check for TC bit and repeat query with TCP if required.
+ if (knot_wire_get_tc(reply->wireformat) != 0 &&
+ ignore_tc == false && net->socktype == SOCK_DGRAM) {
+ WARN("truncated reply from %s, retrying over TCP\n",
+ net->remote_str);
+ knot_packet_free(&reply);
+ net_close(net);
+
+ net->socktype = SOCK_STREAM;
+
+ return process_query_packet(query, net, true, sign_ctx,
+ key_params, style);
+ }
+
+ // Check for question sections equality.
+ if (knot_wire_get_rcode(in) == KNOT_RCODE_NOERROR) {
+ check_reply_question(reply, query);
+ }
+
+ // Verify signature if a key was specified.
+ if (key_params->name != NULL) {
+ ret = verify_packet(reply, sign_ctx, key_params);
+ if (ret != KNOT_EOK) {
+ ERR("reply verification for %s (%s)\n",
+ net->remote_str, knot_strerror(ret));
+ knot_packet_free(&reply);
+ net_close(net);
+ return -1;
+ }
+ }
+
+ // Print reply packet.
+ print_packet(reply, in_len, net, time_diff(&t_query, &t_end),
+ true, style);
+
+ knot_packet_free(&reply);
+ net_close(net);
+
+ // Check for SERVFAIL.
+ if (knot_wire_get_rcode(in) == KNOT_RCODE_SERVFAIL) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void process_query(const query_t *query)
+{
+ node *server = NULL;
+ knot_packet_t *out_packet;
+ uint8_t *out = NULL;
+ size_t out_len = 0;
+ net_t net;
+ int ret;
+
+ if (query == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ // Create query packet.
+ out_packet = create_query_packet(query, &out, &out_len);
+ if (out_packet == NULL) {
+ ERR("can't create query packet\n");
+ return;
+ }
+
+ // Get connection parameters.
+ int iptype = get_iptype(query->ip);
+ int socktype = get_socktype(query->protocol, query->type_num);
+
+ // Loop over server list to process query.
+ WALK_LIST(server, query->servers) {
+ server_t *remote = (server_t *)server;
+
+ DBG("Quering for owner(%s), class(%u), type(%u), server(%s), "
+ "port(%s), protocol(%s)\n", query->owner, query->class_num,
+ query->type_num, remote->name, remote->service,
+ get_sockname(socktype));
+
+ // Loop over the number of retries.
+ for (size_t i = 0; i <= query->retries; i++) {
+ // Initialize network structure for current server.
+ ret = net_init(query->local, remote, iptype, socktype,
+ query->wait, &net);
+ if (ret != KNOT_EOK) {
+ continue;
+ }
+
+ // Loop over all resolved addresses for remote.
+ while (net.srv != NULL) {
+ ret = process_query_packet(out_packet, &net,
+ query->ignore_tc,
+ &query->sign_ctx,
+ &query->key_params,
+ &query->style);
+ // If error try next resolved address.
+ if (ret != 0) {
+ net.srv = (net.srv)->ai_next;
+ continue;
+ }
+
+ break;
+ }
+
+ // Success.
+ if (ret == 0) {
+ net_clean(&net);
+ knot_packet_free(&out_packet);
+ return;
+ // SERVFAIL.
+ } else if (ret == 1 && query->servfail_stop == true) {
+ net_clean(&net);
+ knot_packet_free(&out_packet);
+ return;
+ }
+
+ if (i < query->retries) {
+ DBG("retrying server %s#%s(%s)\n",
+ remote->name, remote->service,
+ get_sockname(socktype));
+ }
+
+ net_clean(&net);
+ }
+
+ WARN("failed to query server %s#%s(%s)\n",
+ remote->name, remote->service, get_sockname(socktype));
+ }
+
+ knot_packet_free(&out_packet);
+}
+
+static int process_packet_xfr(const knot_packet_t *query,
+ net_t *net,
+ const sign_context_t *sign_ctx,
+ const knot_key_params_t *key_params,
+ const style_t *style)
+{
+ struct timeval t_start, t_query, t_end;
+ knot_packet_t *reply;
+ uint8_t in[MAX_PACKET_SIZE];
+ int in_len;
+ int ret;
+ int64_t serial = 0;
+ size_t total_len = 0;
+ size_t msg_count = 0;
+ size_t rr_count = 0;
+
+ // Get start query time.
+ gettimeofday(&t_start, NULL);
+
+ // Connect to the server.
+ ret = net_connect(net);
+ if (ret != KNOT_EOK) {
+ return -1;
+ }
+
+ // Send query packet.
+ ret = net_send(net, query->wireformat, query->size);
+ if (ret != KNOT_EOK) {
+ net_close(net);
+ return -1;
+ }
+
+ // Get stop query time and start reply time.
+ gettimeofday(&t_query, NULL);
+
+
+ // Print query packet if required.
+ if (style->show_query) {
+ print_packet(query, query->size, net,
+ time_diff(&t_start, &t_query),
+ false, style);
+ }
+
+ // Print leading transfer information.
+ print_header_xfr(&query->question, style);
+
+ // Loop over reply messages unless first and last SOA serials differ.
+ while (true) {
+ // Receive a reply message.
+ in_len = net_receive(net, in, sizeof(in));
+ if (in_len <= 0) {
+ net_close(net);
+ return -1;
+ }
+
+ // Create reply packet structure to fill up.
+ reply = knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+ if (reply == NULL) {
+ net_close(net);
+ return -1;
+ }
+
+ // Parse reply to the packet structure.
+ if (knot_packet_parse_from_wire(reply, in, in_len, 0,
+ KNOT_PACKET_DUPL_NO_MERGE)
+ != KNOT_EOK) {
+ ERR("malformed reply packet from %s\n", net->remote_str);
+ knot_packet_free(&reply);
+ net_close(net);
+ return -1;
+ }
+
+ // Compare reply header id.
+ if (check_reply_id(reply, query) == false) {
+ knot_packet_free(&reply);
+ net_close(net);
+ return -1;
+ }
+
+ // Check for reply error.
+ uint8_t rcode_id = knot_wire_get_rcode(in);
+ if (rcode_id != KNOT_RCODE_NOERROR) {
+ knot_lookup_table_t *rcode =
+ knot_lookup_by_id(knot_rcode_names, rcode_id);
+ if (rcode != NULL) {
+ ERR("server %s respond %s\n",
+ net->remote_str, rcode->name);
+ } else {
+ ERR("server %s respond %i\n",
+ net->remote_str, rcode_id);
+ }
+
+ knot_packet_free(&reply);
+ net_close(net);
+ return -1;
+ }
+
+ // The first message has a special treatment.
+ if (msg_count == 0) {
+ // Verify 1. signature if a key was specified.
+ if (key_params->name != NULL) {
+ ret = verify_packet(reply, sign_ctx, key_params);
+ if (ret != KNOT_EOK) {
+ ERR("reply verification for %s (%s)\n",
+ net->remote_str, knot_strerror(ret));
+ knot_packet_free(&reply);
+ net_close(net);
+ return -1;
+ }
+ }
+
+ // Read first SOA serial.
+ serial = first_serial_check(reply);
+
+ if (serial < 0) {
+ ERR("first answer record isn't SOA\n");
+ knot_packet_free(&reply);
+ net_close(net);
+ return -1;
+ }
+
+ // Check for question sections equality.
+ check_reply_question(reply, query);
+ }
+
+ msg_count++;
+ rr_count += reply->header.ancount;
+ total_len += in_len;
+
+ // Print reply packet.
+ print_data_xfr(reply, style);
+
+ // Stop if last SOA record has correct serial.
+ if (last_serial_check(serial, reply)) {
+ knot_packet_free(&reply);
+ break;
+ }
+
+ knot_packet_free(&reply);
+ }
+
+ // Get stop reply time.
+ gettimeofday(&t_end, NULL);
+
+ // Print trailing transfer information.
+ print_footer_xfr(total_len, msg_count, rr_count, net,
+ time_diff(&t_query, &t_end), style);
+
+ net_close(net);
+
+ return 0;
+}
+
+static void process_query_xfr(const query_t *query)
+{
+ knot_packet_t *out_packet;
+ uint8_t *out = NULL;
+ size_t out_len = 0;
+ net_t net;
+ int ret;
+
+ if (query == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ // Create query packet.
+ out_packet = create_query_packet(query, &out, &out_len);
+ if (out_packet == NULL) {
+ ERR("can't create query packet\n");
+ return;
+ }
+
+ // Get connection parameters.
+ int iptype = get_iptype(query->ip);
+ int socktype = get_socktype(query->protocol, query->type_num);
+
+ // Use the first nameserver from the list.
+ server_t *remote = HEAD(query->servers);
+
+ DBG("Quering for owner(%s), class(%u), type(%u), server(%s), "
+ "port(%s), protocol(%s)\n", query->owner, query->class_num,
+ query->type_num, remote->name, remote->service,
+ get_sockname(socktype));
+
+ // Initialize network structure.
+ ret = net_init(query->local, remote, iptype, socktype,
+ query->wait, &net);
+ if (ret != KNOT_EOK) {
+ knot_packet_free(&out_packet);
+ return;
+ }
+
+ // Loop over all resolved addresses for remote.
+ while (net.srv != NULL) {
+ ret = process_packet_xfr(out_packet, &net,
+ &query->sign_ctx,
+ &query->key_params,
+ &query->style);
+ // If error try next resolved address.
+ if (ret != 0) {
+ net.srv = (net.srv)->ai_next;
+ continue;
+ }
+
+ break;
+ }
+
+ if (ret != 0) {
+ ERR("failed to query server %s#%s(%s)\n",
+ remote->name, remote->service, get_sockname(socktype));
+ }
+
+ net_clean(&net);
+ knot_packet_free(&out_packet);
+}
+
+int dig_exec(const dig_params_t *params)
+{
+ node *n = NULL;
+
+ if (params == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ // Loop over query list.
+ WALK_LIST(n, params->queries) {
+ query_t *query = (query_t *)n;
+
+ switch (query->operation) {
+ case OPERATION_QUERY:
+ process_query(query);
+ break;
+ case OPERATION_XFR:
+ process_query_xfr(query);
+ break;
+ case OPERATION_LIST_SOA:
+ break;
+ default:
+ ERR("unsupported operation\n");
+ break;
+ }
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/utils/dig/dig_exec.h b/src/utils/dig/dig_exec.h
new file mode 100644
index 0000000..6e1a40b
--- /dev/null
+++ b/src/utils/dig/dig_exec.h
@@ -0,0 +1,37 @@
+/* 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/>.
+ */
+/*!
+ * \file dig_exec.h
+ *
+ * \author Daniel Salzman <daniel.salzman@nic.cz>
+ *
+ * \brief dig executives.
+ *
+ * \addtogroup knot_utils
+ * @{
+ */
+
+#ifndef _DIG__DIG_EXEC_H_
+#define _DIG__DIG_EXEC_H_
+
+#include "utils/common/params.h" // params_t
+#include "utils/dig/dig_params.h" // query_t
+
+int dig_exec(const dig_params_t *params);
+
+#endif // _DIG__DIG_EXEC_H_
+
+/*! @} */
diff --git a/src/utils/dig/dig_main.c b/src/utils/dig/dig_main.c
new file mode 100644
index 0000000..487b150
--- /dev/null
+++ b/src/utils/dig/dig_main.c
@@ -0,0 +1,39 @@
+/* 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 <stdlib.h> // EXIT_FAILURE
+
+#include "common/errcode.h" // KNOT_EOK
+#include "utils/dig/dig_params.h" // dig_parse
+#include "utils/dig/dig_exec.h" // dig_exec
+
+int main(int argc, char *argv[])
+{
+ int ret = EXIT_SUCCESS;
+
+ dig_params_t params;
+ if (dig_parse(&params, argc, argv) == KNOT_EOK) {
+ if (!params.stop && dig_exec(&params) != KNOT_EOK) {
+ ret = EXIT_FAILURE;
+ }
+ } else {
+ ret = EXIT_FAILURE;
+ }
+
+ dig_clean(&params);
+ return ret;
+}
diff --git a/src/utils/dig/dig_params.c b/src/utils/dig/dig_params.c
new file mode 100644
index 0000000..54361c8
--- /dev/null
+++ b/src/utils/dig/dig_params.c
@@ -0,0 +1,976 @@
+/* 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/dig/dig_params.h"
+
+#include <string.h> // strncmp
+#include <stdio.h> // printf
+#include <getopt.h> // getopt
+#include <stdlib.h> // free
+
+#include "common/lists.h" // list
+#include "common/errcode.h" // KNOT_EOK
+#include "common/descriptor.h" // KNOT_CLASS_IN
+#include "utils/common/msg.h" // WARN
+#include "utils/common/params.h" // parse_class
+#include "utils/common/resolv.h" // get_nameservers
+
+#define DEFAULT_RETRIES_DIG 2
+#define DEFAULT_TIMEOUT_DIG 5
+
+static const flags_t DEFAULT_FLAGS_DIG = {
+ .aa_flag = false,
+ .tc_flag = false,
+ .rd_flag = true,
+ .ra_flag = false,
+ .z_flag = false,
+ .ad_flag = false,
+ .cd_flag = false,
+ .do_flag = false
+};
+
+static const style_t DEFAULT_STYLE_DIG = {
+ .format = FORMAT_FULL,
+ .style = { .wrap = false, .show_class = true, .show_ttl = true,
+ .verbose = false, .reduce = false, .human_ttl = false,
+ .human_tmstamp = true },
+ .show_query = false,
+ .show_header = true,
+ .show_edns = true,
+ .show_question = true,
+ .show_answer = true,
+ .show_authority = true,
+ .show_additional = true,
+ .show_footer = true
+};
+
+query_t* query_create(const char *owner, const query_t *conf)
+{
+ // Create output structure.
+ query_t *query = calloc(1, sizeof(query_t));
+
+ if (query == NULL) {
+ DBG_NULL;
+ return NULL;
+ }
+
+ // Set the query owner if any.
+ if (owner != NULL) {
+ if ((query->owner = strdup(owner)) == NULL) {
+ query_free(query);
+ return NULL;
+ }
+ }
+
+ // Initialize list of servers.
+ init_list(&query->servers);
+
+ // Initialization with defaults or with reference query.
+ if (conf == NULL) {
+ query->local = NULL;
+ query->operation = OPERATION_QUERY;
+ query->ip = IP_ALL;
+ query->protocol = PROTO_ALL;
+ query->port = strdup("");
+ query->udp_size = -1;
+ query->retries = DEFAULT_RETRIES_DIG;
+ query->wait = DEFAULT_TIMEOUT_DIG;
+ query->ignore_tc = false;
+ query->servfail_stop = true;
+ query->class_num = -1;
+ query->type_num = -1;
+ query->xfr_serial = 0;
+ query->flags = DEFAULT_FLAGS_DIG;
+ query->style = DEFAULT_STYLE_DIG;
+ query->nsid = false;
+ } else {
+ if (conf->local != NULL) {
+ query->local = server_create(conf->local->name,
+ conf->local->service);
+ if (query->local == NULL) {
+ query_free(query);
+ return NULL;
+ }
+ } else {
+ query->local = NULL;
+ }
+ query->operation = conf->operation;
+ query->ip = conf->ip;
+ query->protocol = conf->protocol;
+ query->port = strdup(conf->port);
+ query->udp_size = conf->udp_size;
+ query->retries = conf->retries;
+ query->wait = conf->wait;
+ query->ignore_tc = conf->ignore_tc;
+ query->servfail_stop = conf->servfail_stop;
+ query->class_num = conf->class_num;
+ query->type_num = conf->type_num;
+ query->xfr_serial = conf->xfr_serial;
+ query->flags = conf->flags;
+ query->style = conf->style;
+ query->nsid = conf->nsid;
+
+ if (knot_copy_key_params(&conf->key_params, &query->key_params)
+ != KNOT_EOK) {
+ query_free(query);
+ return NULL;
+ }
+ }
+
+ // Check dynamic allocation.
+ if (query->port == NULL) {
+ query_free(query);
+ return NULL;
+ }
+
+ return query;
+}
+
+void query_free(query_t *query)
+{
+ node *n = NULL, *nxt = NULL;
+
+ if (query == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ // Cleanup servers.
+ WALK_LIST_DELSAFE(n, nxt, query->servers) {
+ server_free((server_t *)n);
+ }
+
+ // Cleanup local address.
+ if (query->local != NULL) {
+ server_free(query->local);
+ }
+
+ // Cleanup cryptographic content.
+ free_sign_context(&query->sign_ctx);
+ knot_free_key_params(&query->key_params);
+
+ free(query->owner);
+ free(query->port);
+ free(query);
+}
+
+int dig_init(dig_params_t *params)
+{
+ if (params == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ memset(params, 0, sizeof(*params));
+
+ params->stop = false;
+
+ // Initialize list of queries.
+ init_list(&params->queries);
+
+ // Create config query.
+ if ((params->config = query_create(NULL, NULL)) == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+void dig_clean(dig_params_t *params)
+{
+ node *n = NULL, *nxt = NULL;
+
+ if (params == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ // Clean up queries.
+ WALK_LIST_DELSAFE(n, nxt, params->queries) {
+ query_free((query_t *)n);
+ }
+
+ // Clean up config.
+ query_free((query_t *)params->config);
+
+ // Clean up the structure.
+ memset(params, 0, sizeof(*params));
+}
+
+static int parse_class(const char *value, query_t *query)
+{
+ uint16_t rclass;
+
+ if (params_parse_class(value, &rclass) != KNOT_EOK) {
+ return KNOT_EINVAL;
+ }
+
+ query->class_num = rclass;
+
+ return KNOT_EOK;
+}
+
+static int parse_keyfile(const char *value, query_t *query)
+{
+ knot_free_key_params(&query->key_params);
+
+ if (params_parse_keyfile(value, &query->key_params) != KNOT_EOK) {
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_local(const char *value, query_t *query)
+{
+ server_t *local = parse_nameserver(value, "0");
+ if (local == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (query->local != NULL) {
+ server_free(query->local);
+ }
+
+ query->local = local;
+
+ return KNOT_EOK;
+}
+
+static int parse_name(const char *value, list *queries, const query_t *conf)
+{
+ query_t *query = NULL;
+
+ // If name is not FQDN, append trailing dot.
+ char *fqd_name = get_fqd_name(value);
+
+ // Create new query.
+ query = query_create(fqd_name, conf);
+
+ free(fqd_name);
+
+ if (query == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Add new query to the queries.
+ add_tail(queries, (node *)query);
+
+ return KNOT_EOK;
+}
+
+static int parse_port(const char *value, query_t *query)
+{
+ char **port;
+
+ // Set current server port (last or query default).
+ if (list_size(&query->servers) > 0) {
+ server_t *server = TAIL(query->servers);
+ port = &(server->service);
+ } else {
+ port = &(query->port);
+ }
+
+ char *new_port = strdup(value);
+
+ if (new_port == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Deallocate old string.
+ free(*port);
+
+ *port = new_port;
+
+ return KNOT_EOK;
+}
+
+static int parse_reverse(const char *value, list *queries, const query_t *conf)
+{
+ query_t *query = NULL;
+
+ // Create reverse name.
+ char *reverse = get_reverse_name(value);
+
+ if (reverse == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Create reverse query for given address.
+ query = query_create(reverse, conf);
+
+ free(reverse);
+
+ if (query == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Set type for reverse query.
+ query->type_num = KNOT_RRTYPE_PTR;
+
+ // Add new query to the queries.
+ add_tail(queries, (node *)query);
+
+ return KNOT_EOK;
+}
+
+static int parse_server(const char *value, dig_params_t *params)
+{
+ query_t *query;
+
+ // Set current query (last or config).
+ if (list_size(&params->queries) > 0) {
+ query = TAIL(params->queries);
+ } else {
+ query = params->config;
+ }
+
+ return params_parse_server(value, &query->servers, query->port);
+}
+
+static int parse_tsig(const char *value, query_t *query)
+{
+ knot_free_key_params(&query->key_params);
+
+ if (params_parse_tsig(value, &query->key_params) != KNOT_EOK) {
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_type(const char *value, query_t *query)
+{
+ uint16_t rtype;
+ uint32_t serial;
+
+ if (params_parse_type(value, &rtype, &serial) != KNOT_EOK) {
+ return KNOT_EINVAL;
+ }
+
+ query->type_num = rtype;
+ query->xfr_serial = serial;
+
+ return KNOT_EOK;
+}
+
+static void complete_servers(query_t *query, const query_t *conf)
+{
+ node *n = NULL;
+ char *def_port;
+
+ // Decide which default port use.
+ if (strlen(query->port) > 0) {
+ def_port = query->port;
+ } else if (strlen(conf->port) > 0) {
+ def_port = conf->port;
+ } else {
+ def_port = DEFAULT_DNS_PORT;
+ }
+
+ // Complete specified nameservers if any.
+ if (list_size(&query->servers) > 0) {
+ WALK_LIST(n, query->servers) {
+ server_t *s = (server_t *)n;
+
+ // If the port isn't specified yet use the default one.
+ if (strlen(s->service) == 0) {
+ free(s->service);
+ s->service = strdup(def_port);
+ if (s->service == NULL) {
+ WARN("can't set port %s\n", def_port);
+ return;
+ }
+ }
+ }
+ // Use servers from config if any.
+ } else if (list_size(&conf->servers) > 0) {
+ WALK_LIST(n, conf->servers) {
+ server_t *s = (server_t *)n;
+ char *port = def_port;
+
+ // If the port is already specified, use it.
+ if (strlen(s->service) > 0) {
+ port = s->service;
+ }
+
+ server_t *server = server_create(s->name, port);
+ if (server == NULL) {
+ WARN("can't set nameserver %s port %s\n",
+ s->name, s->service);
+ return;
+ }
+ add_tail(&query->servers, (node *)server);
+ }
+ // Use system specific.
+ } else if (get_nameservers(&query->servers, def_port) <= 0) {
+ WARN("can't read any nameservers\n");
+ }
+}
+
+void complete_queries(list *queries, const query_t *conf)
+{
+ query_t *q = NULL;
+ node *n = NULL;
+
+ if (queries == NULL || conf == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ // If there is no query, add default query: NS to ".".
+ if (list_size(queries) == 0) {
+ q = query_create(".", conf);
+ if (q == NULL) {
+ WARN("can't create query . NS IN\n");
+ return;
+ }
+ q->class_num = KNOT_CLASS_IN;
+ q->type_num = KNOT_RRTYPE_NS;
+ add_tail(queries, (node *)q);
+ }
+
+ WALK_LIST(n, *queries) {
+ query_t *q = (query_t *)n;
+
+ // Fill class number if missing.
+ if (q->class_num < 0) {
+ if (conf->class_num >= 0) {
+ q->class_num = conf->class_num;
+ } else {
+ q->class_num = KNOT_CLASS_IN;
+ }
+ }
+
+ // Fill type number if missing.
+ if (q->type_num < 0) {
+ if (conf->type_num >= 0) {
+ q->type_num = conf->type_num;
+ q->xfr_serial = conf->xfr_serial;
+ } else {
+ q->type_num = KNOT_RRTYPE_A;
+ }
+ }
+
+ // Set zone transfer if any.
+ if (q->type_num == KNOT_RRTYPE_AXFR ||
+ q->type_num == KNOT_RRTYPE_IXFR) {
+ q->operation = OPERATION_XFR;
+ }
+
+ // No retries for TCP.
+ if (q->protocol == PROTO_TCP) {
+ q->retries = 0;
+ }
+
+ // Complete nameservers list.
+ complete_servers(q, conf);
+ }
+}
+
+static void dig_help(void)
+{
+ printf("Usage: kdig [-4] [-6] [-dh] [-b address] [-c class] [-p port]\n"
+ " [-q name] [-t type] [-x address] [-k keyfile]\n"
+ " [-y [algo:]keyname:key] name @server\n"
+ "\n"
+ " +[no]multiline Wrap long records to more lines.\n"
+ " +[no]short Show record data only.\n"
+ " +[no]aaflag Set AA flag.\n"
+ " +[no]tcflag Set TC flag.\n"
+ " +[no]rdflag Set RD flag.\n"
+ " +[no]recurse Same as +[no]rdflag\n"
+ " +[no]rec Same as +[no]rdflag\n"
+ " +[no]raflag Set RA flag.\n"
+ " +[no]zflag Set zero flag bit.\n"
+ " +[no]adflag Set AD flag.\n"
+ " +[no]cdflag Set CD flag.\n"
+ " +[no]dnssec Set DO flag.\n"
+ " +[no]all Show all packet sections.\n"
+ " +[no]qr Show query packet.\n"
+ " +[no]header Show packet header.\n"
+ " +[no]edns Show EDNS pseudosection.\n"
+ " +[no]question Show question section.\n"
+ " +[no]answer Show answer section.\n"
+ " +[no]authority Show authority section.\n"
+ " +[no]additional Show additional section.\n"
+ " +[no]stats Show trailing packet statistics.\n"
+ " +[no]cl Show DNS class.\n"
+ " +[no]ttl Show TTL value.\n"
+ " +time=T Set wait for reply interval in seconds.\n"
+ " +retries=N Set number of retries.\n"
+ " +bufsize=B Set EDNS buffer size.\n"
+ " +[no]tcp Use TCP protocol.\n"
+ " +[no]fail Stop if SERVFAIL.\n"
+ " +[no]ignore Don't use TCP automatically if truncated.\n"
+ " +[no]nsid Request NSID.\n"
+ "\n"
+ " -h, --help Print help.\n"
+ " -v, --version Print program version.\n");
+}
+
+static int parse_opt1(const char *opt, const char *value, dig_params_t *params,
+ int *index)
+{
+ const char *val = value;
+ size_t len = strlen(opt);
+ int add = 1;
+ query_t *query;
+
+ // Set current query (last or config).
+ if (list_size(&params->queries) > 0) {
+ query = TAIL(params->queries);
+ } else {
+ query = params->config;
+ }
+
+ // If there is no space between option and argument.
+ if (len > 1) {
+ val = opt + 1;
+ add = 0;
+ }
+
+ switch (opt[0]) {
+ case '4':
+ if (len > 1) {
+ ERR("invalid option -%s\n", opt);
+ return KNOT_ENOTSUP;
+ }
+
+ query->ip = IP_4;
+ break;
+ case '6':
+ if (len > 1) {
+ ERR("invalid option -%s\n", opt);
+ return KNOT_ENOTSUP;
+ }
+
+ query->ip = IP_6;
+ break;
+ case 'b':
+ if (val == NULL) {
+ ERR("missing address\n");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_local(val, query) != KNOT_EOK) {
+ ERR("bad address %s\n", val);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 'd':
+ msg_enable_debug(1);
+ break;
+ case 'h':
+ if (len > 1) {
+ ERR("invalid option -%s\n", opt);
+ return KNOT_ENOTSUP;
+ }
+
+ dig_help();
+ params->stop = true;
+ break;
+ case 'c':
+ if (val == NULL) {
+ ERR("missing class\n");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_class(val, query) != KNOT_EOK) {
+ ERR("bad class %s\n", val);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 'k':
+ if (val == NULL) {
+ ERR("missing filename\n");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_keyfile(val, query) != KNOT_EOK) {
+ ERR("bad keyfile %s\n", value);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 'p':
+ if (val == NULL) {
+ ERR("missing port\n");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_port(val, query) != KNOT_EOK) {
+ ERR("bad port %s\n", value);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 'q':
+ if (val == NULL) {
+ ERR("missing name\n");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_name(val, &params->queries, params->config)
+ != KNOT_EOK) {
+ ERR("bad query name %s\n", val);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 't':
+ if (val == NULL) {
+ ERR("missing type\n");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_type(val, query) != KNOT_EOK) {
+ ERR("bad type %s\n", val);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 'v':
+ if (len > 1) {
+ ERR("invalid option -%s\n", opt);
+ return KNOT_ENOTSUP;
+ }
+
+ printf(KDIG_VERSION);
+ params->stop = true;
+ break;
+ case 'x':
+ if (val == NULL) {
+ ERR("missing address\n");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_reverse(val, &params->queries, params->config)
+ != KNOT_EOK) {
+ ERR("bad reverse name %s\n", val);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 'y':
+ if (val == NULL) {
+ ERR("missing key\n");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_tsig(val, query) != KNOT_EOK) {
+ ERR("bad key %s\n", value);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case '-':
+ if (strcmp(opt, "-help") == 0) {
+ dig_help();
+ params->stop = true;
+ } else if (strcmp(opt, "-version") == 0) {
+ printf(KDIG_VERSION);
+ params->stop = true;
+ } else {
+ ERR("invalid option: -%s\n", opt);
+ return KNOT_ENOTSUP;
+ }
+ break;
+ default:
+ ERR("invalid option: -%s\n", opt);
+ return KNOT_ENOTSUP;
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_opt2(const char *value, dig_params_t *params)
+{
+ query_t *query;
+
+ // Set current query (last or config).
+ if (list_size(&params->queries) > 0) {
+ query = TAIL(params->queries);
+ } else {
+ query = params->config;
+ }
+
+ // Check for format option.
+ if (strcmp(value, "multiline") == 0) {
+ query->style.style.wrap = true;
+ query->style.format = FORMAT_FULL;
+ query->style.show_header = true;
+ query->style.show_edns = true;
+ query->style.show_footer = true;
+ query->style.style.verbose = true;
+ query->style.style.human_ttl = true;
+ } else if (strcmp(value, "nomultiline") == 0) {
+ query->style.style.wrap = false;
+ }
+ else if (strcmp(value, "short") == 0) {
+ query->style.format = FORMAT_DIG;
+ query->style.show_header = false;
+ query->style.show_edns = false;
+ query->style.show_footer = false;
+ } else if (strcmp(value, "noshort") == 0) {
+ query->style.format = FORMAT_FULL;
+ }
+
+ // Check for flag option.
+ else if (strcmp(value, "aaflag") == 0) {
+ query->flags.aa_flag = true;
+ } else if (strcmp(value, "noaaflag") == 0) {
+ query->flags.aa_flag = false;
+ }
+ else if (strcmp(value, "tcflag") == 0) {
+ query->flags.tc_flag = true;
+ } else if (strcmp(value, "notcflag") == 0) {
+ query->flags.tc_flag = false;
+ }
+ else if (strcmp(value, "rdflag") == 0 ||
+ strcmp(value, "recurse") == 0 ||
+ strcmp(value, "rec") == 0) {
+ query->flags.rd_flag = true;
+ } else if (strcmp(value, "nordflag") == 0 ||
+ strcmp(value, "norecurse") == 0 ||
+ strcmp(value, "norec") == 0) {
+ query->flags.rd_flag = false;
+ }
+ else if (strcmp(value, "raflag") == 0) {
+ query->flags.ra_flag = true;
+ } else if (strcmp(value, "noraflag") == 0) {
+ query->flags.ra_flag = false;
+ }
+ else if (strcmp(value, "zflag") == 0) {
+ query->flags.z_flag = true;
+ } else if (strcmp(value, "nozflag") == 0) {
+ query->flags.z_flag = false;
+ }
+ else if (strcmp(value, "adflag") == 0) {
+ query->flags.ad_flag = true;
+ } else if (strcmp(value, "noadflag") == 0) {
+ query->flags.ad_flag = false;
+ }
+ else if (strcmp(value, "cdflag") == 0) {
+ query->flags.cd_flag = true;
+ } else if (strcmp(value, "nocdflag") == 0) {
+ query->flags.cd_flag = false;
+ }
+ else if (strcmp(value, "dnssec") == 0) {
+ query->flags.do_flag = true;
+ } else if (strcmp(value, "nodnssec") == 0) {
+ query->flags.do_flag = false;
+ }
+
+ // Check for display option.
+ else if (strcmp(value, "all") == 0) {
+ query->style.show_header = true;
+ query->style.show_edns = true;
+ query->style.show_question = true;
+ query->style.show_answer = true;
+ query->style.show_authority = true;
+ query->style.show_additional = true;
+ query->style.show_footer = true;
+ } else if (strcmp(value, "noall") == 0) {
+ query->style.show_header = false;
+ query->style.show_edns = false;
+ query->style.show_query = false;
+ query->style.show_question = false;
+ query->style.show_answer = false;
+ query->style.show_authority = false;
+ query->style.show_additional = false;
+ query->style.show_footer = false;
+ }
+ else if (strcmp(value, "qr") == 0) {
+ query->style.show_query = true;
+ } else if (strcmp(value, "noqr") == 0) {
+ query->style.show_query = false;
+ }
+ else if (strcmp(value, "header") == 0) {
+ query->style.show_header = true;
+ } else if (strcmp(value, "noheader") == 0) {
+ query->style.show_header = false;
+ }
+ else if (strcmp(value, "edns") == 0) {
+ query->style.show_edns = true;
+ } else if (strcmp(value, "noedns") == 0) {
+ query->style.show_edns = false;
+ }
+ else if (strcmp(value, "question") == 0) {
+ query->style.show_question = true;
+ } else if (strcmp(value, "noquestion") == 0) {
+ query->style.show_question = false;
+ }
+ else if (strcmp(value, "answer") == 0) {
+ query->style.show_answer = true;
+ } else if (strcmp(value, "noanswer") == 0) {
+ query->style.show_answer = false;
+ }
+ else if (strcmp(value, "authority") == 0) {
+ query->style.show_authority = true;
+ } else if (strcmp(value, "noauthority") == 0) {
+ query->style.show_authority = false;
+ }
+ else if (strcmp(value, "additional") == 0) {
+ query->style.show_additional = true;
+ } else if (strcmp(value, "noadditional") == 0) {
+ query->style.show_additional = false;
+ }
+ else if (strcmp(value, "stats") == 0) {
+ query->style.show_footer = true;
+ } else if (strcmp(value, "nostats") == 0) {
+ query->style.show_footer = false;
+ }
+ else if (strcmp(value, "cl") == 0) {
+ query->style.style.show_class = true;
+ } else if (strcmp(value, "nocl") == 0) {
+ query->style.style.show_class = false;
+ }
+ else if (strcmp(value, "ttl") == 0) {
+ query->style.style.show_ttl = true;
+ } else if (strcmp(value, "nottl") == 0) {
+ query->style.style.show_ttl = false;
+ }
+
+ // Check for query option.
+ else if (strncmp(value, "time=", 5) == 0) {
+ if (params_parse_wait(value + 5, &query->wait)
+ != KNOT_EOK) {
+ return KNOT_EINVAL;
+ }
+ }
+ else if (strncmp(value, "retries=", 8) == 0) {
+ if (params_parse_num(value + 8, &query->retries)
+ != KNOT_EOK) {
+ return KNOT_EINVAL;
+ }
+ }
+ else if (strncmp(value, "bufsize=", 8) == 0) {
+ if (params_parse_bufsize(value + 8, &query->udp_size)
+ != KNOT_EOK) {
+ return KNOT_EINVAL;
+ }
+ }
+ else if (strcmp(value, "tcp") == 0) {
+ query->protocol = PROTO_TCP;
+ } else if (strcmp(value, "notcp") == 0) {
+ query->protocol = PROTO_UDP;
+ query->ignore_tc = true;
+ }
+ else if (strcmp(value, "fail") == 0) {
+ query->servfail_stop = true;
+ } else if (strcmp(value, "nofail") == 0) {
+ query->servfail_stop = false;
+ }
+ else if (strcmp(value, "ignore") == 0) {
+ query->ignore_tc = true;
+ } else if (strcmp(value, "noignore") == 0) {
+ query->ignore_tc = false;
+ }
+ else if (strcmp(value, "nsid") == 0) {
+ query->nsid = true;
+ } else if (strcmp(value, "nonsid") == 0) {
+ query->nsid = false;
+ }
+
+ // Unknown option.
+ else {
+ ERR("invalid option: +%s\n", value);
+ return KNOT_ENOTSUP;
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_token(const char *value, dig_params_t *params)
+{
+ query_t *query;
+
+ // Set current query (last or config).
+ if (list_size(&params->queries) > 0) {
+ query = TAIL(params->queries);
+ } else {
+ query = params->config;
+ }
+
+ // Try to guess the meaning of the token.
+ if (parse_type(value, query) == KNOT_EOK) {
+ return KNOT_EOK;
+ } else if (parse_class(value, query) == KNOT_EOK) {
+ return KNOT_EOK;
+ } else if (parse_name(value, &params->queries, params->config)
+ == KNOT_EOK) {
+ return KNOT_EOK;
+ }
+
+ ERR("invalid parameter: %s\n", value);
+ return KNOT_ENOTSUP;
+}
+
+int dig_parse(dig_params_t *params, int argc, char *argv[])
+{
+ if (params == NULL || argv == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ // Initialize parameters.
+ if (dig_init(params) != KNOT_EOK) {
+ return KNOT_ERROR;
+ }
+
+ // Command line parameters processing.
+ for (int i = 1; i < argc; i++) {
+ int ret = KNOT_ERROR;
+
+ // Process parameter.
+ switch (argv[i][0]) {
+ case '@':
+ ret = parse_server(argv[i] + 1, params);
+ break;
+ case '-':
+ ret = parse_opt1(argv[i] + 1, argv[i + 1], params, &i);
+ break;
+ case '+':
+ ret = parse_opt2(argv[i] + 1, params);
+ break;
+ default:
+ ret = parse_token(argv[i], params);
+ break;
+ }
+
+ // Check return.
+ switch (ret) {
+ case KNOT_EOK:
+ if (params->stop) {
+ return KNOT_EOK;
+ }
+ break;
+ case KNOT_ENOTSUP:
+ dig_help();
+ default: // Fall through.
+ return ret;
+ }
+ }
+
+ // Complete missing data in queries based on defaults.
+ complete_queries(&params->queries, params->config);
+
+ return KNOT_EOK;
+}
diff --git a/src/utils/dig/dig_params.h b/src/utils/dig/dig_params.h
new file mode 100644
index 0000000..f2258ac
--- /dev/null
+++ b/src/utils/dig/dig_params.h
@@ -0,0 +1,133 @@
+/* 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/>.
+ */
+/*!
+ * \file dig_params.h
+ *
+ * \author Daniel Salzman <daniel.salzman@nic.cz>
+ *
+ * \brief dig command line parameters.
+ *
+ * \addtogroup knot_utils
+ * @{
+ */
+
+#ifndef _DIG__DIG_PARAMS_H_
+#define _DIG__DIG_PARAMS_H_
+
+#include <stdbool.h> // bool
+
+#include "utils/common/params.h" // protocol_t
+#include "utils/common/exec.h" // sign_context_t
+
+#define KDIG_VERSION "kdig, version " PACKAGE_VERSION "\n"
+
+/*! \brief Operation mode of dig. */
+typedef enum {
+ /*!< Standard 1-message query/reply. */
+ OPERATION_QUERY,
+ /*!< Zone transfer (AXFR or IXFR). */
+ OPERATION_XFR,
+ /*!< Query for NS and all authoritative SOA records. */
+ OPERATION_LIST_SOA
+} operation_t;
+
+/*! \brief DNS header and EDNS flags. */
+typedef struct {
+ /*!< Authoritative answer flag. */
+ bool aa_flag;
+ /*!< Truncated flag. */
+ bool tc_flag;
+ /*!< Recursion desired flag. */
+ bool rd_flag;
+ /*!< Recursion available flag. */
+ bool ra_flag;
+ /*!< Z flag. */
+ bool z_flag;
+ /*!< Authenticated data flag. */
+ bool ad_flag;
+ /*!< Checking disabled flag. */
+ bool cd_flag;
+ /*!< DNSSEC OK flag. */
+ bool do_flag;
+} flags_t;
+
+/*! \brief Basic parameters for DNS query. */
+typedef struct {
+ /*!< List node (for list container). */
+ node n;
+ /*!< Name to query on. */
+ char *owner;
+ /*!< List of nameservers to query to. */
+ list servers;
+ /*!< Local interface (optional). */
+ server_t *local;
+ /*!< Operation mode. */
+ operation_t operation;
+ /*!< Version of ip protocol to use. */
+ ip_t ip;
+ /*!< Protocol type (TCP, UDP) to use. */
+ protocol_t protocol;
+ /*!< Port/service to connect to. */
+ char *port;
+ /*!< UDP buffer size (16unsigned + -1 uninitialized). */
+ int32_t udp_size;
+ /*!< Number of UDP retries. */
+ uint32_t retries;
+ /*!< Wait for network response in seconds (-1 means forever). */
+ int32_t wait;
+ /*!< Ignore truncated response. */
+ bool ignore_tc;
+ /*!< Stop quering if servfail. */
+ bool servfail_stop;
+ /*!< Class number (16unsigned + -1 uninitialized). */
+ int32_t class_num;
+ /*!< Type number (16unsigned + -1 uninitialized). */
+ int32_t type_num;
+ /*!< SOA serial for XFR. */
+ uint32_t xfr_serial;
+ /*!< Header flags. */
+ flags_t flags;
+ /*!< Output settings. */
+ style_t style;
+ /*!< Query for NSID. */
+ bool nsid;
+ /*!< Key parameters. */
+ knot_key_params_t key_params;
+ /*!< Context for operations with signatures. */
+ sign_context_t sign_ctx;
+} query_t;
+
+/*! \brief Settings for dig. */
+typedef struct {
+ /*!< Stop processing - just pring help, version,... */
+ bool stop;
+ /*!< List of DNS queries to process. */
+ list queries;
+ /*!< Default settings for queries. */
+ query_t *config;
+} dig_params_t;
+
+query_t* query_create(const char *owner, const query_t *config);
+void query_free(query_t *query);
+void complete_queries(list *queries, const query_t *conf);
+
+int dig_init(dig_params_t *params);
+int dig_parse(dig_params_t *params, int argc, char *argv[]);
+void dig_clean(dig_params_t *params);
+
+#endif // _DIG__DIG_PARAMS_H_
+
+/*! @} */