--- /dev/null
+/*
+ * iterator/iter_scrub.c - scrubbing, normalization, sanitization of DNS msgs.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the NLNET LABS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file has routine(s) for cleaning up incoming DNS messages from
+ * possible useless or malicious junk in it.
+ */
+#include "config.h"
+#include "iterator/iter_scrub.h"
+#include "util/log.h"
+#include "util/net_help.h"
+#include "util/data/msgparse.h"
+#include "util/data/dname.h"
+#include "util/data/msgreply.h"
+
+/**
+ * This routine normalizes a response. This includes removing "irrelevant"
+ * records from the answer and additional sections and (re)synthesizing
+ * CNAMEs from DNAMEs, if present.
+ *
+ * @param pkt: packet.
+ * @param msg: msg to normalize.
+ * @param qinfo: original query.
+ * @param region: where to allocate synthesized CNAMEs.
+ * @return 0 on error.
+ */
+static int
+scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg,
+ struct query_info* qinfo, struct region* region)
+{
+ uint8_t* sname = qinfo->qname;
+ size_t snamelen = qinfo->qname_len;
+ struct rrset_parse* rrset, *prev=NULL;
+
+ if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR &&
+ FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN)
+ return 1;
+
+ /* For the ANSWER section, remove all "irrelevant" records and add
+ * synthesized CNAMEs from DNAMEs
+ * This will strip out-of-order CNAMEs as well. */
+
+ for(rrset = msg->rrset_first; rrset; (prev=rrset),(rrset=rrset->rrset_all_next))
+ {
+ if(rrset->section != LDNS_SECTION_ANSWER)
+ continue;
+ if(rrset->type == LDNS_RR_TYPE_DNAME &&
+ dname_strict_subdomain_c(sname, rrset->dname)) {
+ if(rrset->rr_count != 1) {
+ verbose(VERB_ALGO, "Found DNAME rrset with "
+ "size > 1: %d", rrset->rr_count);
+ return 0;
+ }
+ /* check if next rrset is correct CNAME. else,
+ * synthesize a CNAME */
+ /* if nametoolong, return 0 (should been YXDOMAIN) */
+ continue;
+ }
+
+ /* The only records in the ANSWER section not allowed to */
+ if(dname_pkt_compare(pkt, sname, rrset->dname) != 0) {
+ /* TODO remove_rrset(pkt, msg, prev, rrset); */
+ continue;
+ }
+
+ /* Follow the CNAME chain. */
+ if(rrset->type == LDNS_RR_TYPE_CNAME) {
+ if(rrset->rr_count != 1) {
+ verbose(VERB_ALGO, "Found CNAME rrset with "
+ "size > 1: %d", rrset->rr_count);
+ return 0;
+ }
+ if(rrset->rr_first->size < sizeof(uint16_t)+1)
+ return 0; /* CNAME rdata too small */
+ sname = rrset->rr_first->ttl_data + sizeof(uint32_t)
+ + sizeof(uint16_t); /* skip ttl, rdatalen */
+ snamelen = rrset->rr_first->size - sizeof(uint16_t);
+ }
+
+ /* Otherwise, make sure that the RRset matches the qtype. */
+ if(qinfo->qtype != LDNS_RR_TYPE_ANY &&
+ qinfo->qtype != rrset->type) {
+ /*TODO: remove_rrset(); */
+ }
+
+ /* Otherwise, fetch the additional names from the
+ * relevant rrset. */
+ /* store additional names from rrset rdata */
+ }
+
+ /* Get additional names from AUTHORITY */
+ /* go through authority section, store add. names from rrdat rdata */
+
+ /* For each record in the additional section, remove it if it is an
+ * address record and not in the collection of additional names
+ * found in ANSWER and AUTHORITY. */
+
+ /* FIXME: what about other types? */
+ return 1;
+}
+
+int
+scrub_message(ldns_buffer* pkt, struct msg_parse* msg,
+ struct query_info* qinfo, uint8_t* zonename, size_t zonelen,
+ struct region* region)
+{
+ /* things to check:
+ * if qdcount > 0 : qinfo.
+ * from normalize() from NSclient.
+ * from sanitize() from iterator.
+ */
+ /* basic sanity checks */
+ if(msg->qdcount > 1)
+ return 0;
+ if( !(msg->flags&BIT_QR) )
+ return 0;
+
+ /* if a query is echoed back, make sure it is correct. Otherwise,
+ * this may be not a reply to our query. */
+ if(msg->qdcount == 1) {
+ if(dname_pkt_compare(pkt, msg->qname, qinfo->qname) != 0)
+ return 0;
+ if(msg->qtype != qinfo->qtype || msg->qclass != qinfo->qclass)
+ return 0;
+ }
+
+ /* normalize the response */
+ if(!scrub_normalize(pkt, msg, qinfo, region))
+ return 0;
+ return 1;
+}
--- /dev/null
+/*
+ * iterator/iter_scrub.h - scrubbing, normalization, sanitization of DNS msgs.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the NLNET LABS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file has routine(s) for cleaning up incoming DNS messages from
+ * possible useless or malicious junk in it.
+ */
+
+#ifndef ITERATOR_ITER_SCRUB_H
+#define ITERATOR_ITER_SCRUB_H
+struct msg_parse;
+struct query_info;
+struct region;
+
+/**
+ * Cleanup the passed dns message.
+ * @param pkt: the packet itself, for resolving name compression pointers.
+ * the packet buffer is unaltered.
+ * @param msg: the parsed packet, this structure is cleaned up.
+ * @param qinfo: the query info that was sent to the server. Checked.
+ * @param zonename: the name of the last delegation point.
+ * Used to determine out of bailiwick information.
+ * @param zonelen: length of zonename.
+ * @param region: where to allocate (new) parts of the message.
+ * @return: false if the message is total waste. true if scrubbed with success.
+ */
+int scrub_message(ldns_buffer* pkt, struct msg_parse* msg,
+ struct query_info* qinfo, uint8_t* zonename, size_t zonelen,
+ struct region* region);
+
+#endif /* ITERATOR_ITER_SCRUB_H */
#include "iterator/iter_hints.h"
#include "iterator/iter_delegpt.h"
#include "iterator/iter_resptype.h"
+#include "iterator/iter_scrub.h"
#include "services/cache/dns.h"
#include "util/module.h"
#include "util/netevent.h"
* this query as the parent. So further processing for
* this event will stop until reactivated by the results
* of priming. */
- return false;
+ return 0;
}
/* Reset the RD flag. If this is a query restart, then the RD
if(prime_stub(qstate, iq, ie, id, qstate->qinfo.qname,
qstate->qinfo.qclass)) {
/* A priming sub request was made */
- return false;
+ return 0;
}
/* most events just get forwarded to the next state. */
else verbose(VERB_ALGO, "no current targets -- waiting "
"for %d outstanding queries to respond.",
iq->num_current_queries);
- return false;
+ return 0;
}
/* We have a valid target. */
}
outbound_list_insert(&iq->outlist, outq);
iq->num_current_queries++;
+ qstate->ext_state[id] = module_wait_reply;
return 0;
}
/** process authoritative server reply */
static void
process_response(struct module_qstate* qstate, struct iter_qstate* iq,
- struct iter_env* ie, int id, struct outbound_entry* outbound)
+ struct iter_env* ie, int id, struct outbound_entry* outbound,
+ enum module_ev event)
{
+ struct msg_parse* prs;
+ struct edns_data edns;
+ ldns_buffer* pkt;
+
verbose(VERB_ALGO, "process_response: new external response event");
- /* TODO outbound: use it for scrubbing and so on */
+ iq->response = NULL;
iq->state = QUERY_RESP_STATE;
+ if(event == module_event_timeout || event == module_event_error) {
+ goto handle_it;
+ }
+ if(event != module_event_reply || !qstate->reply) {
+ log_err("Bad event combined with response");
+ outbound_list_remove(&iq->outlist, outbound);
+ qstate->ext_state[id] = module_error;
+ return;
+ }
+
+ /* parse message */
+ prs = (struct msg_parse*)region_alloc(qstate->scratch,
+ sizeof(struct msg_parse));
+ if(!prs) {
+ log_err("out of memory on incoming message");
+ /* like packet got dropped */
+ goto handle_it;
+ }
+ memset(prs, 0, sizeof(*prs));
+ memset(&edns, 0, sizeof(edns));
+ pkt = qstate->reply->c->buffer;
+ ldns_buffer_set_position(pkt, 0);
+ if(!parse_packet(pkt, prs, qstate->scratch))
+ goto handle_it;
+ /* edns is not examined, but removed from message to help cache */
+ if(!parse_extract_edns(prs, &edns))
+ goto handle_it;
+
+ /* normalize and sanitize: easy to delete items from linked lists */
+ if(!scrub_message(pkt, prs, &qstate->qinfo,
+ iq->dp->name, iq->dp->namelen, qstate->scratch))
+ goto handle_it;
+
+ /* allocate response dns_msg in region */
+ /* TODO:
+ iq->response = dns_parse_to_msg(prs, qstate->region);
+ */
+ if(!iq->response)
+ goto handle_it;
+
+handle_it:
+ outbound_list_remove(&iq->outlist, outbound);
iter_handle(qstate, iq, ie, id);
}
iter_handle(qstate, iq, ie, id);
return;
}
- if(event == module_event_reply) {
- process_response(qstate, iq, ie, id, outbound);
+ if(outbound) {
+ process_response(qstate, iq, ie, id, outbound, event);
return;
}
/* TODO: uhh */