/* * Copyright (c) 2016-2022, 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 #include #include #include #include #include #include #include #include #include #include #include "dnscap_common.h" static logerr_t* logerr; static char* opt_o = 0; static int opt_s = 0; static FILE* out = 0; output_t txtout_output; void txtout_usage() { fprintf(stderr, "\ntxtout.so options:\n" "\t-? print these instructions and exit\n" "\t-o output file name\n" "\t-s short output, only QTYPE/QNAME for IN\n"); } void txtout_getopt(int* argc, char** argv[]) { /* * The "getopt" function will be called from the parent to * process plugin options. */ int c; while ((c = getopt(*argc, *argv, "?so:")) != EOF) { switch (c) { case 'o': if (opt_o) free(opt_o); opt_o = strdup(optarg); break; case 's': opt_s = 1; break; case '?': txtout_usage(); if (!optopt || optopt == '?') { exit(0); } // fallthrough default: exit(1); } } } int txtout_start(logerr_t* a_logerr) { /* * The "start" function is called once, when the program * starts. It is used to initialize the plugin. If the * plugin wants to write debugging and or error messages, * it should save the a_logerr pointer passed from the * parent code. */ logerr = a_logerr; if (opt_o) { out = fopen(opt_o, "w"); if (0 == out) { logerr("%s: %s\n", opt_o, strerror(errno)); exit(1); } } else { out = stdout; } setbuf(out, 0); return 0; } void txtout_stop() { /* * The "start" function is called once, when the program * is exiting normally. It might be used to clean up state, * free memory, etc. */ if (out != stdout) fclose(out); } int txtout_open(my_bpftimeval ts) { /* * The "open" function is called at the start of each * collection interval, which might be based on a period * of time or a number of packets. In the original code, * this is where we opened an output pcap file. */ return 0; } int txtout_close(my_bpftimeval ts) { /* * The "close" function is called at the end of each * collection interval, which might be based on a period * of time or on a number of packets. In the original code * this is where we closed an output pcap file. */ return 0; } ia_str_t ia_str = 0; tcpstate_getcurr_t tcpstate_getcurr = 0; tcpstate_reset_t tcpstate_reset = 0; void txtout_extension(int ext, void* arg) { switch (ext) { case DNSCAP_EXT_IA_STR: ia_str = (ia_str_t)arg; break; case DNSCAP_EXT_TCPSTATE_GETCURR: tcpstate_getcurr = (tcpstate_getcurr_t)arg; break; case DNSCAP_EXT_TCPSTATE_RESET: tcpstate_reset = (tcpstate_reset_t)arg; break; } } void txtout_output(const char* descr, iaddr from, iaddr to, uint8_t proto, unsigned flags, unsigned sport, unsigned dport, my_bpftimeval ts, const u_char* pkt_copy, unsigned olen, const u_char* payload, unsigned payloadlen) { /* * Short output, only print QTYPE and QNAME for IN records */ if (opt_s) { if (flags & DNSCAP_OUTPUT_ISDNS) { ldns_pkt* pkt; if (ldns_wire2pkt(&pkt, payload, payloadlen) != LDNS_STATUS_OK) { if (tcpstate_getcurr && tcpstate_reset) tcpstate_reset(tcpstate_getcurr(), ""); return; } ldns_rr_list* qds = ldns_pkt_question(pkt); if (qds) { ldns_rr* qd = ldns_rr_list_rr(qds, 0); if (qd && ldns_rr_get_class(qd) == LDNS_RR_CLASS_IN) { ldns_buffer* buf = ldns_buffer_new(512); if (!buf) { logerr("out of memmory\n"); exit(1); } if (ldns_rr_type2buffer_str(buf, ldns_rr_get_type(qd)) == LDNS_STATUS_OK) { fprintf(out, "%s", (char*)ldns_buffer_begin(buf)); } else { fprintf(out, "ERR"); } ldns_buffer_clear(buf); if (ldns_rdf2buffer_str(buf, ldns_rr_owner(qd)) == LDNS_STATUS_OK) { fprintf(out, " %s\n", (char*)ldns_buffer_begin(buf)); } else { fprintf(out, "ERR\n"); } ldns_buffer_free(buf); } } ldns_pkt_free(pkt); } return; } /* * IP Stuff */ fprintf(out, "%10ld.%06ld", (long)ts.tv_sec, (long)ts.tv_usec); fprintf(out, " %s %u", ia_str(from), sport); fprintf(out, " %s %u", ia_str(to), dport); fprintf(out, " %hhu", proto); if (flags & DNSCAP_OUTPUT_ISDNS) { ldns_pkt* pkt; if (ldns_wire2pkt(&pkt, payload, payloadlen) != LDNS_STATUS_OK) { if (tcpstate_getcurr && tcpstate_reset) tcpstate_reset(tcpstate_getcurr(), ""); fprintf(out, "\n"); return; } /* * DNS Header */ fprintf(out, " %u", ldns_pkt_id(pkt)); fprintf(out, " %u", ldns_pkt_get_opcode(pkt)); fprintf(out, " %u", ldns_pkt_get_rcode(pkt)); fprintf(out, " |"); if (ldns_pkt_qr(pkt)) fprintf(out, "QR|"); if (ldns_pkt_aa(pkt)) fprintf(out, "AA|"); if (ldns_pkt_tc(pkt)) fprintf(out, "TC|"); if (ldns_pkt_rd(pkt)) fprintf(out, "RD|"); if (ldns_pkt_ra(pkt)) fprintf(out, "RA|"); if (ldns_pkt_ad(pkt)) fprintf(out, "AD|"); if (ldns_pkt_cd(pkt)) fprintf(out, "CD|"); ldns_rr_list* qds = ldns_pkt_question(pkt); if (qds) { ldns_rr* qd = ldns_rr_list_rr(qds, 0); if (qd) { ldns_buffer* buf = ldns_buffer_new(512); if (!buf) { logerr("out of memmory\n"); exit(1); } if (ldns_rr_class2buffer_str(buf, ldns_rr_get_class(qd)) == LDNS_STATUS_OK) { fprintf(out, " %s", (char*)ldns_buffer_begin(buf)); } else { fprintf(out, " ERR"); } ldns_buffer_clear(buf); if (ldns_rr_type2buffer_str(buf, ldns_rr_get_type(qd)) == LDNS_STATUS_OK) { fprintf(out, " %s", (char*)ldns_buffer_begin(buf)); } else { fprintf(out, " ERR"); } ldns_buffer_clear(buf); if (ldns_rdf2buffer_str(buf, ldns_rr_owner(qd)) == LDNS_STATUS_OK) { fprintf(out, " %s", (char*)ldns_buffer_begin(buf)); } else { fprintf(out, "ERR"); } ldns_buffer_free(buf); } } ldns_pkt_free(pkt); } /* * Done */ fprintf(out, "\n"); }