scrubber work.

git-svn-id: file:///svn/unbound/trunk@353 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-05-31 14:57:24 +00:00
parent 80391ee2b8
commit a7d3013334
3 changed files with 286 additions and 7 deletions

165
iterator/iter_scrub.c Normal file
View file

@ -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;
}

65
iterator/iter_scrub.h Normal file
View file

@ -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 */

View 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 */