diff options
Diffstat (limited to 'src/utils/dig')
-rw-r--r-- | src/utils/dig/dig_exec.c | 732 | ||||
-rw-r--r-- | src/utils/dig/dig_exec.h | 37 | ||||
-rw-r--r-- | src/utils/dig/dig_main.c | 39 | ||||
-rw-r--r-- | src/utils/dig/dig_params.c | 976 | ||||
-rw-r--r-- | src/utils/dig/dig_params.h | 133 |
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(¶ms, argc, argv) == KNOT_EOK) { + if (!params.stop && dig_exec(¶ms) != KNOT_EOK) { + ret = EXIT_FAILURE; + } + } else { + ret = EXIT_FAILURE; + } + + dig_clean(¶ms); + 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(¶ms->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(¶ms->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(¶ms->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, ¶ms->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, ¶ms->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(¶ms->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(¶ms->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, ¶ms->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(¶ms->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_ + +/*! @} */ |