Merge branch 'Talkabout-redis-expire-records'

This commit is contained in:
George Thessalonikefs 2020-04-01 17:22:38 +02:00
commit a601fd6d3c
12 changed files with 3165 additions and 3047 deletions

View file

@ -160,7 +160,7 @@ testframe_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
static void static void
testframe_store(struct module_env* env, struct cachedb_env* cachedb_env, testframe_store(struct module_env* env, struct cachedb_env* cachedb_env,
char* key, uint8_t* data, size_t data_len) char* key, uint8_t* data, size_t data_len, time_t ATTR_UNUSED(ttl))
{ {
struct testframe_moddata* d = (struct testframe_moddata*) struct testframe_moddata* d = (struct testframe_moddata*)
cachedb_env->backend_data; cachedb_env->backend_data;
@ -606,7 +606,8 @@ cachedb_extcache_store(struct module_qstate* qstate, struct cachedb_env* ie)
/* call backend */ /* call backend */
(*ie->backend->store)(qstate->env, ie, key, (*ie->backend->store)(qstate->env, ie, key,
sldns_buffer_begin(qstate->env->scratch_buffer), sldns_buffer_begin(qstate->env->scratch_buffer),
sldns_buffer_limit(qstate->env->scratch_buffer)); sldns_buffer_limit(qstate->env->scratch_buffer),
qstate->return_msg->rep->ttl);
} }
/** /**

View file

@ -84,7 +84,7 @@ struct cachedb_backend {
/** Store (env, cachedb_env, key, data, data_len) */ /** Store (env, cachedb_env, key, data, data_len) */
void (*store)(struct module_env*, struct cachedb_env*, char*, void (*store)(struct module_env*, struct cachedb_env*, char*,
uint8_t*, size_t); uint8_t*, size_t, time_t);
}; };
#define CACHEDB_HASHSIZE 256 /* bit hash */ #define CACHEDB_HASHSIZE 256 /* bit hash */

View file

