summaryrefslogtreecommitdiffstats
path: root/src/args.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2021-03-04 19:22:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2021-03-04 20:43:22 +0000
commit22c74419e2c258319bc723351876604b3304604b (patch)
tree8c799a78d53f67388fdf42900657eda617c1306a /src/args.c
parentInitial commit. (diff)
downloaddnscap-235d496c12a2daddf1ab9e2159b8ac2e92f5460d.tar.xz
dnscap-235d496c12a2daddf1ab9e2159b8ac2e92f5460d.zip
Adding upstream version 2.0.0+debian.upstream/2.0.0+debian
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/args.c')
-rw-r--r--src/args.c843
1 files changed, 843 insertions, 0 deletions
diff --git a/src/args.c b/src/args.c
new file mode 100644
index 0000000..a7dd500
--- /dev/null
+++ b/src/args.c
@@ -0,0 +1,843 @@
+/*
+ * Copyright (c) 2016-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "args.h"
+#include "endpoint.h"
+#include "iaddr.h"
+#include "log.h"
+#include "tcpstate.h"
+#include "network.h"
+
+#include <ldns/ldns.h>
+
+/*
+ * OpenBSD and Debian Stretch i386 need file local functions for export
+ * to loaded modules, so use this for all platforms.
+ */
+void* _tcpstate_getcurr(void)
+{
+ return (void*)tcpstate_getcurr();
+}
+
+void _tcpstate_reset(void* tcpstate, const char* msg)
+{
+ tcpstate_reset((tcpstate_ptr)tcpstate, msg);
+}
+
+const char* _ia_str(iaddr ia)
+{
+ return ia_str(ia);
+}
+
+extern struct ip6_hdr* network_ipv6;
+extern struct ip* network_ip;
+extern struct udphdr* network_udp;
+
+void set_iaddr(iaddr* from, iaddr* to)
+{
+ if (from) {
+ switch (from->af) {
+ case AF_INET:
+ if (network_ip) {
+ memcpy(&network_ip->ip_src, &from->u.a4, sizeof(struct in_addr));
+ }
+ break;
+ case AF_INET6:
+ if (network_ipv6) {
+ memcpy(&network_ipv6->ip6_src, &from->u.a6, sizeof(struct in6_addr));
+ }
+ break;
+ default:
+ from = 0;
+ break;
+ }
+ }
+ if (to) {
+ switch (to->af) {
+ case AF_INET:
+ if (network_ip) {
+ memcpy(&network_ip->ip_dst, &to->u.a4, sizeof(struct in_addr));
+ }
+ break;
+ case AF_INET6:
+ if (network_ipv6) {
+ memcpy(&network_ipv6->ip6_dst, &to->u.a6, sizeof(struct in6_addr));
+ }
+ break;
+ default:
+ to = 0;
+ break;
+ }
+ }
+ if (from || to) {
+ if (network_ip) {
+ network_ip->ip_sum = 0;
+ network_ip->ip_sum = ~in_checksum((u_char*)network_ip, sizeof *network_ip);
+ }
+ if (network_udp) {
+ network_udp->uh_sum = 0;
+ }
+ }
+}
+
+#ifdef __linux__
+extern char* strptime(const char*, const char*, struct tm*);
+#endif
+
+time_t xtimegm(struct tm* tmp)
+{
+#if defined(__SVR4) && defined(__sun)
+ char tz[3] = "TZ=";
+ putenv((char*)tz);
+ return mktime(tmp);
+#else
+ return timegm(tmp);
+#endif
+}
+
+void usage(const char* msg)
+{
+ struct plugin* p;
+
+ fprintf(stderr, "%s: usage error: %s\n", ProgramName, msg);
+ fprintf(stderr, "\n");
+
+ help_1();
+
+ for (p = HEAD(plugins); p != NULL; p = NEXT(p, link))
+ if (p->usage)
+ (*p->usage)();
+
+ fprintf(stderr,
+ "\nnote: the -? or -\\? option will display full help text\n");
+
+ exit(1);
+}
+
+void help_1(void)
+{
+ fprintf(stderr, "%s: version %s\n\n", ProgramName, PACKAGE_VERSION);
+ fprintf(stderr,
+ "usage: %s\n"
+ " [-?VbNpd1gfTI"
+#ifdef USE_SECCOMP
+ "y"
+#endif
+ "SMD] [-o option=value]+\n"
+ " [-i <if>]+ [-r <file>]+ [-l <vlan>]+ [-L <vlan>]+\n"
+ " [-u <port>] [-m [qun]] [-e [nytfsxir]] [-h [ir]] [-s [ir]]\n"
+ " [-a <host>]+ [-z <host>]+ [-A <host>]+ [-Z <host>]+ [-Y <host>]+\n"
+ " [-w <base> [-W <suffix>] [-k <cmd>] -F <format>]\n"
+ " [-t <lim>] [-c <lim>] [-C <lim>]\n"
+ " [-x <pat>]+ [-X <pat>]+\n"
+ " [-B <datetime>] [-E <datetime>]\n"
+ " [-U <str>] [-q <num|str>] [-Q <num|str>]\n"
+ " [-P plugin.so <plugin options...>]\n",
+ ProgramName);
+}
+
+void help_2(void)
+{
+ help_1();
+ fprintf(stderr,
+ "\noptions:\n"
+ " -? or -\\? print these instructions and exit\n"
+ " -V print version and exit\n"
+ " -o opt=val extended options, see man page for list of options\n"
+ " -b run in background as daemon\n"
+ " -N do not attempt to drop privileges, this is implicit\n"
+ " if only reading offline pcap files\n"
+ " -p do not put interface in promiscuous mode\n"
+ " -d dump verbose trace information to stderr, specify multiple\n"
+ " times to increase debugging\n"
+ " -1 flush output on every packet\n"
+ " -g dump packets dig-style on stderr\n"
+ " -f include fragmented packets\n"
+ " -T include TCP packets (DNS header filters will inspect only the\n"
+ " first DNS header, and the result will apply to all messages\n"
+ " in the TCP stream; DNS payload filters will not be applied.)\n"
+ " -I include ICMP and ICMPv6 packets\n"
+ " -i <if> select this live interface(s)\n"
+ " -r <file> read this pcap file\n"
+ " -l <vlan> select only these vlan(s) (4095 for all)\n"
+ " -L <vlan> select these vlan(s) and non-VLAN frames (4095 for all)\n"
+ " -u <port> dns port (default: 53)\n"
+ " -m [qun] select messages: query, update, notify\n"
+ " -e [nytfsxir] select error/response code\n"
+ " n = no error\n"
+ " y = any error\n"
+ " t = truncated response\n"
+ " f = format error (rcode 1)\n"
+ " s = server failure (rcode 2)\n"
+ " x = nxdomain (rcode 3)\n"
+ " i = not implemented (rcode 4)\n"
+ " r = refused (rcode 5)\n"
+ " -h [ir] hide initiators and/or responders\n"
+ " -s [ir] select sides: initiations, responses\n"
+ " -a <host> want messages from these initiator(s)\n"
+ " -z <host> want messages from these responder(s)\n"
+ " -A <host> want messages NOT to/from these initiator(s)\n"
+ " -Z <host> want messages NOT to/from these responder(s)\n"
+ " -Y <host> drop responses from these responder(s)\n"
+ " -w <base> dump to <base>.<timesec>.<timeusec>\n"
+ " -W <suffix> add suffix to dump file name, e.g. '.pcap'\n"
+ " -k <cmd> kick off <cmd> when each dump closes\n"
+ " -F <format> dump format: pcap (default), cbor, cds\n"
+ " -t <lim> close dump or exit every/after <lim> secs\n"
+ " -c <lim> close dump or exit every/after <lim> pkts\n"
+ " -C <lim> close dump or exit every/after <lim> bytes captured\n"
+ " -x <pat> select messages matching regex <pat>\n"
+ " -X <pat> select messages not matching regex <pat>\n"
+#ifdef USE_SECCOMP
+ " -y enable seccomp-bpf\n"
+#endif
+ " -S show summarized statistics\n"
+ " -B <datetime> begin collecting at this date and time\n"
+ " -E <datetime> end collecting at this date and time\n"
+ " -M set monitor mode on interfaces\n"
+ " -D set immediate mode on interfaces\n"
+ " -U <str> append 'and <str>' to the pcap filter\n"
+ " -q <num|str> select messages based on QTYPE\n"
+ " -Q <num|str> filter out messages based on QTYPE\n"
+ " -P <plugin.so> load plugin, any argument after this is sent to the plugin!\n");
+}
+
+void check_gzip()
+{
+ char* dot = strrchr(dump_suffix, '.');
+ if (dot) {
+ wantgzip = (strcmp(dot, ".gz") == 0) ? TRUE : FALSE;
+ }
+
+#if !(HAVE_GZOPEN && (HAVE_FUNOPEN || HAVE_FOPENCOOKIE))
+ if (wantgzip) {
+ fprintf(stderr, "error: gzip compression requested but not supported\n");
+ exit(1);
+ }
+#endif
+}
+
+int is_responder(iaddr ia)
+{
+ if (EMPTY(responders))
+ return 1;
+ if (ep_present(&responders, ia))
+ return 1;
+ return 0;
+}
+
+void parse_args(int argc, char* argv[])
+{
+ mypcap_ptr mypcap;
+ unsigned long ul;
+ vlan_ptr vlan;
+ unsigned u;
+ int ch;
+ char * p, *match_qtype_arg = 0;
+
+ if ((p = strrchr(argv[0], '/')) == NULL)
+ ProgramName = argv[0];
+ else
+ ProgramName = p + 1;
+ INIT_LIST(vlans_incl);
+ INIT_LIST(vlans_excl);
+ INIT_LIST(mypcaps);
+ INIT_LIST(initiators);
+ INIT_LIST(responders);
+ INIT_LIST(not_initiators);
+ INIT_LIST(not_responders);
+ INIT_LIST(drop_responders);
+ INIT_LIST(myregexes);
+ INIT_LIST(plugins);
+ while ((ch = getopt(argc, argv,
+ "a:bc:de:fgh:i:k:l:m:o:pr:s:t:u:w:x:yz:q:"
+ "A:B:C:DE:F:IL:MNP:STU:VW:X:Y:Z:Q:1?"))
+ != EOF) {
+ switch (ch) {
+ case 'o':
+ if (option_parse(&options, optarg)) {
+ fprintf(stderr, "%s: unknown or invalid extended -o option: %s\n", ProgramName, optarg);
+ exit(1);
+ }
+ break;
+ case 'b':
+ background = TRUE;
+ break;
+ case 'N':
+ dont_drop_privileges = TRUE;
+ break;
+ case 'p':
+ promisc = FALSE;
+ break;
+ case 'd':
+ dumptrace++;
+ break;
+ case '1':
+ flush = TRUE;
+ break;
+ case 'g':
+ preso = TRUE;
+ break;
+ case 'f':
+ wantfrags = TRUE;
+ break;
+ case 'I':
+ wanticmp = TRUE;
+ break;
+ case 'V':
+ printf("%s version %s\n", ProgramName, PACKAGE_VERSION);
+ exit(0);
+ case 'i':
+ if (pcap_offline != NULL)
+ usage("-i makes no sense after -r");
+ mypcap = calloc(1, sizeof *mypcap);
+ assert(mypcap != NULL);
+ INIT_LINK(mypcap, link);
+ mypcap->name = strdup(optarg);
+ assert(mypcap->name != NULL);
+ APPEND(mypcaps, mypcap, link);
+ break;
+ case 'r':
+ if (!EMPTY(mypcaps))
+ usage("-r makes no sense after -i");
+ pcap_offline = calloc(1, sizeof *pcap_offline);
+ assert(pcap_offline != NULL);
+ INIT_LINK(pcap_offline, link);
+ pcap_offline->name = strdup(optarg);
+ assert(pcap_offline->name != NULL);
+ APPEND(mypcaps, pcap_offline, link);
+ only_offline_pcaps = TRUE;
+ break;
+ case 'l':
+ ul = strtoul(optarg, &p, 0);
+ if (*p != '\0' || ul > MAX_VLAN)
+ usage("-l vlan must be an integer 0..4095");
+ vlan = calloc(1, sizeof *vlan);
+ assert(vlan != NULL);
+ INIT_LINK(vlan, link);
+ vlan->vlan = (unsigned)ul;
+ APPEND(vlans_excl, vlan, link);
+ if (0 == ul)
+ fprintf(stderr, "Warning: previous versions of %s "
+ "interpreted 0 as all VLANs. "
+ "If you want all VLANs now you must "
+ "specify %u.\n",
+ ProgramName, MAX_VLAN);
+ break;
+ case 'L':
+ ul = strtoul(optarg, &p, 0);
+ if (*p != '\0' || ul > MAX_VLAN)
+ usage("-L vlan must be an integer 0..4095");
+ vlan = calloc(1, sizeof *vlan);
+ assert(vlan != NULL);
+ INIT_LINK(vlan, link);
+ vlan->vlan = (unsigned)ul;
+ APPEND(vlans_incl, vlan, link);
+ if (0 == ul)
+ fprintf(stderr, "Warning: previous versions of %s "
+ "interpreted 0 as all VLANs. "
+ "If you want all VLANs now you must "
+ "specify %u.\n",
+ ProgramName, MAX_VLAN);
+ break;
+ case 'T':
+ wanttcp = TRUE;
+ break;
+ case 'u':
+ ul = strtoul(optarg, &p, 0);
+ if (*p != '\0' || ul < 1U || ul > 65535U)
+ usage("port must be an integer 1..65535");
+ dns_port = (unsigned)ul;
+ break;
+ case 'm':
+ u = 0;
+ for (p = optarg; *p; p++)
+ switch (*p) {
+ case 'q':
+ u |= MSG_QUERY;
+ break;
+ case 'u':
+ u |= MSG_UPDATE;
+ break;
+ case 'n':
+ u |= MSG_NOTIFY;
+ break;
+ default:
+ usage("-m takes only [qun]");
+ }
+ msg_wanted = u;
+ break;
+ case 's':
+ u = 0;
+ for (p = optarg; *p; p++)
+ switch (*p) {
+ case 'i':
+ u |= DIR_INITIATE;
+ break;
+ case 'r':
+ u |= DIR_RESPONSE;
+ break;
+ default:
+ usage("-s takes only [ir]");
+ }
+ dir_wanted = u;
+ break;
+ case 'h':
+ u = 0;
+ for (p = optarg; *p; p++)
+ switch (*p) {
+ case 'i':
+ u |= END_INITIATOR;
+ break;
+ case 'r':
+ u |= END_RESPONDER;
+ break;
+ default:
+ usage("-h takes only [ir]");
+ }
+ end_hide = u;
+ break;
+ case 'e':
+ u = 0;
+ for (p = optarg; *p; p++)
+ switch (*p) {
+ case 'n':
+ u |= ERR_NO;
+ break;
+ case 'y':
+ u |= ERR_YES;
+ break;
+ case 't':
+ u |= ERR_TRUNC;
+ break;
+ case 'f':
+ u |= ERR_FORMERR;
+ break;
+ case 's':
+ u |= ERR_SERVFAIL;
+ break;
+ case 'x':
+ u |= ERR_NXDOMAIN;
+ break;
+ case 'i':
+ u |= ERR_NOTIMPL;
+ break;
+ case 'r':
+ u |= ERR_REFUSED;
+ break;
+ default:
+ usage("-e takes only [nytfsxir]");
+ }
+ err_wanted = u;
+ break;
+ case 'a':
+ endpoint_arg(&initiators, optarg);
+ break;
+ case 'z':
+ endpoint_arg(&responders, optarg);
+ break;
+ case 'A':
+ endpoint_arg(&not_initiators, optarg);
+ break;
+ case 'Z':
+ endpoint_arg(&not_responders, optarg);
+ break;
+ case 'Y':
+ endpoint_arg(&drop_responders, optarg);
+ break;
+ case 'w':
+ dump_base = optarg;
+ if (strcmp(optarg, "-") == 0)
+ dump_type = to_stdout;
+ else
+ dump_type = to_file;
+ break;
+ case 'W':
+ if (dump_suffix)
+ free(dump_suffix);
+ dump_suffix = strdup(optarg);
+ check_gzip();
+ break;
+ case 'k':
+ if (dump_type != to_file)
+ usage("-k depends on -w"
+ " (note: can't be stdout)");
+ kick_cmd = optarg;
+ break;
+ case 'F':
+ if (!strcmp(optarg, "pcap")) {
+ options.dump_format = pcap;
+ } else if (!strcmp(optarg, "cbor")) {
+ options.dump_format = cbor;
+ } else if (!strcmp(optarg, "cds")) {
+ options.dump_format = cds;
+ } else {
+ usage("invalid output format for -F");
+ }
+ break;
+ case 't':
+ ul = strtoul(optarg, &p, 0);
+ if (*p != '\0')
+ usage("argument to -t must be an integer");
+ limit_seconds = (unsigned)ul;
+ break;
+ case 'c':
+ ul = strtoul(optarg, &p, 0);
+ if (*p != '\0')
+ usage("argument to -c must be an integer");
+ limit_packets = (unsigned)ul;
+ break;
+ case 'C':
+ ul = strtoul(optarg, &p, 0);
+ if (*p != '\0')
+ usage("argument to -C must be an integer");
+ limit_pcapfilesize = (unsigned)ul;
+ break;
+ case 'x':
+ /* FALLTHROUGH */
+ case 'X': {
+ int i;
+ myregex_ptr myregex = calloc(1, sizeof *myregex);
+ assert(myregex != NULL);
+ INIT_LINK(myregex, link);
+ myregex->str = strdup(optarg);
+ i = regcomp(&myregex->reg, myregex->str, REGEX_CFLAGS);
+ if (i != 0) {
+ regerror(i, &myregex->reg,
+ errbuf, sizeof errbuf);
+ usage(errbuf);
+ }
+ myregex->not = (ch == 'X');
+ APPEND(myregexes, myregex, link);
+ } break;
+ case 'B': {
+ struct tm tm;
+ memset(&tm, '\0', sizeof(tm));
+ if (NULL == strptime(optarg, "%F %T", &tm))
+ usage("-B arg must have format YYYY-MM-DD HH:MM:SS");
+ start_time = xtimegm(&tm);
+ } break;
+ case 'E': {
+ struct tm tm;
+ memset(&tm, '\0', sizeof(tm));
+ if (NULL == strptime(optarg, "%F %T", &tm))
+ usage("-E arg must have format YYYY-MM-DD HH:MM:SS");
+ stop_time = xtimegm(&tm);
+ } break;
+ case 'S':
+ print_pcap_stats = TRUE;
+ break;
+ case 'P': {
+ char* fn = strdup(optarg);
+ char* t;
+ char sn[256];
+ struct plugin* p = calloc(1, sizeof(*p));
+ assert(p != NULL);
+ INIT_LINK(p, link);
+ t = strrchr(fn, '/');
+ p->name = strdup(t ? t + 1 : fn);
+ if ((t = strstr(p->name, ".so")))
+ *t = 0;
+ p->handle = dlopen(fn, RTLD_NOW);
+ if (!p->handle) {
+ logerr("%s: %s", fn, dlerror());
+ exit(1);
+ }
+ snprintf(sn, sizeof(sn), "%s_type", p->name);
+ p->type = dlsym(p->handle, sn);
+ if (p->type) {
+ p->pt = (*p->type)();
+ switch (p->pt) {
+ case plugin_output:
+ case plugin_filter:
+ break;
+ default:
+ logerr("invalid plugin type for plugin '%s'", p->name);
+ exit(1);
+ }
+ } else {
+ p->pt = plugin_output;
+ }
+ snprintf(sn, sizeof(sn), "%s_start", p->name);
+ p->start = dlsym(p->handle, sn);
+ snprintf(sn, sizeof(sn), "%s_stop", p->name);
+ p->stop = dlsym(p->handle, sn);
+ snprintf(sn, sizeof(sn), "%s_open", p->name);
+ p->open = dlsym(p->handle, sn);
+ snprintf(sn, sizeof(sn), "%s_close", p->name);
+ p->close = dlsym(p->handle, sn);
+ snprintf(sn, sizeof(sn), "%s_output", p->name);
+ p->output = dlsym(p->handle, sn);
+ if (p->pt == plugin_output && !p->output) {
+ logerr("%s", dlerror());
+ exit(1);
+ }
+ snprintf(sn, sizeof(sn), "%s_filter", p->name);
+ p->filter = dlsym(p->handle, sn);
+ if (p->pt == plugin_filter && !p->filter) {
+ logerr("%s", dlerror());
+ exit(1);
+ }
+ snprintf(sn, sizeof(sn), "%s_usage", p->name);
+ p->usage = dlsym(p->handle, sn);
+ snprintf(sn, sizeof(sn), "%s_extension", p->name);
+ p->extension = dlsym(p->handle, sn);
+ if (p->extension) {
+ (*p->extension)(DNSCAP_EXT_IS_RESPONDER, (void*)is_responder);
+ (*p->extension)(DNSCAP_EXT_IA_STR, (void*)_ia_str);
+ (*p->extension)(DNSCAP_EXT_TCPSTATE_GETCURR, (void*)_tcpstate_getcurr);
+ (*p->extension)(DNSCAP_EXT_TCPSTATE_RESET, (void*)_tcpstate_reset);
+ (*p->extension)(DNSCAP_EXT_SET_IADDR, (void*)set_iaddr);
+ }
+ snprintf(sn, sizeof(sn), "%s_getopt", p->name);
+ p->getopt = dlsym(p->handle, sn);
+ if (p->getopt)
+ (*p->getopt)(&argc, &argv);
+ APPEND(plugins, p, link);
+ if (dumptrace)
+ fprintf(stderr, "Plugin '%s' loaded\n", p->name);
+ free(fn);
+ } break;
+ case 'U':
+ if (extra_bpf)
+ free(extra_bpf);
+ extra_bpf = strdup(optarg);
+ break;
+ case 'y':
+#ifdef USE_SECCOMP
+ use_seccomp = TRUE;
+ break;
+#else
+ usage("-y: seccomp-bpf not enabled");
+#endif
+ case 'M':
+ monitor_mode = TRUE;
+ break;
+ case 'D':
+ immediate_mode = TRUE;
+ break;
+ case 'q': {
+ if (nmatch_qtype) {
+ usage("-q and -Q can't be used together");
+ }
+ free(match_qtype_arg); // fix clang scan-build
+ match_qtype_arg = strdup(optarg);
+ match_qtype = ldns_get_rr_type_by_name(optarg);
+ if (!match_qtype) {
+ ul = strtoul(optarg, &p, 0);
+ if (*p != '\0' || ul < 1U || ul > 65535U)
+ usage("-q QTYPE must be a valid type or an integer 1..65535");
+ match_qtype = (ldns_rr_type)ul;
+ }
+ break;
+ }
+ case 'Q': {
+ if (match_qtype) {
+ usage("-q and -Q can't be used together");
+ }
+ free(match_qtype_arg); // fix clang scan-build
+ match_qtype_arg = strdup(optarg);
+ nmatch_qtype = ldns_get_rr_type_by_name(optarg);
+ if (!nmatch_qtype) {
+ ul = strtoul(optarg, &p, 0);
+ if (*p != '\0' || ul < 1U || ul > 65535U)
+ usage("-Q QTYPE must be a valid type or an integer 1..65535");
+ nmatch_qtype = (ldns_rr_type)ul;
+ }
+ break;
+ }
+ case '?':
+ if (!optopt || optopt == '?') {
+ help_2();
+ options_free(&options);
+ exit(0);
+ }
+ // fallthrough
+ default:
+ usage("unrecognized command line option");
+ }
+ }
+ assert(msg_wanted != 0U);
+ assert(err_wanted != 0U);
+ if (dump_type != nowhere && options.use_layers)
+ usage("use_layers is only compatible with -g so far");
+ if (dump_type == nowhere && !preso && EMPTY(plugins))
+ usage("without -w or -g, there would be no output");
+ if (end_hide != 0U && wantfrags)
+ usage("the -h and -f options are incompatible");
+ if (!EMPTY(vlans_incl) && !EMPTY(vlans_excl))
+ usage("the -L and -l options are mutually exclusive");
+ if (background && (dumptrace || preso))
+ usage("the -b option is incompatible with -d and -g");
+ if (dumptrace >= 1) {
+ endpoint_ptr ep;
+ const char* sep;
+ myregex_ptr mr;
+
+ fprintf(stderr, "%s: version %s\n", ProgramName, PACKAGE_VERSION);
+ fprintf(stderr,
+ "%s: msg %c%c%c, side %c%c, hide %c%c, err %c%c%c%c%c%c%c%c, t %u, c %u, C %zu, %sq %s\n",
+ ProgramName,
+ (msg_wanted & MSG_QUERY) != 0 ? 'Q' : '.',
+ (msg_wanted & MSG_UPDATE) != 0 ? 'U' : '.',
+ (msg_wanted & MSG_NOTIFY) != 0 ? 'N' : '.',
+ (dir_wanted & DIR_INITIATE) != 0 ? 'I' : '.',
+ (dir_wanted & DIR_RESPONSE) != 0 ? 'R' : '.',
+ (end_hide & END_INITIATOR) != 0 ? 'I' : '.',
+ (end_hide & END_RESPONDER) != 0 ? 'R' : '.',
+ (err_wanted & ERR_NO) != 0 ? 'N' : '.',
+ (err_wanted & ERR_YES) == ERR_YES ? 'Y' : '.',
+ (err_wanted & ERR_TRUNC) != 0 ? 't' : '.',
+ (err_wanted & ERR_FORMERR) != 0 ? 'f' : '.',
+ (err_wanted & ERR_SERVFAIL) != 0 ? 's' : '.',
+ (err_wanted & ERR_NXDOMAIN) != 0 ? 'x' : '.',
+ (err_wanted & ERR_NOTIMPL) != 0 ? 'i' : '.',
+ (err_wanted & ERR_REFUSED) != 0 ? 'r' : '.',
+ limit_seconds, limit_packets, limit_pcapfilesize,
+ nmatch_qtype ? "!" : "", match_qtype_arg);
+ sep = "\tinit";
+ for (ep = HEAD(initiators);
+ ep != NULL;
+ ep = NEXT(ep, link)) {
+ fprintf(stderr, "%s %s", sep, ia_str(ep->ia));
+ sep = "";
+ }
+ if (!EMPTY(initiators))
+ fprintf(stderr, "\n");
+ sep = "\tresp";
+ for (ep = HEAD(responders);
+ ep != NULL;
+ ep = NEXT(ep, link)) {
+ fprintf(stderr, "%s %s", sep, ia_str(ep->ia));
+ sep = "";
+ }
+ if (!EMPTY(responders))
+ fprintf(stderr, "\n");
+ sep = "\t!init";
+ for (ep = HEAD(not_initiators);
+ ep != NULL;
+ ep = NEXT(ep, link)) {
+ fprintf(stderr, "%s %s", sep, ia_str(ep->ia));
+ sep = "";
+ }
+ if (!EMPTY(not_initiators))
+ fprintf(stderr, "\n");
+ sep = "\t!resp";
+ for (ep = HEAD(not_responders);
+ ep != NULL;
+ ep = NEXT(ep, link)) {
+ fprintf(stderr, "%s %s", sep, ia_str(ep->ia));
+ sep = "";
+ }
+ if (!EMPTY(not_responders))
+ fprintf(stderr, "\n");
+ sep = "\t!dropresp";
+ for (ep = HEAD(drop_responders);
+ ep != NULL;
+ ep = NEXT(ep, link)) {
+ fprintf(stderr, "%s %s", sep, ia_str(ep->ia));
+ sep = "";
+ }
+ if (!EMPTY(drop_responders))
+ fprintf(stderr, "\n");
+ if (!EMPTY(myregexes)) {
+ fprintf(stderr, "%s: pat:", ProgramName);
+ for (mr = HEAD(myregexes);
+ mr != NULL;
+ mr = NEXT(mr, link))
+ fprintf(stderr, " %s/%s/",
+ mr->not ? "!" : "", mr->str);
+ fprintf(stderr, "\n");
+ }
+ }
+ if (EMPTY(mypcaps)) {
+ pcap_if_t* pcapdev = 0;
+ int res;
+ res = pcap_findalldevs(&pcapdev, errbuf);
+ if (res == -1) {
+ fprintf(stderr, "%s: pcap_findalldevs: %s\n",
+ ProgramName, errbuf);
+ exit(1);
+ } else if (pcapdev == NULL) {
+ fprintf(stderr, "%s: pcap_findalldevs: no devices found\n",
+ ProgramName);
+ exit(1);
+ }
+ mypcap = calloc(1, sizeof *mypcap);
+ assert(mypcap != NULL);
+ INIT_LINK(mypcap, link);
+ mypcap->name = strdup(pcapdev->name);
+ APPEND(mypcaps, mypcap, link);
+ pcap_freealldevs(pcapdev);
+ }
+ if (start_time && stop_time && start_time >= stop_time)
+ usage("start time must be before stop time");
+
+ if (options.dump_format == cbor) {
+ if (!have_cbor_support()) {
+ usage("no built in cbor support");
+ }
+ cbor_set_size(options.cbor_chunk_size);
+ } else if (options.dump_format == cds) {
+ if (!have_cds_support()) {
+ usage("no built in cds support");
+ }
+ cds_set_cbor_size(options.cds_cbor_size);
+ cds_set_message_size(options.cds_message_size);
+ cds_set_max_rlabels(options.cds_max_rlabels);
+ cds_set_min_rlabel_size(options.cds_min_rlabel_size);
+ if (options.cds_use_rdata_index && options.cds_use_rdata_rindex) {
+ usage("can't use both CDS rdata index and rindex");
+ }
+ cds_set_use_rdata_index(options.cds_use_rdata_index);
+ cds_set_use_rdata_rindex(options.cds_use_rdata_rindex);
+ cds_set_rdata_index_min_size(options.cds_rdata_index_min_size);
+ cds_set_rdata_rindex_min_size(options.cds_rdata_rindex_min_size);
+ cds_set_rdata_rindex_size(options.cds_rdata_rindex_size);
+ }
+
+ if (!options.use_layers && (options.defrag_ipv4 || options.defrag_ipv6)) {
+ usage("can't defragment IP packets without use_layers=yes");
+ }
+
+ if (options.reassemble_tcp_bfbparsedns) {
+ if (!options.reassemble_tcp) {
+ usage("can't do byte for byte parsing of DNS without reassemble_tcp=yes");
+ }
+ }
+
+ free(match_qtype_arg);
+}