mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-28 02:29:51 -05:00
scrubber work.
git-svn-id: file:///svn/unbound/trunk@353 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
80391ee2b8
commit
a7d3013334
3 changed files with 286 additions and 7 deletions
165
iterator/iter_scrub.c
Normal file
165
iterator/iter_scrub.c
Normal 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
65
iterator/iter_scrub.h
Normal 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 */
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in a new issue