mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-02-03 20:29:28 -05:00
Merge branch 'master' into cachedb-no-store
This commit is contained in:
commit
47094fd83f
22 changed files with 425 additions and 58 deletions
|
|
@ -793,7 +793,7 @@ iter_priv.lo iter_priv.o: $(srcdir)/iterator/iter_priv.c config.h $(srcdir)/iter
|
|||
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/net_help.h \
|
||||
$(srcdir)/util/storage/dnstree.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/sbuffer.h
|
||||
iter_resptype.lo iter_resptype.o: $(srcdir)/iterator/iter_resptype.c config.h \
|
||||
$(srcdir)/iterator/iter_resptype.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/util/log.h \
|
||||
$(srcdir)/iterator/iter_resptype.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/iterator/iterator.h $(srcdir)/util/log.h \
|
||||
$(srcdir)/services/cache/dns.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
|
||||
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/net_help.h \
|
||||
$(srcdir)/util/data/dname.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/pkthdr.h
|
||||
|
|
|
|||
|
|
@ -265,11 +265,11 @@ cachedb_init(struct module_env* env, int id)
|
|||
return 0;
|
||||
}
|
||||
cachedb_env->enabled = 1;
|
||||
if(env->cfg->serve_expired_reply_ttl)
|
||||
if(env->cfg->serve_expired && env->cfg->serve_expired_reply_ttl)
|
||||
log_warn(
|
||||
"cachedb: serve-expired-reply-ttl is set but not working for data "
|
||||
"originating from the external cache; 0 TLL is used for those.");
|
||||
if(env->cfg->serve_expired_client_timeout)
|
||||
"originating from the external cache; 0 TTL is used for those.");
|
||||
if(env->cfg->serve_expired && env->cfg->serve_expired_client_timeout)
|
||||
log_warn(
|
||||
"cachedb: serve-expired-client-timeout is set but not working for "
|
||||
"data originating from the external cache; expired data are used "
|
||||
|
|
|
|||
|
|
@ -59,11 +59,28 @@ struct redis_moddata {
|
|||
const char* server_path; /* server's unix path, or "", NULL if unused */
|
||||
const char* server_password; /* server's AUTH password, or "", NULL if unused */
|
||||
struct timeval timeout; /* timeout for connection setup and commands */
|
||||
int logical_db; /* the redis logical database to use */
|
||||
};
|
||||
|
||||
static redisReply* redis_command(struct module_env*, struct cachedb_env*,
|
||||
const char*, const uint8_t*, size_t);
|
||||
|
||||
static void
|
||||
moddata_clean(struct redis_moddata** moddata) {
|
||||
if(!moddata || !*moddata)
|
||||
return;
|
||||
if((*moddata)->ctxs) {
|
||||
int i;
|
||||
for(i = 0; i < (*moddata)->numctxs; i++) {
|
||||
if((*moddata)->ctxs[i])
|
||||
redisFree((*moddata)->ctxs[i]);
|
||||
}
|
||||
free((*moddata)->ctxs);
|
||||
}
|
||||
free(*moddata);
|
||||
*moddata = NULL;
|
||||
}
|
||||
|
||||
static redisContext*
|
||||
redis_connect(const struct redis_moddata* moddata)
|
||||
{
|
||||
|
|
@ -97,10 +114,21 @@ redis_connect(const struct redis_moddata* moddata)
|
|||
}
|
||||
freeReplyObject(rep);
|
||||
}
|
||||
if(moddata->logical_db > 0) {
|
||||
redisReply* rep;
|
||||
rep = redisCommand(ctx, "SELECT %d", moddata->logical_db);
|
||||
if(!rep || rep->type == REDIS_REPLY_ERROR) {
|
||||
log_err("failed to set logical database (%d)",
|
||||
moddata->logical_db);
|
||||
freeReplyObject(rep);
|
||||
goto fail;
|
||||
}
|
||||
freeReplyObject(rep);
|
||||
}
|
||||
verbose(VERB_OPS, "Connection to Redis established");
|
||||
return ctx;
|
||||
|
||||
fail:
|
||||
fail:
|
||||
if(ctx)
|
||||
redisFree(ctx);
|
||||
return NULL;
|
||||
|
|
@ -117,14 +145,13 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
|
|||
moddata = calloc(1, sizeof(struct redis_moddata));
|
||||
if(!moddata) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
goto fail;
|
||||
}
|
||||
moddata->numctxs = env->cfg->num_threads;
|
||||
moddata->ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*));
|
||||
if(!moddata->ctxs) {
|
||||
log_err("out of memory");
|
||||
free(moddata);
|
||||
return 0;
|
||||
goto fail;
|
||||
}
|
||||
/* note: server_host is a shallow reference to configured string.
|
||||
* we don't have to free it in this module. */
|
||||
|
|
@ -134,8 +161,15 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
|
|||
moddata->server_password = env->cfg->redis_server_password;
|
||||
moddata->timeout.tv_sec = env->cfg->redis_timeout / 1000;
|
||||
moddata->timeout.tv_usec = (env->cfg->redis_timeout % 1000) * 1000;
|
||||
for(i = 0; i < moddata->numctxs; i++)
|
||||
moddata->ctxs[i] = redis_connect(moddata);
|
||||
moddata->logical_db = env->cfg->redis_logical_db;
|
||||
for(i = 0; i < moddata->numctxs; i++) {
|
||||
redisContext* ctx = redis_connect(moddata);
|
||||
if(!ctx) {
|
||||
log_err("redis_init: failed to init redis");
|
||||
goto fail;
|
||||
}
|
||||
moddata->ctxs[i] = ctx;
|
||||
}
|
||||
cachedb_env->backend_data = moddata;
|
||||
if(env->cfg->redis_expire_records) {
|
||||
redisReply* rep = NULL;
|
||||
|
|
@ -148,7 +182,7 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
|
|||
log_err("redis_init: failed to init redis, the "
|
||||
"redis-expire-records option requires the SETEX command "
|
||||
"(redis >= 2.0.0)");
|
||||
return 0;
|
||||
goto fail;
|
||||
}
|
||||
redis_reply_type = rep->type;
|
||||
freeReplyObject(rep);
|
||||
|
|
@ -160,11 +194,14 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
|
|||
log_err("redis_init: failed to init redis, the "
|
||||
"redis-expire-records option requires the SETEX command "
|
||||
"(redis >= 2.0.0)");
|
||||
return 0;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
moddata_clean(&moddata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -175,18 +212,7 @@ redis_deinit(struct module_env* env, struct cachedb_env* cachedb_env)
|
|||
(void)env;
|
||||
|
||||
verbose(VERB_OPS, "Redis deinitialization");
|
||||
|
||||
if(!moddata)
|
||||
return;
|
||||
if(moddata->ctxs) {
|
||||
int i;
|
||||
for(i = 0; i < moddata->numctxs; i++) {
|
||||
if(moddata->ctxs[i])
|
||||
redisFree(moddata->ctxs[i]);
|
||||
}
|
||||
free(moddata->ctxs);
|
||||
}
|
||||
free(moddata);
|
||||
moddata_clean(&moddata);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -590,7 +590,7 @@ ssl_read_line(RES* res, char* buf, size_t max)
|
|||
while(1) {
|
||||
ssize_t rr = recv(res->fd, buf+len, 1, 0);
|
||||
if(rr <= 0) {
|
||||
if(rr == 0) {
|
||||
if(rr == 0 && len != 0) {
|
||||
buf[len] = 0;
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,27 @@
|
|||
11 October 2023: George
|
||||
- Fix #850: [FR] Ability to use specific database in Redis, with new
|
||||
redis-logical-db configuration option.
|
||||
|
||||
11 October 2023: Wouter
|
||||
- Fix #949: "could not create control compt".
|
||||
- Fix that cachedb does not warn when serve-expired is disabled about
|
||||
use of serve-expired-reply-ttl and serve-expired-client-timeout.
|
||||
- Fix for #949: Fix pythonmod/ubmodule-tst.py for Python 3.x.
|
||||
|
||||
10 October 2023: George
|
||||
- Fix infinite loop when reading multiple lines of input on a broken
|
||||
remote control socket. Addesses #947 and #948.
|
||||
|
||||
9 October 2023: Wouter
|
||||
- Fix edns subnet so that queries with a source prefix of zero cause
|
||||
the recursor send no edns subnet option to the upstream.
|
||||
- Fix that printout of EDNS options shows the EDNS cookie option by
|
||||
name.
|
||||
|
||||
4 October 2023: Wouter
|
||||
- Fix #946: Forwarder returns servfail on upstream response noerror no
|
||||
data.
|
||||
|
||||
3 October 2023: George
|
||||
- Merge #881: Generalise the proxy protocol code.
|
||||
|
||||
|
|
|
|||
|
|
@ -1236,6 +1236,8 @@ remote-control:
|
|||
# redis-timeout: 100
|
||||
# # set timeout on redis records based on DNS response TTL
|
||||
# redis-expire-records: no
|
||||
# # redis logical database to use, 0 is the default database.
|
||||
# redis-logical-db: 0
|
||||
|
||||
# IPSet
|
||||
# Add specify domain into set via ipset.
|
||||
|
|
|
|||
|
|
@ -2707,6 +2707,17 @@ Unbound is configured with \fBserve-expired\fR and \fBserve-expired-ttl\fR is 0,
|
|||
this option is internally reverted to "no". Redis SETEX support is required
|
||||
for this option (Redis >= 2.0.0).
|
||||
This option defaults to no.
|
||||
.TP
|
||||
.B redis-logical-db: \fI<logical database index>
|
||||
The logical database in Redis to use.
|
||||
These are databases in the same Redis instance sharing the same configuration
|
||||
and persisted in the same RDB/AOF file.
|
||||
If unsure about using this option, Redis documentation
|
||||
(https://redis.io/commands/select/) suggests not to use a single Redis instance
|
||||
for multiple unrelated applications.
|
||||
The default database in Redis is 0 while other logical databases need to be
|
||||
explicitly SELECT'ed upon connecting.
|
||||
This option defaults to 0.
|
||||
.SS DNSTAP Logging Options
|
||||
DNSTAP support, when compiled in by using \fB\-\-enable\-dnstap\fR, is enabled
|
||||
in the \fBdnstap:\fR section.
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@ int ecs_whitelist_check(struct query_info* qinfo,
|
|||
qstate->no_cache_store = 0;
|
||||
}
|
||||
|
||||
sq->subnet_sent_no_subnet = 0;
|
||||
if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream &&
|
||||
qstate->env->cfg->client_subnet_always_forward) ||
|
||||
ecs_is_whitelisted(sn_env->whitelist,
|
||||
|
|
@ -166,6 +167,14 @@ int ecs_whitelist_check(struct query_info* qinfo,
|
|||
* set. */
|
||||
if(!edns_opt_list_find(qstate->edns_opts_back_out,
|
||||
qstate->env->cfg->client_subnet_opcode)) {
|
||||
/* if the client is not wanting an EDNS subnet option,
|
||||
* omit it and store that we omitted it but actually
|
||||
* are doing EDNS subnet to the server. */
|
||||
if(sq->ecs_server_out.subnet_source_mask == 0) {
|
||||
sq->subnet_sent_no_subnet = 1;
|
||||
sq->subnet_sent = 0;
|
||||
return 1;
|
||||
}
|
||||
subnet_ecs_opt_list_append(&sq->ecs_server_out,
|
||||
&qstate->edns_opts_back_out, qstate, region);
|
||||
}
|
||||
|
|
@ -515,7 +524,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
|
|||
}
|
||||
|
||||
/* We have not asked for subnet data */
|
||||
if (!sq->subnet_sent) {
|
||||
if (!sq->subnet_sent && !sq->subnet_sent_no_subnet) {
|
||||
if (s_in->subnet_validdata)
|
||||
verbose(VERB_QUERY, "subnetcache: received spurious data");
|
||||
if (sq->subnet_downstream) /* Copy back to client */
|
||||
|
|
@ -524,7 +533,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
|
|||
}
|
||||
|
||||
/* subnet sent but nothing came back */
|
||||
if (!s_in->subnet_validdata) {
|
||||
if (!s_in->subnet_validdata && !sq->subnet_sent_no_subnet) {
|
||||
/* The authority indicated no support for edns subnet. As a
|
||||
* consequence the answer ended up in the regular cache. It
|
||||
* is still useful to put it in the edns subnet cache for
|
||||
|
|
@ -540,6 +549,18 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
|
|||
return module_finished;
|
||||
}
|
||||
|
||||
/* Purposefully there was no sent subnet, and there is consequently
|
||||
* no subnet in the answer. If there was, use the subnet in the answer
|
||||
* anyway. But if there is not, treat it as a prefix 0 answer. */
|
||||
if(sq->subnet_sent_no_subnet && !s_in->subnet_validdata) {
|
||||
/* Fill in 0.0.0.0/0 scope 0, or ::0/0 scope 0, for caching. */
|
||||
s_in->subnet_addr_fam = s_out->subnet_addr_fam;
|
||||
s_in->subnet_source_mask = 0;
|
||||
s_in->subnet_scope_mask = 0;
|
||||
memset(s_in->subnet_addr, 0, INET6_SIZE);
|
||||
s_in->subnet_validdata = 1;
|
||||
}
|
||||
|
||||
/* Being here means we have asked for and got a subnet specific
|
||||
* answer. Also, the answer from the authority is not yet cached
|
||||
* anywhere. */
|
||||
|
|
@ -556,6 +577,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
|
|||
(void)edns_opt_list_remove(&qstate->edns_opts_back_out,
|
||||
qstate->env->cfg->client_subnet_opcode);
|
||||
sq->subnet_sent = 0;
|
||||
sq->subnet_sent_no_subnet = 0;
|
||||
return module_restart_next;
|
||||
}
|
||||
|
||||
|
|
@ -676,6 +698,7 @@ ecs_query_response(struct module_qstate* qstate, struct dns_msg* response,
|
|||
edns_opt_list_remove(&qstate->edns_opts_back_out,
|
||||
qstate->env->cfg->client_subnet_opcode);
|
||||
sq->subnet_sent = 0;
|
||||
sq->subnet_sent_no_subnet = 0;
|
||||
memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out));
|
||||
} else if (!sq->track_max_scope &&
|
||||
FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR &&
|
||||
|
|
@ -737,6 +760,9 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id,
|
|||
sq->ecs_server_in.subnet_scope_mask >
|
||||
sq->max_scope))
|
||||
sq->max_scope = sq->ecs_server_in.subnet_scope_mask;
|
||||
} else if(sq->subnet_sent_no_subnet) {
|
||||
/* The answer can be stored as scope 0, not in global cache. */
|
||||
qstate->no_cache_store = 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -85,6 +85,13 @@ struct subnet_qstate {
|
|||
struct ecs_data ecs_server_out;
|
||||
int subnet_downstream;
|
||||
int subnet_sent;
|
||||
/**
|
||||
* If there was no subnet sent because the client used source prefix
|
||||
* length 0 for omitting the information. Then the answer is cached
|
||||
* like subnet was a /0 scope. Like the subnet_sent flag, but when
|
||||
* the EDNS subnet option is omitted because the client asked.
|
||||
*/
|
||||
int subnet_sent_no_subnet;
|
||||
/** keep track of longest received scope, set after receiving CNAME for
|
||||
* incoming QNAME. */
|
||||
int track_max_scope;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#include "config.h"
|
||||
#include "iterator/iter_resptype.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "iterator/iterator.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/dname.h"
|
||||
|
|
@ -105,7 +106,8 @@ response_type_from_cache(struct dns_msg* msg,
|
|||
|
||||
enum response_type
|
||||
response_type_from_server(int rdset,
|
||||
struct dns_msg* msg, struct query_info* request, struct delegpt* dp)
|
||||
struct dns_msg* msg, struct query_info* request, struct delegpt* dp,
|
||||
int* empty_nodata_found)
|
||||
{
|
||||
uint8_t* origzone = (uint8_t*)"\000"; /* the default */
|
||||
struct ub_packed_rrset_key* s;
|
||||
|
|
@ -284,13 +286,22 @@ response_type_from_server(int rdset,
|
|||
|
||||
/* If we've gotten this far, this is NOERROR/NODATA (which could
|
||||
* be an entirely empty message) */
|
||||
/* but ignore entirely empty messages, noerror/nodata has a soa
|
||||
* negative ttl value in the authority section, this makes it try
|
||||
* again at another authority. And turns it from a 5 second empty
|
||||
* message into a 5 second servfail response. */
|
||||
/* For entirely empty messages, try again, at first, then accept
|
||||
* it it happens more. A regular noerror/nodata response has a soa
|
||||
* negative ttl value in the authority section. This makes it try
|
||||
* again at another authority. And decides between storing a 5 second
|
||||
* empty message or a 5 second servfail response. */
|
||||
if(msg->rep->an_numrrsets == 0 && msg->rep->ns_numrrsets == 0 &&
|
||||
msg->rep->ar_numrrsets == 0)
|
||||
return RESPONSE_TYPE_THROWAWAY;
|
||||
msg->rep->ar_numrrsets == 0) {
|
||||
if(empty_nodata_found) {
|
||||
/* detect as throwaway at first, but accept later. */
|
||||
(*empty_nodata_found)++;
|
||||
if(*empty_nodata_found < EMPTY_NODATA_RETRY_COUNT)
|
||||
return RESPONSE_TYPE_THROWAWAY;
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
}
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
}
|
||||
/* check if recursive answer; saying it has empty cache */
|
||||
if( (msg->rep->flags&BIT_RA) && !(msg->rep->flags&BIT_AA) && !rdset)
|
||||
return RESPONSE_TYPE_REC_LAME;
|
||||
|
|
|
|||
|
|
@ -119,9 +119,11 @@ enum response_type response_type_from_cache(struct dns_msg* msg,
|
|||
* @param request: the request that generated the response.
|
||||
* @param dp: The delegation point that was being queried
|
||||
* when the response was returned.
|
||||
* @param empty_nodata_found: flag to keep track of empty nodata detection.
|
||||
* @return the response type (CNAME or ANSWER).
|
||||
*/
|
||||
enum response_type response_type_from_server(int rdset,
|
||||
struct dns_msg* msg, struct query_info* request, struct delegpt* dp);
|
||||
struct dns_msg* msg, struct query_info* request, struct delegpt* dp,
|
||||
int* empty_nodata_found);
|
||||
|
||||
#endif /* ITERATOR_ITER_RESPTYPE_H */
|
||||
|
|
|
|||
|
|
@ -2940,7 +2940,7 @@ static int
|
|||
processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
struct iter_env* ie, int id)
|
||||
{
|
||||
int dnsseclame = 0, origtypecname = 0;
|
||||
int dnsseclame = 0, origtypecname = 0, orig_empty_nodata_found;
|
||||
enum response_type type;
|
||||
|
||||
iq->num_current_queries--;
|
||||
|
|
@ -2960,12 +2960,25 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
return next_state(iq, QUERYTARGETS_STATE);
|
||||
}
|
||||
iq->timeout_count = 0;
|
||||
orig_empty_nodata_found = iq->empty_nodata_found;
|
||||
type = response_type_from_server(
|
||||
(int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd),
|
||||
iq->response, &iq->qinfo_out, iq->dp);
|
||||
iq->response, &iq->qinfo_out, iq->dp, &iq->empty_nodata_found);
|
||||
iq->chase_to_rd = 0;
|
||||
/* remove TC flag, if this is erroneously set by TCP upstream */
|
||||
iq->response->rep->flags &= ~BIT_TC;
|
||||
if(orig_empty_nodata_found != iq->empty_nodata_found &&
|
||||
iq->empty_nodata_found < EMPTY_NODATA_RETRY_COUNT) {
|
||||
/* try to search at another server */
|
||||
if(qstate->reply) {
|
||||
struct delegpt_addr* a = delegpt_find_addr(
|
||||
iq->dp, &qstate->reply->remote_addr,
|
||||
qstate->reply->remote_addrlen);
|
||||
/* make selection disprefer it */
|
||||
if(a) a->lame = 1;
|
||||
}
|
||||
return next_state(iq, QUERYTARGETS_STATE);
|
||||
}
|
||||
if(type == RESPONSE_TYPE_REFERRAL && (iq->chase_flags&BIT_RD) &&
|
||||
!iq->auth_zone_response) {
|
||||
/* When forwarding (RD bit is set), we handle referrals
|
||||
|
|
@ -3501,7 +3514,7 @@ processPrimeResponse(struct module_qstate* qstate, int id)
|
|||
iq->response->rep->flags &= ~(BIT_RD|BIT_RA); /* ignore rec-lame */
|
||||
type = response_type_from_server(
|
||||
(int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd),
|
||||
iq->response, &iq->qchase, iq->dp);
|
||||
iq->response, &iq->qchase, iq->dp, NULL);
|
||||
if(type == RESPONSE_TYPE_ANSWER) {
|
||||
qstate->return_rcode = LDNS_RCODE_NOERROR;
|
||||
qstate->return_msg = iq->response;
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@ extern int BLACKLIST_PENALTY;
|
|||
* Chosen so that the UNKNOWN_SERVER_NICENESS falls within the band of a
|
||||
* fast server, this causes server exploration as a side benefit. msec. */
|
||||
#define RTT_BAND 400
|
||||
/** Number of retries for empty nodata packets before it is accepted. */
|
||||
#define EMPTY_NODATA_RETRY_COUNT 2
|
||||
|
||||
/**
|
||||
* Global state for the iterator.
|
||||
|
|
@ -415,6 +417,11 @@ struct iter_qstate {
|
|||
*/
|
||||
int refetch_glue;
|
||||
|
||||
/**
|
||||
* This flag detects that a completely empty nodata was received,
|
||||
* already so that it is accepted later. */
|
||||
int empty_nodata_found;
|
||||
|
||||
/** list of pending queries to authoritative servers. */
|
||||
struct outbound_list outlist;
|
||||
|
||||
|
|
|
|||
|
|
@ -863,6 +863,9 @@ Result: ['74.125.43.147', '74.125.43.99', '74.125.43.103', '74.125.43.104']
|
|||
%inline %{
|
||||
//SWIG will see the ub_ctx as a class
|
||||
struct ub_ctx {
|
||||
/* Dummy member, so the struct is not empty, MSVC complains about
|
||||
* that. */
|
||||
int dummy;
|
||||
};
|
||||
%}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,12 @@
|
|||
POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
def init(id, cfg):
|
||||
log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script))
|
||||
scripts=[]
|
||||
s = cfg.python_script
|
||||
while s != None:
|
||||
scripts.append(s.str)
|
||||
s = s.next
|
||||
log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, scripts))
|
||||
return True
|
||||
|
||||
def deinit(id):
|
||||
|
|
@ -55,12 +60,15 @@ def setTTL(qstate, ttl):
|
|||
|
||||
def dataHex(data, prefix=""):
|
||||
res = ""
|
||||
for i in range(0, (len(data)+15)/16):
|
||||
for i in range(0, int((len(data)+15)/16)):
|
||||
res += "%s0x%02X | " % (prefix, i*16)
|
||||
d = map(lambda x:ord(x), data[i*16:i*16+17])
|
||||
if type(data[0]) == type(1):
|
||||
d = map(lambda x:int(x), data[i*16:i*16+17])
|
||||
else:
|
||||
d = map(lambda x:ord(x), data[i*16:i*16+17])
|
||||
for ch in d:
|
||||
res += "%02X " % ch
|
||||
for i in range(0,17-len(d)):
|
||||
for i in range(0,17-len(data[i*16:i*16+17])):
|
||||
res += " "
|
||||
res += "| "
|
||||
for ch in d:
|
||||
|
|
@ -72,31 +80,31 @@ def dataHex(data, prefix=""):
|
|||
return res
|
||||
|
||||
def printReturnMsg(qstate):
|
||||
print "Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount,qstate.return_msg.rep.security, qstate.return_msg.rep.ttl)
|
||||
print " qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str
|
||||
print("Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount,qstate.return_msg.rep.security, qstate.return_msg.rep.ttl))
|
||||
print(" qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str)
|
||||
if (qstate.return_msg.rep):
|
||||
print "RRSets:",qstate.return_msg.rep.rrset_count
|
||||
print("RRSets:",qstate.return_msg.rep.rrset_count)
|
||||
prevkey = None
|
||||
for i in range(0,qstate.return_msg.rep.rrset_count):
|
||||
r = qstate.return_msg.rep.rrsets[i]
|
||||
rk = r.rk
|
||||
print i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags,
|
||||
print "type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class)
|
||||
print(i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags)
|
||||
print("type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class))
|
||||
|
||||
d = r.entry.data
|
||||
print " RRDatas:",d.count+d.rrsig_count
|
||||
print(" RRDatas:",d.count+d.rrsig_count)
|
||||
for j in range(0,d.count+d.rrsig_count):
|
||||
print " ",j,":","TTL=",d.rr_ttl[j],"RR data:"
|
||||
print dataHex(d.rr_data[j]," ")
|
||||
print(" ",j,":","TTL=",d.rr_ttl[j],"RR data:")
|
||||
print(dataHex(d.rr_data[j]," "))
|
||||
|
||||
def operate(id, event, qstate, qdata):
|
||||
log_info("pythonmod: operate called, id: %d, event:%s" % (id, strmodulevent(event)))
|
||||
#print "pythonmod: per query data", qdata
|
||||
#print("pythonmod: per query data", qdata)
|
||||
|
||||
print "Query:", ''.join(map(lambda x:chr(max(32,ord(x))),qstate.qinfo.qname)), qstate.qinfo.qname_list,
|
||||
print "Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype,
|
||||
print "Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass
|
||||
print
|
||||
print("Query:", qstate.qinfo.qname, qstate.qinfo.qname_list, qstate.qinfo.qname_str)
|
||||
print("Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype)
|
||||
print("Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass)
|
||||
print("")
|
||||
|
||||
# TEST:
|
||||
# > dig @127.0.0.1 www.seznam.cz A
|
||||
|
|
@ -118,7 +126,7 @@ def operate(id, event, qstate, qdata):
|
|||
invalidateQueryInCache(qstate, qstate.return_msg.qinfo)
|
||||
|
||||
if (qstate.return_msg.rep.authoritative):
|
||||
print "X"*300
|
||||
print("X"*300)
|
||||
|
||||
setTTL(qstate, 10) #do cache nastavime TTL na 10
|
||||
if not storeQueryInCache(qstate, qstate.return_msg.qinfo, qstate.return_msg.rep, 0):
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@ static sldns_lookup_table sldns_edns_options_data[] = {
|
|||
{ 6, "DHU" },
|
||||
{ 7, "N3U" },
|
||||
{ 8, "edns-client-subnet" },
|
||||
{ 10, "COOKIE" },
|
||||
{ 11, "edns-tcp-keepalive"},
|
||||
{ 12, "Padding" },
|
||||
{ 15, "EDE"},
|
||||
|
|
|
|||
50
testdata/iter_ignore_empty.rpl
vendored
50
testdata/iter_ignore_empty.rpl
vendored
|
|
@ -78,6 +78,18 @@ example2.com. IN NS ns2.example2.com.
|
|||
SECTION ADDITIONAL
|
||||
ns2.example2.com. IN A 1.2.3.5
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode subdomain
|
||||
ADJUST copy_id copy_query
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
foo.com. IN NS
|
||||
SECTION AUTHORITY
|
||||
foo.com. IN NS ns.foo.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.foo.com. IN A 1.2.3.5
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; ns.example.com.
|
||||
|
|
@ -172,6 +184,27 @@ www.example.com. IN A
|
|||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
ENTRY_END
|
||||
|
||||
; foo.com
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR AA NOERROR
|
||||
SECTION QUESTION
|
||||
www.foo.com. IN A
|
||||
SECTION ANSWER
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR AA NOERROR
|
||||
SECTION QUESTION
|
||||
ns.foo.com. IN AAAA
|
||||
SECTION ANSWER
|
||||
SECTION AUTHORITY
|
||||
;foo.com. IN SOA ns2.foo.com root.foo.com 4 14400 3600 604800 3600
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
STEP 1 QUERY
|
||||
|
|
@ -195,4 +228,21 @@ ENTRY_END
|
|||
; wait for pending nameserver lookups.
|
||||
STEP 20 TRAFFIC
|
||||
|
||||
; Test that a nodata stays a nodata.
|
||||
STEP 30 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www.foo.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
STEP 40 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RD RA NOERROR
|
||||
SECTION QUESTION
|
||||
www.foo.com. IN A
|
||||
SECTION ANSWER
|
||||
ENTRY_END
|
||||
|
||||
SCENARIO_END
|
||||
|
|
|
|||
155
testdata/subnet_prezero.crpl
vendored
Normal file
155
testdata/subnet_prezero.crpl
vendored
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
; subnet unit test
|
||||
server:
|
||||
trust-anchor-signaling: no
|
||||
send-client-subnet: 1.2.3.4
|
||||
send-client-subnet: 1.2.3.5
|
||||
target-fetch-policy: "0 0 0 0 0"
|
||||
module-config: "subnetcache validator iterator"
|
||||
qname-minimisation: no
|
||||
minimal-responses: no
|
||||
|
||||
stub-zone:
|
||||
name: "example.com"
|
||||
stub-addr: 1.2.3.4
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN Test subnetcache source prefix zero from client.
|
||||
; In RFC7871 section-7.1.2 (para. 2).
|
||||
; It says that the recursor must send no EDNS subnet or its own address
|
||||
; in the EDNS subnet to the upstream server. And use that answer for the
|
||||
; source prefix length zero query. That type of query is for privacy.
|
||||
; The authority server is then going to use the resolver's IP, if any, to
|
||||
; tailor the answer to the query source address.
|
||||
|
||||
; ns.example.com
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 1.2.3.4
|
||||
|
||||
; reply with 0.0.0.0/0 in reply
|
||||
; For the test the answers for 0.0.0.0/0 queries are SERVFAIL, the normal
|
||||
; answers are NOERROR.
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname ednsdata
|
||||
ADJUST copy_id
|
||||
REPLY QR AA DO SERVFAIL
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN CNAME star.c10r.example.com.
|
||||
SECTION ADDITIONAL
|
||||
HEX_EDNSDATA_BEGIN
|
||||
00 08 00 04 ; OPCODE=subnet, optlen
|
||||
00 01 00 00 ; ip4, scope 0, source 0
|
||||
; 0.0.0.0/0
|
||||
HEX_EDNSDATA_END
|
||||
ENTRY_END
|
||||
|
||||
; reply without subnet
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR AA DO NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN CNAME star.c10r.example.com.
|
||||
ENTRY_END
|
||||
|
||||
; delegation answer for c10r.example.com, with subnet /0
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode subdomain ednsdata
|
||||
ADJUST copy_id copy_query
|
||||
REPLY QR DO SERVFAIL
|
||||
SECTION QUESTION
|
||||
c10r.example.com. IN NS
|
||||
SECTION AUTHORITY
|
||||
c10r.example.com. IN NS ns.c10r.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.c10r.example.com. IN A 1.2.3.5
|
||||
HEX_EDNSDATA_BEGIN
|
||||
00 08 00 04 ; OPCODE=subnet, optlen
|
||||
00 01 00 00 ; ip4, scope 0, source 0
|
||||
; 0.0.0.0/0
|
||||
HEX_EDNSDATA_END
|
||||
ENTRY_END
|
||||
|
||||
; delegation answer for c10r.example.com, without subnet
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode subdomain
|
||||
ADJUST copy_id copy_query
|
||||
REPLY QR DO NOERROR
|
||||
SECTION QUESTION
|
||||
c10r.example.com. IN NS
|
||||
SECTION AUTHORITY
|
||||
c10r.example.com. IN NS ns.c10r.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.c10r.example.com. IN A 1.2.3.5
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; ns.c10r.example.com
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 1.2.3.5
|
||||
|
||||
; reply with 0.0.0.0/0 in reply
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname ednsdata
|
||||
ADJUST copy_id
|
||||
REPLY QR AA DO SERVFAIL
|
||||
SECTION QUESTION
|
||||
star.c10r.example.com. IN A
|
||||
SECTION ANSWER
|
||||
star.c10r.example.com. IN A 1.2.3.6
|
||||
SECTION ADDITIONAL
|
||||
HEX_EDNSDATA_BEGIN
|
||||
00 08 00 04 ; OPCODE=subnet, optlen
|
||||
00 01 00 00 ; ip4, scope 0, source 0
|
||||
; 0.0.0.0/0
|
||||
HEX_EDNSDATA_END
|
||||
ENTRY_END
|
||||
|
||||
; reply without subnet
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR AA DO NOERROR
|
||||
SECTION QUESTION
|
||||
star.c10r.example.com. IN A
|
||||
SECTION ANSWER
|
||||
star.c10r.example.com. IN A 1.2.3.6
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; ask for www.example.com
|
||||
; server answers with CNAME to a delegation, that then
|
||||
; returns a /24 answer.
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD DO
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ADDITIONAL
|
||||
HEX_EDNSDATA_BEGIN
|
||||
00 08 00 04 ; OPCODE=subnet, optlen
|
||||
00 01 00 00 ; ip4, scope 0, source 0
|
||||
; 0.0.0.0/0
|
||||
HEX_EDNSDATA_END
|
||||
ENTRY_END
|
||||
|
||||
STEP 10 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all ednsdata
|
||||
REPLY QR RD RA DO NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN CNAME star.c10r.example.com.
|
||||
star.c10r.example.com. IN A 1.2.3.6
|
||||
SECTION ADDITIONAL
|
||||
HEX_EDNSDATA_BEGIN
|
||||
00 08 00 04 ; OPCODE=subnet, optlen
|
||||
00 01 00 00 ; ip4, scope 0, source 0
|
||||
; 0.0.0.0/0
|
||||
HEX_EDNSDATA_END
|
||||
ENTRY_END
|
||||
SCENARIO_END
|
||||
|
|
@ -389,6 +389,7 @@ config_create(void)
|
|||
cfg->redis_timeout = 100;
|
||||
cfg->redis_server_port = 6379;
|
||||
cfg->redis_expire_records = 0;
|
||||
cfg->redis_logical_db = 0;
|
||||
#endif /* USE_REDIS */
|
||||
#endif /* USE_CACHEDB */
|
||||
#ifdef USE_IPSET
|
||||
|
|
@ -1318,6 +1319,7 @@ config_get_option(struct config_file* cfg, const char* opt,
|
|||
else O_STR(opt, "redis-server-password", redis_server_password)
|
||||
else O_DEC(opt, "redis-timeout", redis_timeout)
|
||||
else O_YNO(opt, "redis-expire-records", redis_expire_records)
|
||||
else O_DEC(opt, "redis-logical-db", redis_logical_db)
|
||||
#endif /* USE_REDIS */
|
||||
#endif /* USE_CACHEDB */
|
||||
#ifdef USE_IPSET
|
||||
|
|
|
|||
|
|
@ -714,6 +714,8 @@ struct config_file {
|
|||
int redis_timeout;
|
||||
/** set timeout on redis records based on DNS response ttl */
|
||||
int redis_expire_records;
|
||||
/** set the redis logical database upon connection */
|
||||
int redis_logical_db;
|
||||
#endif
|
||||
#endif
|
||||
/** Downstream DNS Cookies */
|
||||
|
|
|
|||
|
|
@ -564,6 +564,7 @@ redis-server-path{COLON} { YDVAR(1, VAR_CACHEDB_REDISPATH) }
|
|||
redis-server-password{COLON} { YDVAR(1, VAR_CACHEDB_REDISPASSWORD) }
|
||||
redis-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) }
|
||||
redis-expire-records{COLON} { YDVAR(1, VAR_CACHEDB_REDISEXPIRERECORDS) }
|
||||
redis-logical-db{COLON} { YDVAR(1, VAR_CACHEDB_REDISLOGICALDB) }
|
||||
ipset{COLON} { YDVAR(0, VAR_IPSET) }
|
||||
name-v4{COLON} { YDVAR(1, VAR_IPSET_NAME_V4) }
|
||||
name-v6{COLON} { YDVAR(1, VAR_IPSET_NAME_V6) }
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ extern struct config_parser_state* cfg_parser;
|
|||
%token VAR_CACHEDB VAR_CACHEDB_BACKEND VAR_CACHEDB_SECRETSEED
|
||||
%token VAR_CACHEDB_REDISHOST VAR_CACHEDB_REDISPORT VAR_CACHEDB_REDISTIMEOUT
|
||||
%token VAR_CACHEDB_REDISEXPIRERECORDS VAR_CACHEDB_REDISPATH VAR_CACHEDB_REDISPASSWORD
|
||||
%token VAR_CACHEDB_REDISLOGICALDB
|
||||
%token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM
|
||||
%token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM
|
||||
%token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL
|
||||
|
|
@ -3702,7 +3703,7 @@ contents_cachedb: contents_cachedb content_cachedb
|
|||
content_cachedb: cachedb_backend_name | cachedb_secret_seed |
|
||||
redis_server_host | redis_server_port | redis_timeout |
|
||||
redis_expire_records | redis_server_path | redis_server_password |
|
||||
cachedb_no_store
|
||||
cachedb_no_store | redis_logical_db
|
||||
;
|
||||
cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG
|
||||
{
|
||||
|
|
@ -3818,6 +3819,21 @@ redis_expire_records: VAR_CACHEDB_REDISEXPIRERECORDS STRING_ARG
|
|||
free($2);
|
||||
}
|
||||
;
|
||||
redis_logical_db: VAR_CACHEDB_REDISLOGICALDB STRING_ARG
|
||||
{
|
||||
#if defined(USE_CACHEDB) && defined(USE_REDIS)
|
||||
int db;
|
||||
OUTYY(("P(redis_logical_db:%s)\n", $2));
|
||||
db = atoi($2);
|
||||
if((db == 0 && strcmp($2, "0") != 0) || db < 0)
|
||||
yyerror("valid redis logical database index expected");
|
||||
else cfg_parser->cfg->redis_logical_db = db;
|
||||
#else
|
||||
OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
|
||||
#endif
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_tcp_connection_limit: VAR_TCP_CONNECTION_LIMIT STRING_ARG STRING_ARG
|
||||
{
|
||||
OUTYY(("P(server_tcp_connection_limit:%s %s)\n", $2, $3));
|
||||
|
|
|
|||
Loading…
Reference in a new issue