/* * Copyright (c) 2016-2024 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 /* * 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 ]+ [-r ]+ [-l ]+ [-L ]+\n" " [-u ] [-m [qun]] [-e [nytfsxir]] [-h [ir]] [-s [ir]]\n" " [-a ]+ [-z ]+ [-A ]+ [-Z ]+ [-Y ]+\n" " [-w [-W ] [-k ] -F ]\n" " [-t ] [-c ] [-C ]\n" " [-x ]+ [-X ]+\n" " [-B ] [-E ]\n" " [-U ] [-q ] [-Q ]\n" " [-P plugin.so ]\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 select this live interface(s)\n" " -r read this pcap file\n" " -l select only these vlan(s) (4095 for all)\n" " -L select these vlan(s) and non-VLAN frames (4095 for all)\n" " -u 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 want messages from these initiator(s)\n" " -z want messages from these responder(s)\n" " -A want messages NOT to/from these initiator(s)\n" " -Z want messages NOT to/from these responder(s)\n" " -Y drop responses from these responder(s)\n" " -w dump to ..\n" " -W add suffix to dump file name, e.g. '.pcap'\n" " -k kick off when each dump closes\n" " -F dump format: pcap (default), cbor, cds\n" " -t close dump or exit every/after secs\n" " -c close dump or exit every/after pkts\n" " -C close dump or exit every/after bytes captured\n" " -x select messages matching regex \n" " -X select messages not matching regex \n" #ifdef USE_SECCOMP " -y enable seccomp-bpf\n" #endif " -S show summarized statistics\n" " -B begin collecting at this date and time\n" " -E end collecting at this date and time\n" " -M set monitor mode on interfaces\n" " -D set immediate mode on interfaces\n" " -U append 'and ' to the pcap filter\n" " -q select messages based on QTYPE\n" " -Q filter out messages based on QTYPE\n" " -P 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(¬_initiators, optarg); break; case 'Z': endpoint_arg(¬_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* pl = calloc(1, sizeof(*pl)); assert(pl != NULL); INIT_LINK(pl, link); t = strrchr(fn, '/'); pl->name = strdup(t ? t + 1 : fn); if ((t = strstr(pl->name, ".so"))) *t = 0; pl->handle = dlopen(fn, RTLD_NOW); if (!pl->handle) { logerr("%s: %s", fn, dlerror()); exit(1); } snprintf(sn, sizeof(sn), "%s_type", pl->name); pl->type = dlsym(pl->handle, sn); if (pl->type) { pl->pt = (*pl->type)(); switch (pl->pt) { case plugin_output: case plugin_filter: break; default: logerr("invalid plugin type for plugin '%s'", pl->name); exit(1); } } else { pl->pt = plugin_output; } snprintf(sn, sizeof(sn), "%s_start", pl->name); pl->start = dlsym(pl->handle, sn); snprintf(sn, sizeof(sn), "%s_stop", pl->name); pl->stop = dlsym(pl->handle, sn); snprintf(sn, sizeof(sn), "%s_open", pl->name); pl->open = dlsym(pl->handle, sn); snprintf(sn, sizeof(sn), "%s_close", pl->name); pl->close = dlsym(pl->handle, sn); snprintf(sn, sizeof(sn), "%s_output", pl->name); pl->output = dlsym(pl->handle, sn); if (pl->pt == plugin_output && !pl->output) { logerr("%s", dlerror()); exit(1); } snprintf(sn, sizeof(sn), "%s_filter", pl->name); pl->filter = dlsym(pl->handle, sn); if (pl->pt == plugin_filter && !pl->filter) { logerr("%s", dlerror()); exit(1); } snprintf(sn, sizeof(sn), "%s_usage", pl->name); pl->usage = dlsym(pl->handle, sn); snprintf(sn, sizeof(sn), "%s_extension", pl->name); pl->extension = dlsym(pl->handle, sn); if (pl->extension) { (*pl->extension)(DNSCAP_EXT_IS_RESPONDER, (void*)is_responder); (*pl->extension)(DNSCAP_EXT_IA_STR, (void*)_ia_str); (*pl->extension)(DNSCAP_EXT_TCPSTATE_GETCURR, (void*)_tcpstate_getcurr); (*pl->extension)(DNSCAP_EXT_TCPSTATE_RESET, (void*)_tcpstate_reset); (*pl->extension)(DNSCAP_EXT_SET_IADDR, (void*)set_iaddr); } snprintf(sn, sizeof(sn), "%s_getopt", pl->name); pl->getopt = dlsym(pl->handle, sn); if (pl->getopt) (*pl->getopt)(&argc, &argv); APPEND(plugins, pl, link); if (dumptrace) fprintf(stderr, "Plugin '%s' loaded\n", pl->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); }