@ -59,6 +59,9 @@ struct redis_moddata {
struct timeval timeout; /* timeout for connection setup and commands */ struct timeval timeout; /* timeout for connection setup and commands */
}; };
static redisReply* redis_command(struct module_env*, struct cachedb_env*,
const char*, const uint8_t*, size_t);
static redisContext* static redisContext*
redis_connect(const struct redis_moddata* moddata) redis_connect(const struct redis_moddata* moddata)
{ {
@ -114,6 +117,33 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
for(i = 0; i < moddata->numctxs; i++) for(i = 0; i < moddata->numctxs; i++)
moddata->ctxs[i] = redis_connect(moddata); moddata->ctxs[i] = redis_connect(moddata);
cachedb_env->backend_data = moddata; cachedb_env->backend_data = moddata;
if(env->cfg->redis_expire_records) {
redisReply* rep = NULL;
int redis_reply_type = 0;
/** check if setex command is supported */
rep = redis_command(env, cachedb_env,
"SETEX __UNBOUND_REDIS_CHECK__ 1 none", NULL, 0);
if(!rep) {
/** init failed, no response from redis server*/
log_err("redis_init: failed to init redis, the "
"redis-expire-records option requires the SETEX command "
"(redis >= 2.0.0)");
return 0;
}
redis_reply_type = rep->type;
freeReplyObject(rep);
switch(redis_reply_type) {
case REDIS_REPLY_STATUS:
break;
default:
/** init failed, setex command not supported */
log_err("redis_init: failed to init redis, the "
"redis-expire-records option requires the SETEX command "
"(redis >= 2.0.0)");
return 0;
}
}
return 1; return 1;
} }
@ -219,7 +249,7 @@ redis_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
rep = redis_command(env, cachedb_env, cmdbuf, NULL, 0); rep = redis_command(env, cachedb_env, cmdbuf, NULL, 0);
if(!rep) if(!rep)
return 0; return 0;
switch (rep->type) { switch(rep->type) {
case REDIS_REPLY_NIL: case REDIS_REPLY_NIL:
verbose(VERB_ALGO, "redis_lookup: no data cached"); verbose(VERB_ALGO, "redis_lookup: no data cached");
break; break;
@ -249,16 +279,33 @@ redis_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
static void static void
redis_store(struct module_env* env, struct cachedb_env* cachedb_env, redis_store(struct module_env* env, struct cachedb_env* cachedb_env,
char* key, uint8_t* data, size_t data_len) char* key, uint8_t* data, size_t data_len, time_t ttl)
{ {
redisReply* rep; redisReply* rep;
char cmdbuf[4+(CACHEDB_HASHSIZE/8)*2+3+1]; /* "SET " + key + " %b" */
int n; int n;
int set_ttl = (env->cfg->redis_expire_records &&
(!env->cfg->serve_expired || env->cfg->serve_expired_ttl > 0));
/* Supported commands:
* - "SET " + key + " %b"
* - "SETEX " + key + " " + ttl + " %b"
*/
char cmdbuf[6+(CACHEDB_HASHSIZE/8)*2+11+3+1];
if (!set_ttl) {
verbose(VERB_ALGO, "redis_store %s (%d bytes)", key, (int)data_len);
/* build command to set to a binary safe string */
n = snprintf(cmdbuf, sizeof(cmdbuf), "SET %s %%b", key);
} else {
/* add expired ttl time to redis ttl to avoid premature eviction of key */
ttl += env->cfg->serve_expired_ttl;
verbose(VERB_ALGO, "redis_store %s (%d bytes) with ttl %u",
key, (int)data_len, (uint32_t)ttl);
/* build command to set to a binary safe string */
n = snprintf(cmdbuf, sizeof(cmdbuf), "SETEX %s %u %%b", key,
(uint32_t)ttl);
}
verbose(VERB_ALGO, "redis_store %s (%d bytes)", key, (int)data_len);
/* build command to set to a binary safe string */
n = snprintf(cmdbuf, sizeof(cmdbuf), "SET %s %%b", key);
if(n < 0 || n >= (int)sizeof(cmdbuf)) { if(n < 0 || n >= (int)sizeof(cmdbuf)) {
log_err("redis_store: unexpected failure to build command"); log_err("redis_store: unexpected failure to build command");
return; return;

View file

@ -1,3 +1,6 @@
1 April 2020: George
- Merge PR #206: Redis TTL, by Talkabout.
30 March 2020: Wouter 30 March 2020: Wouter
- Merge PR #207: Clarify if-automatic listens on 0.0.0.0 and :: - Merge PR #207: Clarify if-automatic listens on 0.0.0.0 and ::
- Merge PR #208: Fix uncached CLIENT_RESPONSE'es on stateful - Merge PR #208: Fix uncached CLIENT_RESPONSE'es on stateful

View file

@ -2130,6 +2130,14 @@ If this timeout expires Unbound closes the connection, treats it as
if the Redis server does not have the requested data, and will try to if the Redis server does not have the requested data, and will try to
re-establish a new connection later. re-establish a new connection later.
This option defaults to 100 milliseconds. This option defaults to 100 milliseconds.
.TP
.B redis-expire-records: \fI<yes or no>
If Redis record expiration is enabled. If yes, unbound sets timeout for Redis
records so that Redis can evict keys that have expired automatically. If
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.
.SS DNSTAP Logging Options .SS DNSTAP Logging Options
DNSTAP support, when compiled in, is enabled in the \fBdnstap:\fR section. DNSTAP support, when compiled in, is enabled in the \fBdnstap:\fR section.
This starts an extra thread (when compiled with threading) that writes This starts an extra thread (when compiled with threading) that writes

View file

@ -337,6 +337,7 @@ config_create(void)
if(!(cfg->redis_server_host = strdup("127.0.0.1"))) goto error_exit; if(!(cfg->redis_server_host = strdup("127.0.0.1"))) goto error_exit;
cfg->redis_timeout = 100; cfg->redis_timeout = 100;
cfg->redis_server_port = 6379; cfg->redis_server_port = 6379;
cfg->redis_expire_records = 0;
#endif /* USE_REDIS */ #endif /* USE_REDIS */
#endif /* USE_CACHEDB */ #endif /* USE_CACHEDB */
#ifdef USE_IPSET #ifdef USE_IPSET
@ -1135,6 +1136,7 @@ config_get_option(struct config_file* cfg, const char* opt,
else O_STR(opt, "redis-server-host", redis_server_host) else O_STR(opt, "redis-server-host", redis_server_host)
else O_DEC(opt, "redis-server-port", redis_server_port) else O_DEC(opt, "redis-server-port", redis_server_port)
else O_DEC(opt, "redis-timeout", redis_timeout) else O_DEC(opt, "redis-timeout", redis_timeout)
else O_YNO(opt, "redis-expire-records", redis_expire_records)
#endif /* USE_REDIS */ #endif /* USE_REDIS */
#endif /* USE_CACHEDB */ #endif /* USE_CACHEDB */
#ifdef USE_IPSET #ifdef USE_IPSET

View file

@ -598,6 +598,8 @@ struct config_file {
int redis_server_port; int redis_server_port;
/** timeout (in ms) for communication with the redis server */ /** timeout (in ms) for communication with the redis server */
int redis_timeout; int redis_timeout;
/** set timeout on redis records based on DNS response ttl */
int redis_expire_records;
#endif #endif
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -500,6 +500,7 @@ secret-seed{COLON} { YDVAR(1, VAR_CACHEDB_SECRETSEED) }
redis-server-host{COLON} { YDVAR(1, VAR_CACHEDB_REDISHOST) } redis-server-host{COLON} { YDVAR(1, VAR_CACHEDB_REDISHOST) }
redis-server-port{COLON} { YDVAR(1, VAR_CACHEDB_REDISPORT) } redis-server-port{COLON} { YDVAR(1, VAR_CACHEDB_REDISPORT) }
redis-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) } redis-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) }
redis-expire-records{COLON} { YDVAR(1, VAR_CACHEDB_REDISEXPIRERECORDS) }
ipset{COLON} { YDVAR(0, VAR_IPSET) } ipset{COLON} { YDVAR(0, VAR_IPSET) }
name-v4{COLON} { YDVAR(1, VAR_IPSET_NAME_V4) } name-v4{COLON} { YDVAR(1, VAR_IPSET_NAME_V4) }
name-v6{COLON} { YDVAR(1, VAR_IPSET_NAME_V6) } name-v6{COLON} { YDVAR(1, VAR_IPSET_NAME_V6) }

File diff suppressed because it is too large Load diff

View file

@ -297,41 +297,42 @@ extern int yydebug;
VAR_CACHEDB_REDISHOST = 503, VAR_CACHEDB_REDISHOST = 503,
VAR_CACHEDB_REDISPORT = 504, VAR_CACHEDB_REDISPORT = 504,
VAR_CACHEDB_REDISTIMEOUT = 505, VAR_CACHEDB_REDISTIMEOUT = 505,
VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM = 506, VAR_CACHEDB_REDISEXPIRERECORDS = 506,
VAR_FOR_UPSTREAM = 507, VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM = 507,
VAR_AUTH_ZONE = 508, VAR_FOR_UPSTREAM = 508,
VAR_ZONEFILE = 509, VAR_AUTH_ZONE = 509,
VAR_MASTER = 510, VAR_ZONEFILE = 510,
VAR_URL = 511, VAR_MASTER = 511,
VAR_FOR_DOWNSTREAM = 512, VAR_URL = 512,
VAR_FALLBACK_ENABLED = 513, VAR_FOR_DOWNSTREAM = 513,
VAR_TLS_ADDITIONAL_PORT = 514, VAR_FALLBACK_ENABLED = 514,
VAR_LOW_RTT = 515, VAR_TLS_ADDITIONAL_PORT = 515,
VAR_LOW_RTT_PERMIL = 516, VAR_LOW_RTT = 516,
VAR_FAST_SERVER_PERMIL = 517, VAR_LOW_RTT_PERMIL = 517,
VAR_FAST_SERVER_NUM = 518, VAR_FAST_SERVER_PERMIL = 518,
VAR_ALLOW_NOTIFY = 519, VAR_FAST_SERVER_NUM = 519,
VAR_TLS_WIN_CERT = 520, VAR_ALLOW_NOTIFY = 520,
VAR_TCP_CONNECTION_LIMIT = 521, VAR_TLS_WIN_CERT = 521,
VAR_FORWARD_NO_CACHE = 522, VAR_TCP_CONNECTION_LIMIT = 522,
VAR_STUB_NO_CACHE = 523, VAR_FORWARD_NO_CACHE = 523,
VAR_LOG_SERVFAIL = 524, VAR_STUB_NO_CACHE = 524,
VAR_DENY_ANY = 525, VAR_LOG_SERVFAIL = 525,
VAR_UNKNOWN_SERVER_TIME_LIMIT = 526, VAR_DENY_ANY = 526,
VAR_LOG_TAG_QUERYREPLY = 527, VAR_UNKNOWN_SERVER_TIME_LIMIT = 527,
VAR_STREAM_WAIT_SIZE = 528, VAR_LOG_TAG_QUERYREPLY = 528,
VAR_TLS_CIPHERS = 529, VAR_STREAM_WAIT_SIZE = 529,
VAR_TLS_CIPHERSUITES = 530, VAR_TLS_CIPHERS = 530,
VAR_IPSET = 531, VAR_TLS_CIPHERSUITES = 531,
VAR_IPSET_NAME_V4 = 532, VAR_IPSET = 532,
VAR_IPSET_NAME_V6 = 533, VAR_IPSET_NAME_V4 = 533,
VAR_TLS_SESSION_TICKET_KEYS = 534, VAR_IPSET_NAME_V6 = 534,
VAR_RPZ = 535, VAR_TLS_SESSION_TICKET_KEYS = 535,
VAR_TAGS = 536, VAR_RPZ = 536,
VAR_RPZ_ACTION_OVERRIDE = 537, VAR_TAGS = 537,
VAR_RPZ_CNAME_OVERRIDE = 538, VAR_RPZ_ACTION_OVERRIDE = 538,
VAR_RPZ_LOG = 539, VAR_RPZ_CNAME_OVERRIDE = 539,
VAR_RPZ_LOG_NAME = 540 VAR_RPZ_LOG = 540,
VAR_RPZ_LOG_NAME = 541
}; };
#endif #endif
/* Tokens. */ /* Tokens. */
@ -583,41 +584,42 @@ extern int yydebug;
#define VAR_CACHEDB_REDISHOST 503 #define VAR_CACHEDB_REDISHOST 503
#define VAR_CACHEDB_REDISPORT 504 #define VAR_CACHEDB_REDISPORT 504
#define VAR_CACHEDB_REDISTIMEOUT 505 #define VAR_CACHEDB_REDISTIMEOUT 505
#define VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM 506 #define VAR_CACHEDB_REDISEXPIRERECORDS 506
#define VAR_FOR_UPSTREAM 507 #define VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM 507
#define VAR_AUTH_ZONE 508 #define VAR_FOR_UPSTREAM 508
#define VAR_ZONEFILE 509 #define VAR_AUTH_ZONE 509
#define VAR_MASTER 510 #define VAR_ZONEFILE 510
#define VAR_URL 511 #define VAR_MASTER 511
#define VAR_FOR_DOWNSTREAM 512 #define VAR_URL 512
#define VAR_FALLBACK_ENABLED 513 #define VAR_FOR_DOWNSTREAM 513
#define VAR_TLS_ADDITIONAL_PORT 514 #define VAR_FALLBACK_ENABLED 514
#define VAR_LOW_RTT 515 #define VAR_TLS_ADDITIONAL_PORT 515
#define VAR_LOW_RTT_PERMIL 516 #define VAR_LOW_RTT 516
#define VAR_FAST_SERVER_PERMIL 517 #define VAR_LOW_RTT_PERMIL 517
#define VAR_FAST_SERVER_NUM 518 #define VAR_FAST_SERVER_PERMIL 518
#define VAR_ALLOW_NOTIFY 519 #define VAR_FAST_SERVER_NUM 519
#define VAR_TLS_WIN_CERT 520 #define VAR_ALLOW_NOTIFY 520
#define VAR_TCP_CONNECTION_LIMIT 521 #define VAR_TLS_WIN_CERT 521
#define VAR_FORWARD_NO_CACHE 522 #define VAR_TCP_CONNECTION_LIMIT 522
#define VAR_STUB_NO_CACHE 523 #define VAR_FORWARD_NO_CACHE 523
#define VAR_LOG_SERVFAIL 524 #define VAR_STUB_NO_CACHE 524
#define VAR_DENY_ANY 525 #define VAR_LOG_SERVFAIL 525
#define VAR_UNKNOWN_SERVER_TIME_LIMIT 526 #define VAR_DENY_ANY 526
#define VAR_LOG_TAG_QUERYREPLY 527 #define VAR_UNKNOWN_SERVER_TIME_LIMIT 527
#define VAR_STREAM_WAIT_SIZE 528 #define VAR_LOG_TAG_QUERYREPLY 528
#define VAR_TLS_CIPHERS 529 #define VAR_STREAM_WAIT_SIZE 529
#define VAR_TLS_CIPHERSUITES 530 #define VAR_TLS_CIPHERS 530
#define VAR_IPSET 531 #define VAR_TLS_CIPHERSUITES 531
#define VAR_IPSET_NAME_V4 532 #define VAR_IPSET 532
#define VAR_IPSET_NAME_V6 533 #define VAR_IPSET_NAME_V4 533
#define VAR_TLS_SESSION_TICKET_KEYS 534 #define VAR_IPSET_NAME_V6 534
#define VAR_RPZ 535 #define VAR_TLS_SESSION_TICKET_KEYS 535
#define VAR_TAGS 536 #define VAR_RPZ 536
#define VAR_RPZ_ACTION_OVERRIDE 537 #define VAR_TAGS 537
#define VAR_RPZ_CNAME_OVERRIDE 538 #define VAR_RPZ_ACTION_OVERRIDE 538
#define VAR_RPZ_LOG 539 #define VAR_RPZ_CNAME_OVERRIDE 539
#define VAR_RPZ_LOG_NAME 540 #define VAR_RPZ_LOG 540
#define VAR_RPZ_LOG_NAME 541
/* Value type. */ /* Value type. */
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
@ -627,7 +629,7 @@ union YYSTYPE
char* str; char* str;
#line 631 "util/configparser.h" #line 633 "util/configparser.h"
}; };
typedef union YYSTYPE YYSTYPE; typedef union YYSTYPE YYSTYPE;

