mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-23 00:00:51 -05:00
Implemented qname minimisation
git-svn-id: file:///svn/unbound/trunk@3554 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
ae7c49cb0b
commit
a05bf09811
11 changed files with 3120 additions and 3067 deletions
|
|
@ -1,3 +1,6 @@
|
|||
30 November 2015: Ralph
|
||||
- Implemented qname minimisation
|
||||
|
||||
30 November 2015: Wouter
|
||||
- Fix for #724: conf syntax to read files from run dir (on Windows).
|
||||
|
||||
|
|
|
|||
|
|
@ -587,6 +587,12 @@ queries. For domains that do not support 0x20 and also fail with fallback
|
|||
because they keep sending different answers, like some load balancers.
|
||||
Can be given multiple times, for different domains.
|
||||
.TP
|
||||
.B qname\-minimisation: \fI<yes or no>
|
||||
Send minimum amount of information to upstream servers to enhance privacy.
|
||||
Only sent minimum required labels of the QNAME and set QTYPE to NS when
|
||||
possible. Best effort approach, full QNAME and original QTYPE will be sent when
|
||||
upstream replies with a RCODE other than NOERROR. Default is off.
|
||||
.TP
|
||||
.B private\-address: \fI<IP address or subnet>
|
||||
Give IPv4 of IPv6 addresses or classless subnets. These are addresses
|
||||
on your private network, and are not allowed to be returned for
|
||||
|
|
|
|||
|
|
@ -81,6 +81,21 @@ iter_init(struct module_env* env, int id)
|
|||
log_err("iterator: could not apply configuration settings.");
|
||||
return 0;
|
||||
}
|
||||
if(env->cfg->qname_minimisation) {
|
||||
uint8_t dname[LDNS_MAX_DOMAINLEN+1];
|
||||
size_t len = sizeof(dname);
|
||||
if(sldns_str2wire_dname_buf("ip6.arpa.", dname, &len) != 0) {
|
||||
log_err("ip6.arpa. parse error");
|
||||
return 0;
|
||||
}
|
||||
iter_env->ip6arpa_dname = (uint8_t*)malloc(len);
|
||||
if(!iter_env->ip6arpa_dname) {
|
||||
log_err("malloc failure");
|
||||
return 0;
|
||||
}
|
||||
memcpy(iter_env->ip6arpa_dname, dname, len);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -101,6 +116,8 @@ iter_deinit(struct module_env* env, int id)
|
|||
if(!env || !env->modinfo[id])
|
||||
return;
|
||||
iter_env = (struct iter_env*)env->modinfo[id];
|
||||
if(env->cfg->qname_minimisation)
|
||||
free(iter_env->ip6arpa_dname);
|
||||
free(iter_env->target_fetch_policy);
|
||||
priv_delete(iter_env->priv);
|
||||
donotq_delete(iter_env->donotq);
|
||||
|
|
@ -145,6 +162,12 @@ iter_new(struct module_qstate* qstate, int id)
|
|||
/* Start with the (current) qname. */
|
||||
iq->qchase = qstate->qinfo;
|
||||
outbound_list_init(&iq->outlist);
|
||||
if (qstate->env->cfg->qname_minimisation)
|
||||
iq->minimisation_state = INIT_MINIMISE_STATE;
|
||||
else
|
||||
iq->minimisation_state = DONOT_MINIMISE_STATE;
|
||||
|
||||
memset(&iq->qinfo_out, 0, sizeof(struct query_info));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -590,6 +613,11 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
|
|||
subiq->qchase = subq->qinfo;
|
||||
subiq->chase_flags = subq->query_flags;
|
||||
subiq->refetch_glue = 0;
|
||||
if(qstate->env->cfg->qname_minimisation)
|
||||
subiq->minimisation_state = INIT_MINIMISE_STATE;
|
||||
else
|
||||
subiq->minimisation_state = DONOT_MINIMISE_STATE;
|
||||
memset(&subiq->qinfo_out, 0, sizeof(struct query_info));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -1042,6 +1070,8 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
iq->query_restart_count++;
|
||||
iq->sent_count = 0;
|
||||
sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
|
||||
if(qstate->env->cfg->qname_minimisation)
|
||||
iq->minimisation_state = INIT_MINIMISE_STATE;
|
||||
return next_state(iq, INIT_REQUEST_STATE);
|
||||
}
|
||||
|
||||
|
|
@ -1599,6 +1629,8 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
iq->refetch_glue = 1;
|
||||
iq->query_restart_count++;
|
||||
iq->sent_count = 0;
|
||||
if(qstate->env->cfg->qname_minimisation)
|
||||
iq->minimisation_state = INIT_MINIMISE_STATE;
|
||||
return next_state(iq, INIT_REQUEST_STATE);
|
||||
}
|
||||
}
|
||||
|
|
@ -1975,9 +2007,76 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
}
|
||||
}
|
||||
|
||||
if(iq->minimisation_state == INIT_MINIMISE_STATE) {
|
||||
/* (Re)set qinfo_out to (new) delegation point, except
|
||||
* when qinfo_out is already a subdomain op dp. This happens
|
||||
* when resolving ip6.arpa dnames. */
|
||||
if(!(iq->qinfo_out.qname_len
|
||||
&& dname_subdomain_c(iq->qchase.qname,
|
||||
iq->qinfo_out.qname)
|
||||
&& dname_subdomain_c(iq->qinfo_out.qname,
|
||||
iq->dp->name))) {
|
||||
iq->qinfo_out.qname = iq->dp->name;
|
||||
iq->qinfo_out.qname_len = iq->dp->namelen;
|
||||
iq->qinfo_out.qtype = LDNS_RR_TYPE_NS;
|
||||
iq->qinfo_out.qclass = iq->qchase.qclass;
|
||||
}
|
||||
|
||||
iq->minimisation_state = MINIMISE_STATE;
|
||||
}
|
||||
if(iq->minimisation_state == MINIMISE_STATE) {
|
||||
int labdiff = dname_count_labels(iq->qchase.qname) -
|
||||
dname_count_labels(iq->qinfo_out.qname);
|
||||
|
||||
iq->qinfo_out.qname = iq->qchase.qname;
|
||||
iq->qinfo_out.qname_len = iq->qchase.qname_len;
|
||||
|
||||
/* Special treatment for ip6.arpa lookups.
|
||||
* Reverse IPv6 dname has 34 labels, increment the IP part
|
||||
* (usually first 32 labels) by 8 labels (7 more than the
|
||||
* default 1 label increment). */
|
||||
if(labdiff <= 32 &&
|
||||
dname_subdomain_c(iq->qchase.qname, ie->ip6arpa_dname)) {
|
||||
labdiff -= 7;
|
||||
/* Small chance of zone cut after first label. Stop
|
||||
* minimising */
|
||||
if(labdiff <= 1)
|
||||
labdiff = 0;
|
||||
}
|
||||
|
||||
if(labdiff > 1) {
|
||||
verbose(VERB_QUERY, "removing %d labels", labdiff-1);
|
||||
dname_remove_labels(&iq->qinfo_out.qname,
|
||||
&iq->qinfo_out.qname_len,
|
||||
labdiff-1);
|
||||
}
|
||||
if(labdiff < 1 ||
|
||||
(labdiff < 2 && iq->qchase.qtype == LDNS_RR_TYPE_DS))
|
||||
/* Stop minimising this query, resolve "as usual" */
|
||||
iq->minimisation_state = DONOT_MINIMISE_STATE;
|
||||
else {
|
||||
struct dns_msg* msg = dns_cache_lookup(qstate->env,
|
||||
iq->qinfo_out.qname, iq->qinfo_out.qname_len,
|
||||
iq->qinfo_out.qtype, iq->qinfo_out.qclass,
|
||||
qstate->query_flags, qstate->region,
|
||||
qstate->env->scratch);
|
||||
if(msg && msg->rep->an_numrrsets == 0)
|
||||
/* no need to send query if it is already
|
||||
* cached as NOERROR/NODATA */
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
if(iq->minimisation_state == SKIP_MINIMISE_STATE)
|
||||
/* Do not increment qname, continue incrementing next
|
||||
* iteration */
|
||||
iq->minimisation_state = MINIMISE_STATE;
|
||||
if(iq->minimisation_state == DONOT_MINIMISE_STATE)
|
||||
iq->qinfo_out = iq->qchase;
|
||||
|
||||
/* We have a valid target. */
|
||||
if(verbosity >= VERB_QUERY) {
|
||||
log_query_info(VERB_QUERY, "sending query:", &iq->qchase);
|
||||
log_query_info(VERB_QUERY, "sending query:", &iq->qinfo_out);
|
||||
log_name_addr(VERB_QUERY, "sending to target:", iq->dp->name,
|
||||
&target->addr, target->addrlen);
|
||||
verbose(VERB_ALGO, "dnssec status: %s%s",
|
||||
|
|
@ -1986,8 +2085,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
}
|
||||
fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query));
|
||||
outq = (*qstate->env->send_query)(
|
||||
iq->qchase.qname, iq->qchase.qname_len,
|
||||
iq->qchase.qtype, iq->qchase.qclass,
|
||||
iq->qinfo_out.qname, iq->qinfo_out.qname_len,
|
||||
iq->qinfo_out.qtype, iq->qinfo_out.qclass,
|
||||
iq->chase_flags | (iq->chase_to_rd?BIT_RD:0), EDNS_DO|BIT_CD,
|
||||
iq->dnssec_expected, iq->caps_fallback || is_caps_whitelisted(
|
||||
ie, iq), &target->addr, target->addrlen, iq->dp->name,
|
||||
|
|
@ -2042,6 +2141,9 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
enum response_type type;
|
||||
iq->num_current_queries--;
|
||||
if(iq->response == NULL) {
|
||||
/* Don't increment qname when QNAME minimisation is enabled */
|
||||
if (qstate->env->cfg->qname_minimisation)
|
||||
iq->minimisation_state = SKIP_MINIMISE_STATE;
|
||||
iq->chase_to_rd = 0;
|
||||
iq->dnssec_lame_query = 0;
|
||||
verbose(VERB_ALGO, "query response was timeout");
|
||||
|
|
@ -2142,6 +2244,15 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
sock_list_insert(&qstate->reply_origin,
|
||||
&qstate->reply->addr, qstate->reply->addrlen,
|
||||
qstate->region);
|
||||
if(iq->minimisation_state != DONOT_MINIMISE_STATE) {
|
||||
/* Best effort qname-minimisation.
|
||||
* Stop minimising and send full query when RCODE
|
||||
* is not NOERROR */
|
||||
if(FLAGS_GET_RCODE(iq->response->rep->flags) !=
|
||||
LDNS_RCODE_NOERROR)
|
||||
iq->minimisation_state = DONOT_MINIMISE_STATE;
|
||||
return next_state(iq, QUERYTARGETS_STATE);
|
||||
}
|
||||
return final_state(iq);
|
||||
} else if(type == RESPONSE_TYPE_REFERRAL) {
|
||||
/* REFERRAL type responses get a reset of the
|
||||
|
|
@ -2201,6 +2312,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
* point to the referral. */
|
||||
iq->deleg_msg = iq->response;
|
||||
iq->dp = delegpt_from_message(iq->response, qstate->region);
|
||||
if (qstate->env->cfg->qname_minimisation)
|
||||
iq->minimisation_state = INIT_MINIMISE_STATE;
|
||||
if(!iq->dp)
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
if(!cache_fill_missing(qstate->env, iq->qchase.qclass,
|
||||
|
|
@ -2280,6 +2393,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
/* set the current request's qname to the new value. */
|
||||
iq->qchase.qname = sname;
|
||||
iq->qchase.qname_len = snamelen;
|
||||
if (qstate->env->cfg->qname_minimisation)
|
||||
iq->minimisation_state = INIT_MINIMISE_STATE;
|
||||
/* Clear the query state, since this is a query restart. */
|
||||
iq->deleg_msg = NULL;
|
||||
iq->dp = NULL;
|
||||
|
|
@ -2353,6 +2468,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
/* LAME, THROWAWAY and "unknown" all end up here.
|
||||
* Recycle to the QUERYTARGETS state to hopefully try a
|
||||
* different target. */
|
||||
if (qstate->env->cfg->qname_minimisation)
|
||||
iq->minimisation_state = SKIP_MINIMISE_STATE;
|
||||
return next_state(iq, QUERYTARGETS_STATE);
|
||||
}
|
||||
|
||||
|
|
@ -2968,7 +3085,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
prs->flags &= ~BIT_CD;
|
||||
|
||||
/* normalize and sanitize: easy to delete items from linked lists */
|
||||
if(!scrub_message(pkt, prs, &iq->qchase, iq->dp->name,
|
||||
if(!scrub_message(pkt, prs, &iq->qinfo_out, iq->dp->name,
|
||||
qstate->env->scratch, qstate->env, ie)) {
|
||||
/* if 0x20 enabled, start fallback, but we have no message */
|
||||
if(event == module_event_capsfail && !iq->caps_fallback) {
|
||||
|
|
|
|||
|
|
@ -112,6 +112,32 @@ struct iter_env {
|
|||
* array of max_dependency_depth+1 size.
|
||||
*/
|
||||
int* target_fetch_policy;
|
||||
|
||||
/** ip6.arpa dname in wireformat, used for qname-minimisation */
|
||||
uint8_t* ip6arpa_dname;
|
||||
};
|
||||
|
||||
/**
|
||||
* QNAME minimisation state
|
||||
*/
|
||||
enum minimisation_state {
|
||||
/**
|
||||
* (Re)start minimisation. Outgoing QNAME should be set to dp->name.
|
||||
* State entered on new query or after following refferal or CNAME.
|
||||
*/
|
||||
INIT_MINIMISE_STATE = 0,
|
||||
/**
|
||||
* QNAME minimisataion ongoing. Increase QNAME on every iteration.
|
||||
*/
|
||||
MINIMISE_STATE,
|
||||
/**
|
||||
* Don't increment QNAME this iteration
|
||||
*/
|
||||
SKIP_MINIMISE_STATE,
|
||||
/**
|
||||
* Send out full QNAME + original QTYPE
|
||||
*/
|
||||
DONOT_MINIMISE_STATE,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -322,6 +348,15 @@ struct iter_qstate {
|
|||
|
||||
/** list of pending queries to authoritative servers. */
|
||||
struct outbound_list outlist;
|
||||
|
||||
/** QNAME minimisation state */
|
||||
enum minimisation_state minimisation_state;
|
||||
|
||||
/**
|
||||
* The query info that is sent upstream. Will be a subset of qchase
|
||||
* when qname minimisation is enabled.
|
||||
*/
|
||||
struct query_info qinfo_out;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -240,6 +240,7 @@ config_create(void)
|
|||
cfg->ratelimit_for_domain = NULL;
|
||||
cfg->ratelimit_below_domain = NULL;
|
||||
cfg->ratelimit_factor = 10;
|
||||
cfg->qname_minimisation = 0;
|
||||
return cfg;
|
||||
error_exit:
|
||||
config_delete(cfg);
|
||||
|
|
@ -473,6 +474,7 @@ int config_set_option(struct config_file* cfg, const char* opt,
|
|||
else S_MEMSIZE("ratelimit-size:", ratelimit_size)
|
||||
else S_POW2("ratelimit-slabs:", ratelimit_slabs)
|
||||
else S_NUMBER_OR_ZERO("ratelimit-factor:", ratelimit_factor)
|
||||
else S_YNO("qname-minimisation:", qname_minimisation)
|
||||
/* val_sig_skew_min and max are copied into val_env during init,
|
||||
* so this does not update val_env with set_option */
|
||||
else if(strcmp(opt, "val-sig-skew-min:") == 0)
|
||||
|
|
@ -747,6 +749,7 @@ config_get_option(struct config_file* cfg, const char* opt,
|
|||
else O_DEC(opt, "ratelimit-factor", ratelimit_factor)
|
||||
else O_DEC(opt, "val-sig-skew-min", val_sig_skew_min)
|
||||
else O_DEC(opt, "val-sig-skew-max", val_sig_skew_max)
|
||||
else O_YNO(opt, "qname-minimisation", qname_minimisation)
|
||||
/* not here:
|
||||
* outgoing-permit, outgoing-avoid - have list of ports
|
||||
* local-zone - zones and nodefault variables
|
||||
|
|
|
|||
|
|
@ -364,6 +364,8 @@ struct config_file {
|
|||
struct config_str2list* ratelimit_below_domain;
|
||||
/** ratelimit factor, 0 blocks all, 10 allows 1/10 of traffic */
|
||||
int ratelimit_factor;
|
||||
|
||||
int qname_minimisation;
|
||||
};
|
||||
|
||||
/** from cfg username, after daemonise setup performed */
|
||||
|
|
|
|||
4376
util/configlexer.c
4376
util/configlexer.c
File diff suppressed because it is too large
Load diff
|
|
@ -205,6 +205,7 @@ SQANY [^\'\n\r\\]|\\.
|
|||
/* note that flex makes the longest match and '.' is any but not nl */
|
||||
LEXOUT(("comment(%s) ", yytext)); /* ignore */ }
|
||||
server{COLON} { YDVAR(0, VAR_SERVER) }
|
||||
qname-minimisation{COLON} { YDVAR(1, VAR_QNAME_MINIMISATION) }
|
||||
num-threads{COLON} { YDVAR(1, VAR_NUM_THREADS) }
|
||||
verbosity{COLON} { YDVAR(1, VAR_VERBOSITY) }
|
||||
port{COLON} { YDVAR(1, VAR_PORT) }
|
||||
|
|
|
|||
1581
util/configparser.c
1581
util/configparser.c
File diff suppressed because it is too large
Load diff
|
|
@ -1,8 +1,8 @@
|
|||
/* A Bison parser, made by GNU Bison 2.7. */
|
||||
/* A Bison parser, made by GNU Bison 2.7.12-4996. */
|
||||
|
||||
/* Bison interface for Yacc-like parsers in C
|
||||
|
||||
Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
|
||||
Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -204,7 +204,8 @@ extern int yydebug;
|
|||
VAR_RATELIMIT_FACTOR = 413,
|
||||
VAR_CAPS_WHITELIST = 414,
|
||||
VAR_CACHE_MAX_NEGATIVE_TTL = 415,
|
||||
VAR_PERMIT_SMALL_HOLDDOWN = 416
|
||||
VAR_PERMIT_SMALL_HOLDDOWN = 416,
|
||||
VAR_QNAME_MINIMISATION = 417
|
||||
};
|
||||
#endif
|
||||
/* Tokens. */
|
||||
|
|
@ -367,20 +368,21 @@ extern int yydebug;
|
|||
#define VAR_CAPS_WHITELIST 414
|
||||
#define VAR_CACHE_MAX_NEGATIVE_TTL 415
|
||||
#define VAR_PERMIT_SMALL_HOLDDOWN 416
|
||||
#define VAR_QNAME_MINIMISATION 417
|
||||
|
||||
|
||||
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
typedef union YYSTYPE
|
||||
{
|
||||
/* Line 2058 of yacc.c */
|
||||
/* Line 2053 of yacc.c */
|
||||
#line 64 "./util/configparser.y"
|
||||
|
||||
char* str;
|
||||
|
||||
|
||||
/* Line 2058 of yacc.c */
|
||||
#line 384 "util/configparser.h"
|
||||
/* Line 2053 of yacc.c */
|
||||
#line 386 "util/configparser.h"
|
||||
} YYSTYPE;
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ extern struct config_parser_state* cfg_parser;
|
|||
%token VAR_RATELIMIT VAR_RATELIMIT_SLABS VAR_RATELIMIT_SIZE
|
||||
%token VAR_RATELIMIT_FOR_DOMAIN VAR_RATELIMIT_BELOW_DOMAIN VAR_RATELIMIT_FACTOR
|
||||
%token VAR_CAPS_WHITELIST VAR_CACHE_MAX_NEGATIVE_TTL VAR_PERMIT_SMALL_HOLDDOWN
|
||||
%token VAR_QNAME_MINIMISATION
|
||||
|
||||
%%
|
||||
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
|
||||
|
|
@ -186,7 +187,7 @@ content_server: server_num_threads | server_verbosity | server_port |
|
|||
server_ratelimit_size | server_ratelimit_for_domain |
|
||||
server_ratelimit_below_domain | server_ratelimit_factor |
|
||||
server_caps_whitelist | server_cache_max_negative_ttl |
|
||||
server_permit_small_holddown
|
||||
server_permit_small_holddown | server_qname_minimisation
|
||||
;
|
||||
stubstart: VAR_STUB_ZONE
|
||||
{
|
||||
|
|
@ -1318,6 +1319,16 @@ server_ratelimit_factor: VAR_RATELIMIT_FACTOR STRING_ARG
|
|||
free($2);
|
||||
}
|
||||
;
|
||||
server_qname_minimisation: VAR_QNAME_MINIMISATION STRING_ARG
|
||||
{
|
||||
OUTYY(("P(server_qname_minimisation:%s)\n", $2));
|
||||
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
|
||||
yyerror("expected yes or no.");
|
||||
else cfg_parser->cfg->qname_minimisation =
|
||||
(strcmp($2, "yes")==0);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
stub_name: VAR_NAME STRING_ARG
|
||||
{
|
||||
OUTYY(("P(name:%s)\n", $2));
|
||||
|
|
|
|||
Loading…
Reference in a new issue