]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
scrubber work.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 31 May 2007 14:57:24 +0000 (14:57 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 31 May 2007 14:57:24 +0000 (14:57 +0000)
git-svn-id: file:///svn/unbound/trunk@353 be551aaa-1e26-0410-a405-d3ace91eadb9

iterator/iter_scrub.c [new file with mode: 0644]
iterator/iter_scrub.h [new file with mode: 0644]
iterator/iterator.c

diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c
new file mode 100644 (file)
index 0000000..5d7151a
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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;
+}
diff --git a/iterator/iter_scrub.h b/iterator/iter_scrub.h
new file mode 100644 (file)
index 0000000..8f32cea
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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 */
index b6722319a1032dd88cb8b104f98e2ac176388f0c..412b075bade6385bb56c3e2d278140a8e38d6d6d 100644 (file)
@@ -46,6 +46,7 @@
 #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"
@@ -743,7 +744,7 @@ return nextState(event, req, state, IterEventState.INIT_REQUEST_STATE);
                 * 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 
@@ -784,7 +785,7 @@ processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq,
        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. */
@@ -1042,7 +1043,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
                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. */
@@ -1060,6 +1061,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
        }
        outbound_list_insert(&iq->outlist, outq);
        iq->num_current_queries++;
+       qstate->ext_state[id] = module_wait_reply;
 
        return 0;
 }
@@ -1177,11 +1179,58 @@ process_request(struct module_qstate* qstate, struct iter_qstate* iq,
 /** 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);
 }
 
@@ -1213,8 +1262,8 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int 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 */