View file

@ -162,6 +162,7 @@ extern struct config_parser_state* cfg_parser;
%token VAR_IPSECMOD_MAX_TTL VAR_IPSECMOD_WHITELIST VAR_IPSECMOD_STRICT %token VAR_IPSECMOD_MAX_TTL VAR_IPSECMOD_WHITELIST VAR_IPSECMOD_STRICT
%token VAR_CACHEDB VAR_CACHEDB_BACKEND VAR_CACHEDB_SECRETSEED %token VAR_CACHEDB VAR_CACHEDB_BACKEND VAR_CACHEDB_SECRETSEED
%token VAR_CACHEDB_REDISHOST VAR_CACHEDB_REDISPORT VAR_CACHEDB_REDISTIMEOUT %token VAR_CACHEDB_REDISHOST VAR_CACHEDB_REDISPORT VAR_CACHEDB_REDISTIMEOUT
%token VAR_CACHEDB_REDISEXPIRERECORDS
%token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM %token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM
%token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM %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 %token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL
@ -3077,7 +3078,8 @@ cachedbstart: VAR_CACHEDB
contents_cachedb: contents_cachedb content_cachedb contents_cachedb: contents_cachedb content_cachedb
| ; | ;
content_cachedb: cachedb_backend_name | cachedb_secret_seed | content_cachedb: cachedb_backend_name | cachedb_secret_seed |
redis_server_host | redis_server_port | redis_timeout redis_server_host | redis_server_port | redis_timeout |
redis_expire_records
; ;
cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG
{ {
@ -3143,6 +3145,19 @@ redis_timeout: VAR_CACHEDB_REDISTIMEOUT STRING_ARG
free($2); free($2);
} }
; ;
redis_expire_records: VAR_CACHEDB_REDISEXPIRERECORDS STRING_ARG
{
#if defined(USE_CACHEDB) && defined(USE_REDIS)
OUTYY(("P(redis_expire_records:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->redis_expire_records = (strcmp($2, "yes")==0);
#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 server_tcp_connection_limit: VAR_TCP_CONNECTION_LIMIT STRING_ARG STRING_ARG
{ {
OUTYY(("P(server_tcp_connection_limit:%s %s)\n", $2, $3)); OUTYY(("P(server_tcp_connection_limit:%s %s)\n", $2, $3));