static void cares_callback(void *arg, int status, unsigned char *abuf, int alen) { int i; unsigned int ancount, nscount, arcount; const unsigned char *aptr; #ifdef DEBUG printf("cares_callback: status=%i, alen=%i\n", status, alen); #endif if (status != ARES_SUCCESS) { if (verbose > 1) printf("ares failed: %s\n", ares_strerror(status)); return; } ancount = DNS_HEADER_ANCOUNT(abuf); nscount = DNS_HEADER_NSCOUNT(abuf); arcount = DNS_HEADER_ARCOUNT(abuf); #ifdef DEBUG printf("ancount: %i, nscount: %i, arcount: %i\n", ancount, nscount, arcount); #endif /* safety check */ if (alen < NS_HFIXEDSZ) return; aptr = abuf + NS_HFIXEDSZ; aptr = skip_query(aptr, abuf, alen); for (i = 0; i < ancount && caadr == 0; i++) { if (ca_tmpname == NULL) aptr = parse_rr(aptr, abuf, alen); else aptr = skip_rr(aptr, abuf, alen); } if (caadr == 0) { for (i = 0; i < nscount; i++) { aptr = skip_rr(aptr, abuf, alen); } for (i = 0; i < arcount && caadr == 0; i++) { aptr = parse_rr(aptr, abuf, alen); } } }
static bool dns_mt(const struct sk_buff *skb, struct xt_action_param *par) #endif { const struct xt_dns_info *info = par->matchinfo; u8 *dns; size_t len, offset; bool is_match, invert; u16 counts[4]; /* qdcount, ancount, nscount, arcount */ u16 udpsize; int i; /* skip fragments */ if (par->fragoff) return false; NFDEBUG("skb->len: %d, skb->data_len: %d, par->thoff: %d\n", skb->len, skb->data_len, par->thoff); /* find UDP payload */ offset = par->thoff + sizeof(struct udphdr); len = skb->len - offset; if (len > sizeof(pktbuf)) { pr_warn(KBUILD_MODNAME": Packet too big. Increase MAX_MTU (size %d)\n", skb->len); return false; } dns = skb_header_pointer(skb, offset, len, pktbuf); if (dns == NULL) { pr_warn(KBUILD_MODNAME": skb_header_pointer failed!\n"); return false; } /* minimum DNS query payload is 17 bytes (for "." root zone) */ if (len < 17) return false; NFDEBUG("skb->len: %d, skb->data_len: %d, len: %zu\n", skb->len, skb->data_len, len); debug_dump_buf(dns, len, 0, "ipt_dns"); /* check if we are dealing with DNS query */ if (info->flags & XT_DNS_QUERY) { invert = ((info->invert_flags & XT_DNS_QUERY) != 0); is_match = ((dns[2] & NS_QR) == NS_QR_QUERY); if (is_match == invert) return false; } /* check if we are dealing with DNS response */ if (info->flags & XT_DNS_RESPONSE) { invert = ((info->invert_flags & XT_DNS_RESPONSE) != 0); is_match = ((dns[2] & NS_QR) == NS_QR_RESPONSE); if (is_match == invert) return false; } /* fill counts[] with data from dns header */ for (i=0; i<4; i++) { counts[i] = ntohs(((u16*)dns)[i+2]); } /* query type test */ if (info->flags & XT_DNS_QTYPE) { NFDEBUG("Entering qtype match\n"); invert = ((info->invert_flags & XT_DNS_QTYPE) != 0); is_match = counts[0] > 0; /* qdcount at least 1 */ if (!is_match) goto qtype_out; /* offset is set to the first question section */ offset = 12; is_match = skip_name(dns, len, &offset); if (!is_match) goto qtype_out; NFDEBUG("Matching qtype: %x %x %x %x\n", dns[offset-4], dns[offset-3], dns[offset-2], dns[offset-1]); /* match if type=info->type, class IN */ is_match = (dns[offset-4] == 0x00) && (dns[offset-3] == info->qtype) && (dns[offset-2] == 0x00) && (dns[offset-1] == 0x01); qtype_out: if (is_match == invert) return false; } /* check for EDNS0 */ if (info->flags & XT_DNS_EDNS0) { invert = ((info->invert_flags & XT_DNS_EDNS0) != 0); is_match = counts[3] > 0; /* arcount at least 1 */ offset = 12; /* skip query sections */ for (i=0; i<counts[0]; i++) { is_match &= skip_name(dns, len, &offset); if (!is_match) break; } if (!is_match) goto edns0_out; /* skip answer and authority sections */ for (i=0; i<(counts[1]+counts[2]); i++) { is_match &= skip_rr(dns, len, &offset); if (!is_match) break; } if (!is_match) goto edns0_out; /* try to find EDNS0 pseudo-RR */ for (i=0; i<counts[3]; i++) { if (dns[offset] == 0 && dns[offset+1] == 0 && dns[offset+2] == 41) break; is_match &= skip_rr(dns, len, &offset); if (!is_match) break; } if (!is_match || (i == counts[3])) { is_match = false; goto edns0_out; } /* EDNS0 found */ if (info->flags & XT_DNS_BUFSIZE) { /* TODO: XT_DNS_BUFSIZE inversion not implemented */ udpsize = dns[offset+3] << 8 | dns[offset+4]; if (udpsize < info->bufsize[0] || udpsize > info->bufsize[1]) { is_match = false; goto edns0_out; } } debug_dump_buf(dns, len, offset, "ipt_dns_edns0"); edns0_out: if (is_match == invert) return false; } /* Nothing stopped us so far, let's accept the packet */ return true; }