const char * hostname_from_question(ns_msg msg) { static char hostname[256] = {0}; ns_rr rr; int rrnum, rrmax; const char *result; int result_len; rrmax = ns_msg_count(msg, ns_s_qd); if (rrmax == 0) return NULL; for (rrnum = 0; rrnum < rrmax; rrnum++) { if (local_ns_parserr(&msg, ns_s_qd, rrnum, &rr)) { logger_log(LOG_ERR, "local_ns_parserr"); return NULL; } result = ns_rr_name(rr); result_len = strlen(result) + 1; if (result_len > sizeof(hostname)) { logger_log(LOG_ERR, "hostname too long: %s", result); } memset(hostname, 0, sizeof(hostname)); memcpy(hostname, result, result_len); return hostname; } return NULL; }
static int print_axfr(FILE *file, const u_char *msg, size_t msglen) { ns_msg handle; if (ns_initparse(msg, msglen, &handle) < 0) { fprintf(file, ";; ns_initparse: %s\n", strerror(errno)); return (ns_r_formerr); } if (ns_msg_getflag(handle, ns_f_rcode) != ns_r_noerror) return (ns_msg_getflag(handle, ns_f_rcode)); /* * We are looking for info from answer resource records. * If there aren't any, return with an error. We assume * there aren't any question records. */ if (ns_msg_count(handle, ns_s_an) == 0) return (NO_INFO); #ifdef PROTOCOLDEBUG printf(";;; (message of %d octets has %d answers)\n", msglen, ns_msg_count(handle, ns_s_an)); #endif for (;;) { static char origin[NS_MAXDNAME], name_ctx[NS_MAXDNAME]; const char *name; char buf[2048]; /* XXX need to malloc/realloc. */ ns_rr rr; if (ns_parserr(&handle, ns_s_an, -1, &rr)) { if (errno != ENODEV) { fprintf(file, ";; ns_parserr: %s\n", strerror(errno)); return (FORMERR); } break; } name = ns_rr_name(rr); if (origin[0] == '\0' && name[0] != '\0') { if (strcmp(name, ".") != 0) strcpy(origin, name); fprintf(file, "$ORIGIN %s.\n", origin); if (strcmp(name, ".") == 0) strcpy(origin, name); if (res.pfcode & RES_PRF_TRUNC) strcpy(name_ctx, "@"); } if (ns_sprintrr(&handle, &rr, (res.pfcode & RES_PRF_TRUNC) ? name_ctx : NULL, (res.pfcode & RES_PRF_TRUNC) ? origin : NULL, buf, sizeof buf) < 0) { fprintf(file, ";; ns_sprintrr: %s\n", strerror(errno)); return (FORMERR); } strcpy(name_ctx, name); fputs(buf, file); fputc('\n', file); } return (SUCCESS); }
/*% * Convert an RR to presentation format. * * return: *\li Number of characters written to buf, or -1 (check errno). */ int ns_sprintrr(const ns_msg* handle, const ns_rr* rr, const char* name_ctx, const char* origin, char* buf, size_t buflen) { int n; n = ns_sprintrrf(ns_msg_base(*handle), ns_msg_size(*handle), ns_rr_name(*rr), ns_rr_class(*rr), ns_rr_type(*rr), ns_rr_ttl(*rr), ns_rr_rdata(*rr), ns_rr_rdlen(*rr), name_ctx, origin, buf, buflen); return (n); }
static void do_section(ns_msg *handle, ns_sect section, int pflag, FILE *file) { int n, sflag, rrnum; ns_opcode opcode; ns_rr rr; /* * Print answer records. */ sflag = (int)(_res.pfcode & pflag); if (_res.pfcode && !sflag) return; opcode = ns_msg_getflag(*handle, ns_f_opcode); rrnum = 0; for (;;) { if (ns_parserr(handle, section, rrnum, &rr)) { if (errno != ENODEV) fprintf(file, ";; ns_parserr: %s\n", strerror(errno)); else if (rrnum > 0 && sflag != 0 && (_res.pfcode & RES_PRF_HEAD1)) putc('\n', file); return; } if (rrnum == 0 && sflag != 0 && (_res.pfcode & RES_PRF_HEAD1)) fprintf(file, ";; %s SECTION:\n", p_section(section, opcode)); if (section == ns_s_qd) fprintf(file, ";;\t%s, type = %s, class = %s\n", ns_rr_name(rr), p_type(ns_rr_type(rr)), p_class(ns_rr_class(rr))); else { char *buf; buf = (char*)malloc(2024); if (buf) { n = ns_sprintrr(handle, &rr, NULL, NULL, buf, sizeof buf); if (n < 0) { fprintf(file, ";; ns_sprintrr: %s\n", strerror(errno)); free(buf); return; } fputs(buf, file); fputc('\n', file); free(buf); } } rrnum++; } }
static void do_section(const res_state statp, ns_msg *handle, ns_sect section, int pflag, FILE *file) { int n, sflag, rrnum; int buflen = 2048; char *buf; ns_opcode opcode; ns_rr rr; /* * Print answer records. */ sflag = (statp->pfcode & pflag); if (statp->pfcode && !sflag) return; buf = malloc((size_t)buflen); if (buf == NULL) { fprintf(file, ";; memory allocation failure\n"); return; } opcode = (ns_opcode) ns_msg_getflag(*handle, ns_f_opcode); rrnum = 0; for (;;) { if (ns_parserr(handle, section, rrnum, &rr)) { if (errno != ENODEV) fprintf(file, ";; ns_parserr: %s\n", strerror(errno)); else if (rrnum > 0 && sflag != 0 && (statp->pfcode & RES_PRF_HEAD1)) putc('\n', file); goto cleanup; } if (rrnum == 0 && sflag != 0 && (statp->pfcode & RES_PRF_HEAD1)) fprintf(file, ";; %s SECTION:\n", p_section(section, opcode)); if (section == ns_s_qd) fprintf(file, ";;\t%s, type = %s, class = %s\n", ns_rr_name(rr), p_type(ns_rr_type(rr)), p_class(ns_rr_class(rr))); else if (section == ns_s_ar && ns_rr_type(rr) == ns_t_opt) { u_int32_t ttl = ns_rr_ttl(rr); fprintf(file, "; EDNS: version: %u, udp=%u, flags=%04x\n", (ttl>>16)&0xff, ns_rr_class(rr), ttl&0xffff); } else {
static const char *hostname_from_question(ns_msg msg) { ns_rr rr; int rrnum, rrmax; const char *result; int result_len; rrmax = ns_msg_count(msg, ns_s_qd); if (rrmax == 0) return NULL; for (rrnum = 0; rrnum < rrmax; rrnum++) { if (ns_parserr(&msg, ns_s_qd, rrnum, &rr)) { ERR("ns_parserr"); return NULL; } result = ns_rr_name(rr); result_len = strlen(result) + 1; if (result_len > hostname_buflen) { hostname_buflen = result_len << 1; hostname_buf = realloc(hostname_buf, hostname_buflen); } memcpy(hostname_buf, result, result_len); return hostname_buf; } return NULL; }
static JSBool parse_section(JSContext *cx, JSObject *sobj, ns_msg *hdl, ns_sect sect) { int rrnum, i; ns_rr rr; JSObject *robj; JSString *name; for (rrnum = 0; rrnum < ns_msg_count(*hdl, sect); rrnum++) { if (ns_parserr(hdl, sect, rrnum, &rr) < 0) return JS_RetError(cx, "ns_parserr: %s", strerror(errno)); robj = JS_NewObject(cx, NULL, NULL, NULL); if (!robj) return JS_RetErrno(cx, ENOMEM); if (!JS_DefineElement(cx, sobj, rrnum, OBJECT_TO_JSVAL(robj), NULL, NULL, JSPROP_ENUMERATE)) return JS_RetErrno(cx, ENOMEM); name = JS_NewStringCopyZ(cx, ns_rr_name(rr)); if (!name || !JS_DefineProperty(cx, robj, "name", STRING_TO_JSVAL(name), NULL, NULL, JSPROP_ENUMERATE)) return JS_RetErrno(cx, ENOMEM); if (!JS_DefineProperty(cx, robj, "type", INT_TO_JSVAL(ns_rr_type(rr)), NULL, NULL, JSPROP_ENUMERATE)) return JS_RetErrno(cx, ENOMEM); for (i = 0; i < ARRAY_SIZE(pmap); i++) { if (pmap[i].type != ns_rr_type(rr)) continue; if (!pmap[i].func(cx, robj, hdl, &rr)) return JS_FALSE; break; } } return JS_TRUE; }
void dowse_output(const char *descr, iaddr from, iaddr to, uint8_t proto, int isfrag, unsigned sport, unsigned dport, my_bpftimeval ts, const u_char *pkt_copy, unsigned olen, const u_char *dnspkt, unsigned dnslen) { /* dnspkt may be NULL if IP packet does not contain a valid DNS message */ char output[MAX_OUTPUT]; if (dnspkt) { ns_msg msg; int qdcount; ns_rr rr; int *val; char *sval; char *extracted; char *resolved; char *from; int res; char action = 'A'; char from_color[16]; ns_initparse(dnspkt, dnslen, &msg); if (!ns_msg_getflag(msg, ns_f_qr)) return; /* * -- flags -- * 0 1 5 6 7 8 11 15 * +----+----------------+----+----+----+-----------+----------------+ * | QR | Operation Code | AA | TC | RA | Zero | Recode | * +----+----------------+----+----+----+-----------+----------------+ * * Question/Response : ns_f_qr * Operation code : ns_f_opcode * Authoritative Answer : ns_f_aa * Truncation occurred : ns_f_tc * Recursion Desired : ns_f_rd * Recursion Available : ns_f_ra * MBZ : ns_f_z * Authentic Data (DNSSEC) : ns_f_ad * Checking Disabled (DNSSEC) : ns_f_cd * Response code : ns_f_rcode */ // logerr("msg: %p",msg); qdcount = ns_msg_count(msg, ns_s_qd); if (qdcount > 0 && 0 == ns_parserr(&msg, ns_s_qd, 0, &rr)) { // where the query comes from from = ia_resolv(to); // if its from ourselves omit it if(strncmp(from,hostname,MAX_DOMAIN)==0) return; if(own_ipv4) if(strncmp(from,own_ipv4,MAX_DOMAIN)==0) return; // not reverse resolved means not known by Dowse, code RED if(is_ip(from)) strcpy(from_color,"#FF0000"); resolved = ns_rr_name(rr); // what domain is being looked up extracted = extract_domain(resolved); res = hashmap_get(visited, extracted, (void**)(&val)); switch(res) { case MAP_MISSING : // never visited val = malloc(sizeof(int)); *val = 1; // just a placeholder for now res = hashmap_put(visited, strdup(extracted), val); break; case MAP_OK: // already visited action = 'M'; break; // TODO error checks case MAP_FULL: case MAP_OMEM: break; } // compose the path of the detected query // add category if listed if(listpath) { // add known domain list information res = hashmap_get(domainlist, extracted, (void**)(&sval)); switch(res) { case MAP_OK: /* render with the category in front of domain */ snprintf(output,MAX_OUTPUT,"%lu|%s|%c|%s/%s/%s", ts.tv_sec, from, action, tld, sval, extracted); break; default: /* render only the domain in root category */ snprintf(output,MAX_OUTPUT,"%lu|%s|%c|%s/%s", ts.tv_sec, from, action, tld, extracted); break; } } else /* render only the domain in root category */ snprintf(output,MAX_OUTPUT,"%lu|%s|%c|%s/%s", ts.tv_sec, from, action, tld, extracted); /* write to file */ if(fileout) { fputs(output, fileout); fputc('\n',fileout); if(fileout) fflush(fileout); } /* print fast on console for realtime */ if(console) { puts(output); fflush(stdout); } } } }
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) { ns_msg msg; int qdcount, err = 0; ns_rr rr; if (ns_initparse(payload, payloadlen, &msg) < 0) { if (tcpstate_getcurr && tcpstate_reset) tcpstate_reset(tcpstate_getcurr(), ""); return; } qdcount = ns_msg_count(msg, ns_s_qd); if (qdcount > 0 && 0 == (err = ns_parserr(&msg, ns_s_qd, 0, &rr)) && ns_rr_class(rr) == 1) { fprintf(out, "%s %s\n", p_type(ns_rr_type(rr)), ns_rr_name(rr)); } if (err < 0) { if (tcpstate_getcurr && tcpstate_reset) tcpstate_reset(tcpstate_getcurr(), ""); } } 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) { ns_msg msg; int qdcount, err = 0; ns_rr rr; if (ns_initparse(payload, payloadlen, &msg) < 0) { if (tcpstate_getcurr && tcpstate_reset) tcpstate_reset(tcpstate_getcurr(), ""); fprintf(out, "\n"); return; } /* * DNS Header */ fprintf(out, " %u", ns_msg_id(msg)); fprintf(out, " %u", ns_msg_getflag(msg, ns_f_opcode)); fprintf(out, " %u", ns_msg_getflag(msg, ns_f_rcode)); fprintf(out, " |"); if (ns_msg_getflag(msg, ns_f_qr)) fprintf(out, "QR|"); if (ns_msg_getflag(msg, ns_f_aa)) fprintf(out, "AA|"); if (ns_msg_getflag(msg, ns_f_tc)) fprintf(out, "TC|"); if (ns_msg_getflag(msg, ns_f_rd)) fprintf(out, "RD|"); if (ns_msg_getflag(msg, ns_f_ra)) fprintf(out, "RA|"); if (ns_msg_getflag(msg, ns_f_ad)) fprintf(out, "AD|"); if (ns_msg_getflag(msg, ns_f_cd)) fprintf(out, "CD|"); qdcount = ns_msg_count(msg, ns_s_qd); if (qdcount > 0 && 0 == (err = ns_parserr(&msg, ns_s_qd, 0, &rr))) { fprintf(out, " %s %s %s", p_class(ns_rr_class(rr)), p_type(ns_rr_type(rr)), ns_rr_name(rr)); } if (err < 0) { if (tcpstate_getcurr && tcpstate_reset) tcpstate_reset(tcpstate_getcurr(), ""); } } /* * Done */ fprintf(out, "\n"); }
static void do_section(const res_state statp, ns_msg *handle, ns_sect section, int pflag, FILE *file) { int n, sflag, rrnum; static int buflen = 2048; char *buf; ns_opcode opcode; ns_rr rr; /* * Print answer records. */ sflag = (statp->pfcode & pflag); if (statp->pfcode && !sflag) return; buf = malloc(buflen); if (buf == NULL) { fprintf(file, ";; memory allocation failure\n"); return; } opcode = (ns_opcode) ns_msg_getflag(*handle, ns_f_opcode); rrnum = 0; for (;;) { if (ns_parserr(handle, section, rrnum, &rr)) { if (errno != ENODEV) fprintf(file, ";; ns_parserr: %s\n", strerror(errno)); else if (rrnum > 0 && sflag != 0 && (statp->pfcode & RES_PRF_HEAD1)) putc('\n', file); goto cleanup; } if (rrnum == 0 && sflag != 0 && (statp->pfcode & RES_PRF_HEAD1)) fprintf(file, ";; %s SECTION:\n", p_section(section, opcode)); if (section == ns_s_qd) fprintf(file, ";;\t%s, type = %s, class = %s\n", ns_rr_name(rr), p_type(ns_rr_type(rr)), p_class(ns_rr_class(rr))); else if (section == ns_s_ar && ns_rr_type(rr) == ns_t_opt) { u_int16_t optcode, optlen, rdatalen = ns_rr_rdlen(rr); u_int32_t ttl = ns_rr_ttl(rr); fprintf(file, "; EDNS: version: %u, udp=%u, flags=%04x\n", (ttl>>16)&0xff, ns_rr_class(rr), ttl&0xffff); while (rdatalen >= 4) { const u_char *cp = ns_rr_rdata(rr); int i; GETSHORT(optcode, cp); GETSHORT(optlen, cp); if (optcode == NS_OPT_NSID) { fputs("; NSID: ", file); if (optlen == 0) { fputs("; NSID\n", file); } else { fputs("; NSID: ", file); for (i = 0; i < optlen; i++) fprintf(file, "%02x ", cp[i]); fputs(" (",file); for (i = 0; i < optlen; i++) fprintf(file, "%c", isprint(cp[i])? cp[i] : '.'); fputs(")\n", file); } } else { if (optlen == 0) { fprintf(file, "; OPT=%u\n", optcode); } else { fprintf(file, "; OPT=%u: ", optcode); for (i = 0; i < optlen; i++) fprintf(file, "%02x ", cp[i]); fputs(" (",file); for (i = 0; i < optlen; i++) fprintf(file, "%c", isprint(cp[i]) ? cp[i] : '.'); fputs(")\n", file); } } rdatalen -= 4 + optlen; } } else {
static void do_section (int pfcode, ns_msg *handle, ns_sect section, int pflag, FILE *file) { int n, sflag, rrnum; static int buflen = 2048; char *buf; ns_opcode opcode; ns_rr rr; /* * Print answer records. */ sflag = (pfcode & pflag); if (pfcode && !sflag) return; buf = malloc(buflen); if (buf == NULL) { fprintf(file, ";; memory allocation failure\n"); return; } opcode = (ns_opcode) ns_msg_getflag(*handle, ns_f_opcode); rrnum = 0; for (;;) { if (ns_parserr(handle, section, rrnum, &rr)) { if (errno != ENODEV) fprintf(file, ";; ns_parserr: %s\n", strerror(errno)); else if (rrnum > 0 && sflag != 0 && (pfcode & RES_PRF_HEAD1)) putc('\n', file); goto cleanup; } if (rrnum == 0 && sflag != 0 && (pfcode & RES_PRF_HEAD1)) fprintf(file, ";; %s SECTION:\n", p_section(section, opcode)); if (section == ns_s_qd) fprintf(file, ";;\t%s, type = %s, class = %s\n", ns_rr_name(rr), p_type(ns_rr_type(rr)), p_class(ns_rr_class(rr))); else { n = ns_sprintrr(handle, &rr, NULL, NULL, buf, buflen); if (n < 0) { if (errno == ENOSPC) { free(buf); buf = NULL; if (buflen < 131072) buf = malloc(buflen += 1024); if (buf == NULL) { fprintf(file, ";; memory allocation failure\n"); return; } continue; } fprintf(file, ";; ns_sprintrr: %s\n", strerror(errno)); goto cleanup; } fputs(buf, file); fputc('\n', file); } rrnum++; } cleanup: free(buf); }
static void test_res_fake_a_via_cname(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; ns_msg handle; ns_rr rr; /* expanded resource record */ const uint8_t *rrdata; char cname[MAXDNAME]; char addr[INET_ADDRSTRLEN]; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); /* Query for A record, but the key is a CNAME. The expected result is * that the whole chain of CNAMEs will be included in the answer section * along with the resulting A */ rv = res_nquery(&dnsstate, "rwrap.org", ns_c_in, ns_t_a, answer, sizeof(answer)); assert_in_range(rv, 1, 256); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have three answers and the answers * must be a parseable RR of type CNAME and have the cname as in the * fake hosts file */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 3); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_cname); rrdata = ns_rr_rdata(rr); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, cname, MAXDNAME); assert_int_not_equal(rv, -1); assert_string_equal(cname, "web.cwrap.org"); assert_int_equal(ns_parserr(&handle, ns_s_an, 1, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_cname); rrdata = ns_rr_rdata(rr); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, cname, MAXDNAME); assert_int_not_equal(rv, -1); assert_string_equal(cname, "www.cwrap.org"); assert_int_equal(ns_parserr(&handle, ns_s_an, 2, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_string_equal(ns_rr_name(rr), "www.cwrap.org"); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "127.0.0.22"); }
static void test_res_fake_cname_query(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; ns_msg handle; ns_rr rr; /* expanded resource record */ const uint8_t *rrdata; char cname[MAXDNAME]; char addr[INET_ADDRSTRLEN]; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "rwrap.org", ns_c_in, ns_t_cname, answer, sizeof(answer)); assert_in_range(rv, 1, 256); ns_initparse(answer, 256, &handle); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type CNAME and have the cname as in the * fake hosts file */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_cname); rrdata = ns_rr_rdata(rr); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, cname, MAXDNAME); assert_int_not_equal(rv, -1); assert_string_equal(cname, "web.cwrap.org"); /* The CNAME points to an A record that's present in the additional * section */ assert_int_equal(ns_msg_count(handle, ns_s_ar), 2); assert_int_equal(ns_parserr(&handle, ns_s_ar, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_cname); assert_string_equal(ns_rr_name(rr), "web.cwrap.org"); rrdata = ns_rr_rdata(rr); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, cname, MAXDNAME); assert_int_not_equal(rv, -1); assert_string_equal(cname, "www.cwrap.org"); assert_int_equal(ns_parserr(&handle, ns_s_ar, 1, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_string_equal(ns_rr_name(rr), "www.cwrap.org"); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "127.0.0.22"); }
/* * Test the case of a SRV record query where the * fake hosts file entry is minimal in the sense * that it omits the priority and weight entries. * The server then fills in some default values. */ static void test_res_fake_srv_query_minimal(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; ns_msg handle; ns_rr rr; /* expanded resource record */ const uint8_t *rrdata; int prio; int weight; int port; char hostname[MAXDNAME]; char addr[INET_ADDRSTRLEN]; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "_krb5._tcp.cwrap.org", ns_c_in, ns_t_srv, answer, sizeof(answer)); assert_in_range(rv, 1, 256); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type SRV and have the priority, weight, * port and hostname as in the fake hosts file */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_srv); rrdata = ns_rr_rdata(rr); NS_GET16(prio, rrdata); NS_GET16(weight, rrdata); NS_GET16(port, rrdata); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, hostname, MAXDNAME); assert_int_not_equal(rv, -1); assert_int_equal(prio, 1); assert_int_equal(weight, 100); assert_int_equal(port, 88); assert_string_equal(hostname, "krb5.cwrap.org"); /* The additional section contains the A record of krb5.cwrap.org */ assert_int_equal(ns_msg_count(handle, ns_s_ar), 1); assert_int_equal(ns_parserr(&handle, ns_s_ar, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_string_equal(ns_rr_name(rr), "krb5.cwrap.org"); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "127.0.0.23"); }
/** * create_naptr_record() */ static NAPTR_record *create_naptr_record(const ns_msg *handle, const ns_rr *rr) { size_t rdchars; const u_char *rdata, *edata; const u_char *message = ns_msg_base(*handle); size_t message_len = ns_msg_size(*handle); char buf[NS_MAXDNAME]; /* defined in nameser.h */ NAPTR_record *naptr_record = malloc(sizeof(NAPTR_record)); memset(naptr_record,0,sizeof(NAPTR_record)); /* ** Domain field */ naptr_record->domain = strdup(ns_rr_name(*rr)); /* ** TTL field */ naptr_record->ttl = ns_rr_ttl(*rr); /* ** Copy from rdata ** (borrowed from BIND-8.2.2 ns_print.c) */ rdata = ns_rr_rdata(*rr); edata = rdata+ns_rr_rdlen(*rr); /* ** Order & preference field */ naptr_record->order = ns_get16(rdata); rdata += NS_INT16SZ; naptr_record->preference = ns_get16(rdata); rdata += NS_INT16SZ; /* ** Flags field */ if((naptr_record->flags = get_rdata_str(rdata,edata,&rdchars))==NULL) { free_naptr_record(naptr_record); return NULL; } rdata += rdchars; /* ** Service field */ if((naptr_record->service = get_rdata_str(rdata,edata,&rdchars))==NULL) { free_naptr_record(naptr_record); return NULL; } rdata += rdchars; /* ** RegExp field */ if((naptr_record->regexp = get_rdata_str(rdata,edata,&rdchars))==NULL) { free_naptr_record(naptr_record); return NULL; } rdata += rdchars; /* ** Replacement field ** Note: dn_expand() sets the first character to '\0' for the root, ** we are going to set it back to '.' */ if(dn_expand(message,message+message_len,rdata,buf,NS_MAXDNAME)==-1) { free_naptr_record(naptr_record); return NULL; } if(buf[0]=='\0') { buf[0]='.'; buf[1]='\0'; } naptr_record->replacement = strdup(buf); return naptr_record; }
static void dump_dns_rr(ns_msg *msg, ns_rr *rr, ns_sect sect, FILE *trace) { char buf[NS_MAXDNAME]; u_int class, type; const u_char *rd; u_int32_t soa[5]; u_int16_t mx; int n; memset(buf, 0, sizeof(buf)); class = ns_rr_class(*rr); type = ns_rr_type(*rr); fprintf(trace, "%s,%s,%s", ns_rr_name(*rr), p_class(class), p_type(type)); if (sect == ns_s_qd) return; fprintf(trace, ",%lu", (u_long)ns_rr_ttl(*rr)); rd = ns_rr_rdata(*rr); switch (type) { case ns_t_soa: n = ns_name_uncompress(ns_msg_base(*msg), ns_msg_end(*msg), rd, buf, sizeof buf); if (n < 0) goto error; putc(',', trace); fputs(buf, trace); rd += n; n = ns_name_uncompress(ns_msg_base(*msg), ns_msg_end(*msg), rd, buf, sizeof buf); if (n < 0) goto error; putc(',', trace); fputs(buf, trace); rd += n; if (ns_msg_end(*msg) - rd < 5*NS_INT32SZ) goto error; for (n = 0; n < 5; n++) MY_GET32(soa[n], rd); sprintf(buf, "%u,%u,%u,%u,%u", soa[0], soa[1], soa[2], soa[3], soa[4]); break; case ns_t_a: if (ns_msg_end(*msg) - rd < 4) goto error; inet_ntop(AF_INET, rd, buf, sizeof buf); break; case ns_t_aaaa: if (ns_msg_end(*msg) - rd < 16) goto error; inet_ntop(AF_INET6, rd, buf, sizeof buf); break; case ns_t_mx: if (ns_msg_end(*msg) - rd < 2) goto error; MY_GET16(mx, rd); fprintf(trace, ",%u", mx); /* FALLTHROUGH */ case ns_t_ns: case ns_t_ptr: case ns_t_cname: n = ns_name_uncompress(ns_msg_base(*msg), ns_msg_end(*msg), rd, buf, sizeof buf); if (n < 0) goto error; break; /* * GGM 2014/09/04 deal with edns0 a bit more clearly */ case ns_t_opt: { u_long edns0csize; u_short edns0version; u_short edns0rcode; u_char edns0dobit; u_char edns0z; /* class encodes client UDP size accepted */ edns0csize = (u_long)(class); /* * the first two bytes of ttl encode edns0 version, and the extended rcode */ edns0version = ((u_long)ns_rr_ttl(*rr) & 0x00ff0000) >> 16; edns0rcode = ((u_long)ns_rr_ttl(*rr) & 0xff000000) >> 24; /* * the next two bytes of ttl encode DO bit as the top bit, and the remainder is the 'z' value */ edns0dobit = (u_long)ns_rr_ttl(*rr) & 0x8000 ? '1' : '0'; edns0z = (u_long)ns_rr_ttl(*rr) & 0x7fff; /* optlen is the size of the OPT rdata */ u_short optlen = ns_rr_rdlen(*rr); fprintf(trace, ",edns0[len=%d,UDP=%lu,ver=%d,rcode=%d,DO=%c,z=%d] %c\n\t", optlen, edns0csize, edns0version, edns0rcode, edns0dobit, edns0z, '\\'); /* if we have any data */ while (optlen >= 4) { /* the next two shorts are the edns0 opt code, and the length of the optionsection */ u_short edns0optcod; u_short edns0lenopt; MY_GET16(edns0optcod, rd); MY_GET16(edns0lenopt, rd); optlen -= 4; fprintf(trace, "edns0[code=%d,codelen=%d] ", edns0optcod, edns0lenopt); /* * Check that the OPTION-LENGTH for this EDNS0 option doesn't * exceed the size of the remaining OPT record rdata. If it does, * just bail. */ if (edns0lenopt > optlen) goto error; /* * "pre-consume" edns0lenopt bytes from optlen here because * below we're going to decrement edns0lenopt as we go. * At this point optlen will refer to the size of the remaining * OPT_T rdata after parsing the current option. */ optlen -= edns0lenopt; /* if we have edns0_client_subnet */ if (edns0optcod == 0x08) { if (edns0lenopt < 4) goto error; u_short afi; MY_GET16(afi, rd); u_short masks; MY_GET16(masks, rd); edns0lenopt -= 4; u_short srcmask = (masks & 0xff00) >> 8; u_short scomask = (masks & 0xff); char buf[128]; u_char addr[16]; memset(addr, 0, sizeof addr); memcpy(addr, rd, edns0lenopt < sizeof(addr) ? edns0lenopt : sizeof(addr)); if (afi == 0x1) { inet_ntop(AF_INET, addr, buf, sizeof buf); } else if (afi == 0x2) { inet_ntop(AF_INET6, addr, buf, sizeof buf); } else { fprintf(trace, "unknown AFI %d\n", afi); strcpy(buf,"<unknown>"); } fprintf(trace, "edns0_client_subnet=%s/%d (scope %d)", buf, srcmask, scomask); } /* increment the rd pointer by the remaining option data size */ rd += edns0lenopt; } } break; default: error: sprintf(buf, "[%u]", ns_rr_rdlen(*rr)); } if (buf[0] != '\0') { putc(',', trace); fputs(buf, trace); } }
void royparse_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) { if (flags & DNSCAP_OUTPUT_ISDNS) { int rrmax; ns_msg msg; ns_rr rr; if (ns_initparse(payload, payloadlen, &msg) < 0) { fprintf(r_out, "ERR\n"); return; } if (ns_msg_getflag(msg, ns_f_qr) != 0 && sport == 53) { fprintf(r_out, "%cD_", ns_msg_getflag(msg, ns_f_rd) ? 'R' : 'N'); switch (ns_msg_getflag(msg, ns_f_opcode)) { case ns_o_query: fprintf(r_out, "QUERY"); break; case ns_o_notify: fprintf(r_out, "NOTIFY"); break; case ns_o_update: fprintf(r_out, "UPDATE"); break; default: fprintf(r_out, "ELSE"); } fprintf(r_out, "_%u_%cA_", ns_msg_count(msg, ns_s_an) ? 1 : 0, ns_msg_getflag(msg, ns_f_aa) ? 'A' : 'N'); switch (ns_msg_getflag(msg, ns_f_rcode)) { case ns_r_noerror: fprintf(r_out, "NOERROR"); break; case ns_r_formerr: fprintf(r_out, "FORMERR"); break; case ns_r_nxdomain: fprintf(r_out, "NXDOMAIN"); break; case ns_r_notimpl: fprintf(r_out, "NOTIMP"); break; case ns_r_refused: fprintf(r_out, "REFUSED"); break; case ns_r_notauth: fprintf(r_out, "NOTAUTH"); break; default: fprintf(r_out, "ELSE"); } fprintf(r_out, " %s,", royparse_ia_str(to)); if (ns_msg_count(msg, ns_s_qd) > 0) { if (ns_parserr(&msg, ns_s_qd, 0, &rr) == 0) { royparse_normalize(ns_rr_name(rr)); fprintf(r_out, "%s%s,%u", ns_rr_name(rr), (ns_rr_name(rr)[0] == '.') ? "" : ".", ns_rr_type(rr)); } else fprintf(r_out, "ERR,ERR"); } else fprintf(r_out, ","); fprintf(r_out, ",%ld,%s%s%s%s", ns_msg_size(msg), ns_msg_id(msg) < 256 ? "-L" : "", ns_msg_getflag(msg, ns_f_tc) ? "-TC" : "", ns_msg_getflag(msg, ns_f_ad) ? "-AD" : "", ns_msg_getflag(msg, ns_f_cd) ? "-CD" : ""); rrmax = ns_msg_count(msg, ns_s_ar); while (rrmax > 0) { rrmax--; if (ns_parserr(&msg, ns_s_ar, rrmax, &rr) == 0) { if (ns_rr_type(rr) == ns_t_opt) { fprintf(r_out, "-%c", (u_long)ns_rr_ttl(rr) & NS_OPT_DNSSEC_OK ? 'D' : 'E'); break; } } } fprintf(r_out, "\n"); } else if (opt_q != 0 && ns_msg_getflag(msg, ns_f_qr) == 0 && dport == 53) { struct pcap_pkthdr h; if (flags & DNSCAP_OUTPUT_ISLAYER) return; memset(&h, 0, sizeof h); h.ts = ts; h.len = h.caplen = olen; pcap_dump((u_char*)q_out, &h, pkt_copy); } } }
void dowse_output(const char *descr, iaddr from, iaddr to, uint8_t proto, int isfrag, unsigned sport, unsigned dport, my_bpftimeval ts, const u_char *pkt_copy, unsigned olen, const u_char *dnspkt, unsigned dnslen) { /* dnspkt may be NULL if IP packet does not contain a valid DNS message */ char output[MAX_OUTPUT]; if (dnspkt) { ns_msg msg; int qdcount; ns_rr rr; int *val; char *sval; char *extracted; char *resolved; char *from; int res; char action = 'A'; char from_color[16]; ns_initparse(dnspkt, dnslen, &msg); if (!ns_msg_getflag(msg, ns_f_qr)) return; /* * -- flags -- * 0 1 5 6 7 8 11 15 * +----+----------------+----+----+----+-----------+----------------+ * | QR | Operation Code | AA | TC | RA | Zero | Recode | * +----+----------------+----+----+----+-----------+----------------+ * * Question/Response : ns_f_qr * Operation code : ns_f_opcode * Authoritative Answer : ns_f_aa * Truncation occurred : ns_f_tc * Recursion Desired : ns_f_rd * Recursion Available : ns_f_ra * MBZ : ns_f_z * Authentic Data (DNSSEC) : ns_f_ad * Checking Disabled (DNSSEC) : ns_f_cd * Response code : ns_f_rcode */ // logerr("msg: %p",msg); qdcount = ns_msg_count(msg, ns_s_qd); if (qdcount > 0 && 0 == ns_parserr(&msg, ns_s_qd, 0, &rr)) { // where the query comes from from = ia_resolv(to); // if its from ourselves omit it if(strncmp(from,hostname,MAX_DOMAIN)==0) return; if(own_ipv4) if(strncmp(from,own_ipv4,MAX_DOMAIN)==0) return; // not reverse resolved means not known by Dowse, code RED if(is_ip(from)) strcpy(from_color,"#FF0000"); resolved = ns_rr_name(rr); // what domain is being looked up extracted = extract_domain(resolved); res = hashmap_get(visited, extracted, (void**)(&val)); switch(res) { // TODO: fix malloc and strdup here as they grow as a leak on long term case MAP_MISSING : // never visited /* ==13150== 992 bytes in 248 blocks are definitely lost in loss record 45 of 49 */ /* ==13150== at 0x4C28C20: malloc (vg_replace_malloc.c:296) */ /* ==13150== by 0x5C3D326: dowse_output (dowse.c:417) */ /* ==13150== by 0x404CAB: output (dnscap.c:2201) */ /* ==13150== by 0x406779: network_pkt (dnscap.c:2164) */ /* ==13150== by 0x407065: dl_pkt (dnscap.c:1608) */ /* ==13150== by 0x503F5E9: ??? (in /usr/lib/x86_64-linux-gnu/libpcap.so.1.6.2) */ /* ==13150== by 0x5043783: ??? (in /usr/lib/x86_64-linux-gnu/libpcap.so.1.6.2) */ /* ==13150== by 0x4037E0: poll_pcaps (dnscap.c:1344) */ /* ==13150== by 0x4037E0: main (dnscap.c:406) */ val = malloc(sizeof(int)); *val = 1; // just a placeholder for now /* ==13150== 3,069 bytes in 248 blocks are definitely lost in loss record 47 of 49 */ /* ==13150== at 0x4C28C20: malloc (vg_replace_malloc.c:296) */ /* ==13150== by 0x5511A69: strdup (strdup.c:42) */ /* ==13150== by 0x5C3D34D: dowse_output (dowse.c:419) */ /* ==13150== by 0x404CAB: output (dnscap.c:2201) */ /* ==13150== by 0x406779: network_pkt (dnscap.c:2164) */ /* ==13150== by 0x407065: dl_pkt (dnscap.c:1608) */ /* ==13150== by 0x503F5E9: ??? (in /usr/lib/x86_64-linux-gnu/libpcap.so.1.6.2) */ /* ==13150== by 0x5043783: ??? (in /usr/lib/x86_64-linux-gnu/libpcap.so.1.6.2) */ /* ==13150== by 0x4037E0: poll_pcaps (dnscap.c:1344) */ /* ==13150== by 0x4037E0: main (dnscap.c:406) */ res = hashmap_put(visited, strdup(extracted), val); break; case MAP_OK: // already visited action = 'M'; break; // TODO error checks case MAP_FULL: case MAP_OMEM: break; } // compose the path of the detected query // add category if listed if(listpath) { // add known domain list information res = hashmap_get(domainlist, extracted, (void**)(&sval)); switch(res) { case MAP_OK: /* render with the category in front of domain */ snprintf(output,MAX_OUTPUT,"%lu|%s|%c|%s/%s/%s", ts2epoch(&ts,NULL), // from our epoch.c from, action, tld, sval, extracted); break; default: /* render only the domain in root category */ snprintf(output,MAX_OUTPUT,"%lu|%s|%c|%s/%s", ts2epoch(&ts,NULL), // from our epoch.c from, action, tld, extracted); break; } } else /* render only the domain in root category */ snprintf(output,MAX_OUTPUT,"%lu|%s|%c|%s/%s", ts2epoch(&ts,NULL), // from our epoch.c from, action, tld, extracted); /* write to file */ if(fileout) { fputs(output, fileout); fputc('\n',fileout); if(fileout) fflush(fileout); } /* print fast on console for realtime */ if(console) { puts(output); fflush(stdout); } if(redis) { rreply = redisCommand(redis, "PUBLISH dns-query-channel %s", output); logerr("PUBLISH dns-query-channel: %s\n", rreply->str); freeReplyObject(rreply); } } } }