Merge branch 'master' into features/no-ttl-zero-cacherep

This commit is contained in:
Yorgos Thessalonikefs 2025-09-12 15:24:06 +02:00
commit d521135f66
277 changed files with 33780 additions and 5985 deletions

View file

@ -193,8 +193,12 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: false
persist-credentials: false
- name: test_windows
if: ${{ matrix.test_windows == 'yes' }}
env:
LIBEXPAT_FNAME: expat-2.7.0
LIBEXPAT_VERSION_DIR: R_2_7_0
shell: bash
run: |
export unboundpath=`pwd`
@ -237,9 +241,9 @@ jobs:
cd ..
mkdir expat
echo "curl expat"
curl -L -k -s -S -o expat-2.2.10.tar.gz https://github.com/libexpat/libexpat/releases/download/R_2_2_10/expat-2.2.10.tar.gz
tar xzf expat-2.2.10.tar.gz
cd expat-2.2.10
curl -L -k -s -S -o $LIBEXPAT_FNAME.tar.gz https://github.com/libexpat/libexpat/releases/download/$LIBEXPAT_VERSION_DIR/$LIBEXPAT_FNAME.tar.gz
tar xzf $LIBEXPAT_FNAME.tar.gz
cd $LIBEXPAT_FNAME
echo "./configure SHELL=/usr/bin/bash CONFIG_SHELL=/usr/bin/bash --prefix=\"$prepath/expat\" --exec-prefix=\"$prepath/expat\" --bindir=\"$prepath/expat/bin\" --includedir=\"$prepath/expat/include\" --mandir=\"$prepath/expat/man\" --libdir=\"$prepath/expat/lib\""
./configure SHELL=/usr/bin/bash CONFIG_SHELL=/usr/bin/bash --prefix="$prepath/expat" --exec-prefix="$prepath/expat" --bindir="$prepath/expat/bin" --includedir="$prepath/expat/include" --mandir="$prepath/expat/man" --libdir="$prepath/expat/lib"
# fixup SHELL is treated specially, but SHELZZ is not by make.

View file

@ -13,6 +13,8 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: configure
run: ./configure --enable-debug
- name: make

File diff suppressed because it is too large Load diff

70
ax_build_date_epoch.m4 Normal file
View file

@ -0,0 +1,70 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_build_date_epoch.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_BUILD_DATE_EPOCH(VARIABLE[, FORMAT[, ACTION-IF-FAIL]])
#
# DESCRIPTION
#
# Sets VARIABLE to a string representing the current time. It is
# formatted according to FORMAT if specified, otherwise it is formatted as
# the number of seconds (excluding leap seconds) since the UNIX epoch (01
# Jan 1970 00:00:00 UTC).
#
# If the SOURCE_DATE_EPOCH environment variable is set, it uses the value
# of that variable instead of the current time. See
# https://reproducible-builds.org/specs/source-date-epoch). If
# SOURCE_DATE_EPOCH is set but cannot be properly interpreted as a UNIX
# timestamp, then execute ACTION-IF-FAIL if specified, otherwise error.
#
# VARIABLE is AC_SUBST-ed.
#
# LICENSE
#
# Copyright (c) 2016 Eric Bavier <bavier@member.fsf.org>
#
# 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 the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 2
AC_DEFUN([AX_BUILD_DATE_EPOCH],
[dnl
AC_MSG_CHECKING([for build time])
ax_date_fmt="m4_default($2,%s)"
AS_IF([test x"$SOURCE_DATE_EPOCH" = x],
[$1=`date "+$ax_date_fmt"`],
[ax_build_date=`date -u -d "@$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null \
|| date -u -r "$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null`
AS_IF([test x"$ax_build_date" = x],
[m4_ifval([$3],
[$3],
[AC_MSG_ERROR([malformed SOURCE_DATE_EPOCH])])],
[$1=$ax_build_date])])
AC_MSG_RESULT([$$1])
])dnl AX_BUILD_DATE_EPOCH

View file

@ -47,6 +47,7 @@
#include "util/regional.h"
#include "util/net_help.h"
#include "util/config_file.h"
#include "util/data/dname.h"
#include "util/data/msgreply.h"
#include "util/data/msgencode.h"
#include "services/cache/dns.h"
@ -341,6 +342,7 @@ calc_hash(struct query_info* qinfo, struct module_env* env, char* buf,
/* copy the hash info into the clear buffer */
if(clen + qinfo->qname_len < sizeof(clear)) {
memmove(clear+clen, qinfo->qname, qinfo->qname_len);
query_dname_tolower(clear+clen);
clen += qinfo->qname_len;
}
if(clen + 4 < sizeof(clear)) {

View file

@ -46,26 +46,62 @@
#include "cachedb/cachedb.h"
#include "util/alloc.h"
#include "util/config_file.h"
#include "util/locks.h"
#include "util/timeval_func.h"
#include "sldns/sbuffer.h"
#ifdef USE_REDIS
#include "hiredis/hiredis.h"
struct redis_moddata {
redisContext** ctxs; /* thread-specific redis contexts */
int numctxs; /* number of ctx entries */
const char* server_host; /* server's IP address or host name */
int server_port; /* server's TCP port */
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 command_timeout; /* timeout for commands */
struct timeval connect_timeout; /* timeout for connect */
int logical_db; /* the redis logical database to use */
int setex_available; /* if the SETEX command is supported */
/* thread-specific redis contexts */
redisContext** ctxs;
redisContext** replica_ctxs;
/* number of ctx entries */
int numctxs;
/* server's IP address or host name */
const char* server_host;
const char* replica_server_host;
/* server's TCP port */
int server_port;
int replica_server_port;
/* server's unix path, or "", NULL if unused */
const char* server_path;
const char* replica_server_path;
/* server's AUTH password, or "", NULL if unused */
const char* server_password;
const char* replica_server_password;
/* timeout for commands */
struct timeval command_timeout;
struct timeval replica_command_timeout;
/* timeout for connection setup */
struct timeval connect_timeout;
struct timeval replica_connect_timeout;
/* the reconnect interval time. */
struct timeval reconnect_interval;
struct timeval replica_reconnect_interval;
/* reconnect attempts, 0 if connected, counts up failed reconnects. */
int reconnect_attempts;
int replica_reconnect_attempts;
/* Lock on reconnect_wait time. */
lock_basic_type wait_lock;
lock_basic_type replica_wait_lock;
/* reconnect wait time, wait until it has passed before reconnect. */
struct timeval reconnect_wait;
struct timeval replica_reconnect_wait;
/* the redis logical database to use */
int logical_db;
int replica_logical_db;
/* if the SET with EX command is supported */
int set_with_ex_available;
};
/** The limit on the number of redis connect attempts. After failure if
* the number is exceeded, the reconnects are throttled by the wait time. */
#define REDIS_RECONNECT_ATTEMPT_LIMIT 3
static redisReply* redis_command(struct module_env*, struct cachedb_env*,
const char*, const uint8_t*, size_t);
const char*, const uint8_t*, size_t, int);
static void
moddata_clean(struct redis_moddata** moddata) {
@ -79,63 +115,139 @@ moddata_clean(struct redis_moddata** moddata) {
}
free((*moddata)->ctxs);
}
if((*moddata)->replica_ctxs) {
int i;
for(i = 0; i < (*moddata)->numctxs; i++) {
if((*moddata)->replica_ctxs[i])
redisFree((*moddata)->replica_ctxs[i]);
}
free((*moddata)->replica_ctxs);
}
lock_basic_destroy(&(*moddata)->wait_lock);
lock_basic_destroy(&(*moddata)->replica_wait_lock);
free(*moddata);
*moddata = NULL;
}
static redisContext*
redis_connect(const struct redis_moddata* moddata)
redis_connect(const char* host, int port, const char* path,
const char* password, int logical_db,
const struct timeval connect_timeout,
const struct timeval command_timeout,
const struct timeval* reconnect_interval,
int* reconnect_attempts,
struct timeval* reconnect_wait,
lock_basic_type* wait_lock,
struct timeval* now_tv,
const char* infostr)
{
struct timeval now_val;
redisContext* ctx;
if(moddata->server_path && moddata->server_path[0]!=0) {
ctx = redisConnectUnixWithTimeout(moddata->server_path,
moddata->connect_timeout);
/* See if the redis server is down, and reconnect has to wait. */
if(*reconnect_attempts > REDIS_RECONNECT_ATTEMPT_LIMIT) {
/* Acquire lock to look at timeval, the integer has atomic
* integrity. */
struct timeval wait_tv;
if(now_tv) {
now_val = *now_tv;
} else {
if(gettimeofday(&now_val, NULL) < 0)
log_err("redis: gettimeofday: %s",
strerror(errno));
}
lock_basic_lock(wait_lock);
wait_tv = *reconnect_wait;
lock_basic_unlock(wait_lock);
if(timeval_smaller(&now_val, &wait_tv)) {
verbose(VERB_ALGO, "redis %sdown, reconnect wait",
infostr);
return NULL;
}
}
if(path && path[0]!=0) {
ctx = redisConnectUnixWithTimeout(path, connect_timeout);
} else {
ctx = redisConnectWithTimeout(moddata->server_host,
moddata->server_port, moddata->connect_timeout);
ctx = redisConnectWithTimeout(host, port, connect_timeout);
}
if(!ctx || ctx->err) {
const char *errstr = "out of memory";
if(ctx)
errstr = ctx->errstr;
log_err("failed to connect to redis server: %s", errstr);
log_err("failed to connect to redis %sserver: %s", infostr, errstr);
goto fail;
}
if(redisSetTimeout(ctx, moddata->command_timeout) != REDIS_OK) {
log_err("failed to set redis timeout, %s", ctx->errstr);
if(redisSetTimeout(ctx, command_timeout) != REDIS_OK) {
log_err("failed to set redis %stimeout, %s", infostr, ctx->errstr);
goto fail;
}
if(moddata->server_password && moddata->server_password[0]!=0) {
if(password && password[0]!=0) {
redisReply* rep;
rep = redisCommand(ctx, "AUTH %s", moddata->server_password);
rep = redisCommand(ctx, "AUTH %s", password);
if(!rep || rep->type == REDIS_REPLY_ERROR) {
log_err("failed to authenticate with password");
log_err("failed to authenticate %swith password", infostr);
freeReplyObject(rep);
goto fail;
}
freeReplyObject(rep);
}
if(moddata->logical_db > 0) {
if(logical_db > 0) {
redisReply* rep;
rep = redisCommand(ctx, "SELECT %d", moddata->logical_db);
rep = redisCommand(ctx, "SELECT %d", logical_db);
if(!rep || rep->type == REDIS_REPLY_ERROR) {
log_err("failed to set logical database (%d)",
moddata->logical_db);
log_err("failed %sto set logical database (%d)",
infostr, logical_db);
freeReplyObject(rep);
goto fail;
}
freeReplyObject(rep);
}
verbose(VERB_OPS, "Connection to Redis established");
*reconnect_attempts = 0;
if(verbosity >= VERB_OPS) {
char port_str[6+1];
port_str[0] = ' ';
(void)snprintf(port_str+1, sizeof(port_str)-1, "%d", port);
verbose(VERB_OPS, "Connection to Redis %sestablished (%s%s)",
infostr,
path&&path[0]!=0?path:host,
path&&path[0]!=0?"":port_str);
}
return ctx;
fail:
if(ctx)
redisFree(ctx);
(*reconnect_attempts)++;
if(*reconnect_attempts > REDIS_RECONNECT_ATTEMPT_LIMIT) {
/* Wait for the reconnect interval before trying again. */
struct timeval tv;
if(now_tv) {
now_val = *now_tv;
} else {
if(gettimeofday(&now_val, NULL) < 0)
log_err("redis: gettimeofday: %s",
strerror(errno));
}
tv = now_val;
timeval_add(&tv, reconnect_interval);
lock_basic_lock(wait_lock);
*reconnect_wait = tv;
lock_basic_unlock(wait_lock);
verbose(VERB_ALGO, "redis %sreconnect wait until %d.%6.6d",
infostr, (int)tv.tv_sec, (int)tv.tv_usec);
}
return NULL;
}
static void
set_timeout(struct timeval* timeout, int value, int explicit_value)
{
int v = explicit_value != 0 ? explicit_value : value;
timeout->tv_sec = v / 1000;
timeout->tv_usec = (v % 1000) * 1000;
}
static int
redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
{
@ -149,39 +261,76 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
log_err("out of memory");
goto fail;
}
lock_basic_init(&moddata->wait_lock);
lock_protect(&moddata->wait_lock, &moddata->reconnect_wait,
sizeof(moddata->reconnect_wait));
lock_basic_init(&moddata->replica_wait_lock);
lock_protect(&moddata->replica_wait_lock,
&moddata->replica_reconnect_wait,
sizeof(moddata->replica_reconnect_wait));
moddata->numctxs = env->cfg->num_threads;
/* note: server_host and similar string configuration options are
* shallow references to configured strings; we don't have to free them
* in this module. */
moddata->server_host = env->cfg->redis_server_host;
moddata->replica_server_host = env->cfg->redis_replica_server_host;
moddata->server_port = env->cfg->redis_server_port;
moddata->replica_server_port = env->cfg->redis_replica_server_port;
moddata->server_path = env->cfg->redis_server_path;
moddata->replica_server_path = env->cfg->redis_replica_server_path;
moddata->server_password = env->cfg->redis_server_password;
moddata->replica_server_password = env->cfg->redis_replica_server_password;
set_timeout(&moddata->command_timeout,
env->cfg->redis_timeout,
env->cfg->redis_command_timeout);
set_timeout(&moddata->replica_command_timeout,
env->cfg->redis_replica_timeout,
env->cfg->redis_replica_command_timeout);
set_timeout(&moddata->connect_timeout,
env->cfg->redis_timeout,
env->cfg->redis_connect_timeout);
set_timeout(&moddata->replica_connect_timeout,
env->cfg->redis_replica_timeout,
env->cfg->redis_replica_connect_timeout);
set_timeout(&moddata->reconnect_interval, 1000, 0);
set_timeout(&moddata->replica_reconnect_interval, 1000, 0);
moddata->logical_db = env->cfg->redis_logical_db;
moddata->replica_logical_db = env->cfg->redis_replica_logical_db;
moddata->ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*));
if(!moddata->ctxs) {
log_err("out of memory");
goto fail;
}
/* note: server_host is a shallow reference to configured string.
* we don't have to free it in this module. */
moddata->server_host = env->cfg->redis_server_host;
moddata->server_port = env->cfg->redis_server_port;
moddata->server_path = env->cfg->redis_server_path;
moddata->server_password = env->cfg->redis_server_password;
moddata->command_timeout.tv_sec = env->cfg->redis_timeout / 1000;
moddata->command_timeout.tv_usec =
(env->cfg->redis_timeout % 1000) * 1000;
moddata->connect_timeout.tv_sec = env->cfg->redis_timeout / 1000;
moddata->connect_timeout.tv_usec =
(env->cfg->redis_timeout % 1000) * 1000;
if(env->cfg->redis_command_timeout != 0) {
moddata->command_timeout.tv_sec =
env->cfg->redis_command_timeout / 1000;
moddata->command_timeout.tv_usec =
(env->cfg->redis_command_timeout % 1000) * 1000;
if((moddata->replica_server_host && moddata->replica_server_host[0]!=0)
|| (moddata->replica_server_path && moddata->replica_server_path[0]!=0)) {
/* There is a replica configured, allocate ctxs */
moddata->replica_ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*));
if(!moddata->replica_ctxs) {
log_err("out of memory");
goto fail;
}
}
if(env->cfg->redis_connect_timeout != 0) {
moddata->connect_timeout.tv_sec =
env->cfg->redis_connect_timeout / 1000;
moddata->connect_timeout.tv_usec =
(env->cfg->redis_connect_timeout % 1000) * 1000;
}
moddata->logical_db = env->cfg->redis_logical_db;
for(i = 0; i < moddata->numctxs; i++) {
redisContext* ctx = redis_connect(moddata);
redisContext* ctx = redis_connect(
moddata->server_host,
moddata->server_port,
moddata->server_path,
moddata->server_password,
moddata->logical_db,
moddata->connect_timeout,
moddata->command_timeout,
&moddata->reconnect_interval,
&moddata->reconnect_attempts,
&moddata->reconnect_wait,
&moddata->wait_lock,
env->now_tv,
"");
if(!ctx) {
log_err("redis_init: failed to init redis "
"(for thread %d)", i);
@ -190,17 +339,42 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
}
moddata->ctxs[i] = ctx;
}
if(moddata->replica_ctxs) {
for(i = 0; i < moddata->numctxs; i++) {
redisContext* ctx = redis_connect(
moddata->replica_server_host,
moddata->replica_server_port,
moddata->replica_server_path,
moddata->replica_server_password,
moddata->replica_logical_db,
moddata->replica_connect_timeout,
moddata->replica_command_timeout,
&moddata->replica_reconnect_interval,
&moddata->replica_reconnect_attempts,
&moddata->replica_reconnect_wait,
&moddata->replica_wait_lock,
env->now_tv,
"replica ");
if(!ctx) {
log_err("redis_init: failed to init redis "
"replica (for thread %d)", i);
/* And continue, the context can be established
* later, just like after a disconnect. */
}
moddata->replica_ctxs[i] = ctx;
}
}
cachedb_env->backend_data = moddata;
if(env->cfg->redis_expire_records &&
moddata->ctxs[env->alloc->thread_num] != NULL) {
redisReply* rep = NULL;
int redis_reply_type = 0;
/** check if setex command is supported */
/** check if set with ex command is supported */
rep = redis_command(env, cachedb_env,
"SETEX __UNBOUND_REDIS_CHECK__ 1 none", NULL, 0);
"SET __UNBOUND_REDIS_CHECK__ none EX 1", NULL, 0, 1);
if(!rep) {
/** init failed, no response from redis server*/
goto setex_fail;
goto set_with_ex_fail;
}
redis_reply_type = rep->type;
freeReplyObject(rep);
@ -208,17 +382,17 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
case REDIS_REPLY_STATUS:
break;
default:
/** init failed, setex command not supported */
goto setex_fail;
/** init failed, set_with_ex command not supported */
goto set_with_ex_fail;
}
moddata->setex_available = 1;
moddata->set_with_ex_available = 1;
}
return 1;
setex_fail:
set_with_ex_fail:
log_err("redis_init: failure during redis_init, the "
"redis-expire-records option requires the SETEX command "
"(redis >= 2.0.0)");
"redis-expire-records option requires the SET with EX command "
"(redis >= 2.6.12)");
return 1;
fail:
moddata_clean(&moddata);
@ -250,9 +424,9 @@ redis_deinit(struct module_env* env, struct cachedb_env* cachedb_env)
*/
static redisReply*
redis_command(struct module_env* env, struct cachedb_env* cachedb_env,
const char* command, const uint8_t* data, size_t data_len)
const char* command, const uint8_t* data, size_t data_len, int write)
{
redisContext* ctx;
redisContext* ctx, **ctx_selector;
redisReply* rep;
struct redis_moddata* d = (struct redis_moddata*)
cachedb_env->backend_data;
@ -263,17 +437,50 @@ redis_command(struct module_env* env, struct cachedb_env* cachedb_env,
* assumption throughout the unbound architecture, so we simply assert
* it. */
log_assert(env->alloc->thread_num < d->numctxs);
ctx = d->ctxs[env->alloc->thread_num];
ctx_selector = !write && d->replica_ctxs
?d->replica_ctxs
:d->ctxs;
ctx = ctx_selector[env->alloc->thread_num];
/* If we've not established a connection to the server or we've closed
* it on a failure, try to re-establish a new one. Failures will be
* logged in redis_connect(). */
if(!ctx) {
ctx = redis_connect(d);
d->ctxs[env->alloc->thread_num] = ctx;
if(!write && d->replica_ctxs) {
ctx = redis_connect(
d->replica_server_host,
d->replica_server_port,
d->replica_server_path,
d->replica_server_password,
d->replica_logical_db,
d->replica_connect_timeout,
d->replica_command_timeout,
&d->replica_reconnect_interval,
&d->replica_reconnect_attempts,
&d->replica_reconnect_wait,
&d->replica_wait_lock,
env->now_tv,
"replica ");
} else {
ctx = redis_connect(
d->server_host,
d->server_port,
d->server_path,
d->server_password,
d->logical_db,
d->connect_timeout,
d->command_timeout,
&d->reconnect_interval,
&d->reconnect_attempts,
&d->reconnect_wait,
&d->wait_lock,
env->now_tv,
"");
}
ctx_selector[env->alloc->thread_num] = ctx;
}
if(!ctx)
return NULL;
if(!ctx) return NULL;
/* Send the command and get a reply, synchronously. */
rep = (redisReply*)redisCommand(ctx, command, data, data_len);
@ -283,7 +490,7 @@ redis_command(struct module_env* env, struct cachedb_env* cachedb_env,
log_err("redis_command: failed to receive a reply, "
"closing connection: %s", ctx->errstr);
redisFree(ctx);
d->ctxs[env->alloc->thread_num] = NULL;
ctx_selector[env->alloc->thread_num] = NULL;
return NULL;
}
@ -301,7 +508,14 @@ redis_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
char* key, struct sldns_buffer* result_buffer)
{
redisReply* rep;
char cmdbuf[4+(CACHEDB_HASHSIZE/8)*2+1]; /* "GET " + key */
/* Supported commands:
* - "GET " + key
*/
#define REDIS_LOOKUP_MAX_BUF_LEN \
4 /* "GET " */ \
+(CACHEDB_HASHSIZE/8)*2 /* key hash */ \
+ 1 /* \0 */
char cmdbuf[REDIS_LOOKUP_MAX_BUF_LEN];
int n;
int ret = 0;
@ -313,7 +527,7 @@ redis_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
return 0;
}
rep = redis_command(env, cachedb_env, cmdbuf, NULL, 0);
rep = redis_command(env, cachedb_env, cmdbuf, NULL, 0, 0);
if(!rep)
return 0;
switch(rep->type) {
@ -352,27 +566,43 @@ redis_store(struct module_env* env, struct cachedb_env* cachedb_env,
int n;
struct redis_moddata* moddata = (struct redis_moddata*)
cachedb_env->backend_data;
int set_ttl = (moddata->setex_available &&
int set_ttl = (moddata->set_with_ex_available &&
env->cfg->redis_expire_records &&
(!env->cfg->serve_expired || env->cfg->serve_expired_ttl > 0));
/* Supported commands:
* - "SET " + key + " %b"
* - "SETEX " + key + " " + ttl + " %b"
* - "SET " + key + " %b EX " + ttl
* older redis 2.0.0 was "SETEX " + key + " " + ttl + " %b"
* - "EXPIRE " + key + " 0"
*/
char cmdbuf[6+(CACHEDB_HASHSIZE/8)*2+11+3+1];
#define REDIS_STORE_MAX_BUF_LEN \
7 /* "EXPIRE " */ \
+(CACHEDB_HASHSIZE/8)*2 /* key hash */ \
+ 7 /* " %b EX " */ \
+ 20 /* ttl (uint64_t) */ \
+ 1 /* \0 */
char cmdbuf[REDIS_STORE_MAX_BUF_LEN];
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 if(ttl == 0) {
/* use the EXPIRE command, SET with EX 0 is an invalid time. */
/* Replies with REDIS_REPLY_INTEGER of 1. */
verbose(VERB_ALGO, "redis_store expire %s (%d bytes)",
key, (int)data_len);
n = snprintf(cmdbuf, sizeof(cmdbuf), "EXPIRE %s 0", key);
data = NULL;
data_len = 0;
} 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);
key, (int)data_len, (unsigned)(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);
n = snprintf(cmdbuf, sizeof(cmdbuf), "SET %s %%b EX %u", key,
(unsigned)(uint32_t)ttl);
}
@ -381,11 +611,12 @@ redis_store(struct module_env* env, struct cachedb_env* cachedb_env,
return;
}
rep = redis_command(env, cachedb_env, cmdbuf, data, data_len);
rep = redis_command(env, cachedb_env, cmdbuf, data, data_len, 1);
if(rep) {
verbose(VERB_ALGO, "redis_store set completed");
if(rep->type != REDIS_REPLY_STATUS &&
rep->type != REDIS_REPLY_ERROR) {
rep->type != REDIS_REPLY_ERROR &&
rep->type != REDIS_REPLY_INTEGER) {
log_err("redis_store: unexpected type of reply (%d)",
rep->type);
}

View file

@ -57,7 +57,7 @@ int getnameinfo(const struct sockaddr *sa, size_t ATTR_UNUSED(salen), char *host
}
if (host != NULL) {
if (flags & NI_NUMERICHOST) {
if ((flags & NI_NUMERICHOST)) {
if (strlcpy(host, inet_ntoa(sin->sin_addr),
hostlen) >= hostlen)
return (EAI_MEMORY);
@ -168,7 +168,7 @@ getaddrinfo(const char *hostname, const char *servname,
port = 0;
}
if (hints && hints->ai_flags & AI_PASSIVE) {
if (hints && (hints->ai_flags & AI_PASSIVE)) {
addr = htonl(0x00000000);
if (hostname && inet_aton(hostname, &in) != 0)
addr = in.s_addr;
@ -193,7 +193,7 @@ getaddrinfo(const char *hostname, const char *servname,
}
/* Don't try DNS if AI_NUMERICHOST is set */
if (hints && hints->ai_flags & AI_NUMERICHOST)
if (hints && (hints->ai_flags & AI_NUMERICHOST))
return (EAI_NONAME);
hp = gethostbyname(hostname);

View file

@ -5,12 +5,8 @@
#undef malloc
#include <sys/types.h>
#ifndef USE_WINSOCK
void *malloc ();
#else
/* provide a prototype */
void *malloc (size_t n);
#endif
/* Allocate an N-byte block of memory from the heap.
If N is zero, allocate a 1-byte block. */

View file

@ -173,6 +173,10 @@
0 if you don't. */
#undef HAVE_DECL_SSL_CTX_SET_ECDH_AUTO
/* Define to 1 if you have the declaration of `SSL_CTX_set_tmp_ecdh', and to 0
if you don't. */
#undef HAVE_DECL_SSL_CTX_SET_TMP_ECDH
/* Define to 1 if you have the declaration of `strlcat', and to 0 if you
don't. */
#undef HAVE_DECL_STRLCAT
@ -378,6 +382,9 @@
/* Define if we have LibreSSL */
#undef HAVE_LIBRESSL
/* If we have atomic_store */
#undef HAVE_LINK_ATOMIC_STORE
/* Define to 1 if you have the <linux/net_tstamp.h> header file. */
#undef HAVE_LINUX_NET_TSTAMP_H
@ -474,6 +481,9 @@
`ngtcp2_crypto_quictls_from_ossl_encryption_level' function. */
#undef HAVE_NGTCP2_CRYPTO_QUICTLS_FROM_OSSL_ENCRYPTION_LEVEL
/* Define to 1 if you have the `ngtcp2_crypto_quictls_init' function. */
#undef HAVE_NGTCP2_CRYPTO_QUICTLS_INIT
/* Define to 1 if the system has the type `ngtcp2_encryption_level'. */
#undef HAVE_NGTCP2_ENCRYPTION_LEVEL
@ -481,6 +491,9 @@
*/
#undef HAVE_NGTCP2_NGTCP2_CRYPTO_OPENSSL_H
/* Define to 1 if you have the <ngtcp2/ngtcp2_crypto_ossl.h> header file. */
#undef HAVE_NGTCP2_NGTCP2_CRYPTO_OSSL_H
/* Define to 1 if you have the <ngtcp2/ngtcp2_crypto_quictls.h> header file.
*/
#undef HAVE_NGTCP2_NGTCP2_CRYPTO_QUICTLS_H
@ -642,9 +655,6 @@
function. */
#undef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_EVP_CB
/* Define to 1 if you have the `SSL_CTX_set_tmp_ecdh' function. */
#undef HAVE_SSL_CTX_SET_TMP_ECDH
/* Define to 1 if you have the `SSL_get0_alpn_selected' function. */
#undef HAVE_SSL_GET0_ALPN_SELECTED
@ -663,6 +673,9 @@
/* Define to 1 if you have the <stdarg.h> header file. */
#undef HAVE_STDARG_H
/* Define to 1 if you have the <stdatomic.h> header file. */
#undef HAVE_STDATOMIC_H
/* Define to 1 if you have the <stdbool.h> header file. */
#undef HAVE_STDBOOL_H
@ -1017,6 +1030,9 @@
/* Define this to enable client TCP Fast Open. */
#undef USE_MSG_FASTOPEN
/* Define this to use ngtcp2_crypto_ossl. */
#undef USE_NGTCP2_CRYPTO_OSSL
/* Define this to enable client TCP Fast Open. */
#undef USE_OSX_MSG_FASTOPEN

369
configure vendored
View file

@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for unbound 1.22.1.
# Generated by GNU Autoconf 2.71 for unbound 1.23.2.
#
# Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>.
#
@ -622,8 +622,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='unbound'
PACKAGE_TARNAME='unbound'
PACKAGE_VERSION='1.22.1'
PACKAGE_STRING='unbound 1.22.1'
PACKAGE_VERSION='1.23.2'
PACKAGE_STRING='unbound 1.23.2'
PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues'
PACKAGE_URL=''
@ -685,7 +685,9 @@ opt_dnstap_socket_path
ENABLE_DNSTAP
PROTOBUFC_LIBS
PROTOBUFC_CFLAGS
PROTOC_GEN_C
PROTOC_C
PROTOC
UBSYMS
EXTRALINK
COMMON_OBJ_ALL_SYMBOLS
@ -712,6 +714,7 @@ SSLLIB
HAVE_SSL
PC_CRYPTO_DEPENDENCY
CONFIG_DATE
SOURCE_DATE_EPOCH
GCC_DOCKER_LINTFLAGS
NETBSD_LINTFLAGS
PYUNBOUND_UNINSTALL
@ -959,6 +962,7 @@ SYSTEMD_LIBS
SYSTEMD_DAEMON_CFLAGS
SYSTEMD_DAEMON_LIBS
PYTHON_VERSION
SOURCE_DATE_EPOCH
PROTOBUFC_CFLAGS
PROTOBUFC_LIBS'
@ -1509,7 +1513,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures unbound 1.22.1 to adapt to many kinds of systems.
\`configure' configures unbound 1.23.2 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1575,7 +1579,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of unbound 1.22.1:";;
short | recursive ) echo "Configuration of unbound 1.23.2:";;
esac
cat <<\_ACEOF
@ -1752,6 +1756,10 @@ Some influential environment variables:
The installed Python version to use, for example '2.3'. This
string will be appended to the Python interpreter canonical
name.
SOURCE_DATE_EPOCH
If it is set, it uses the value of that variable instead of the
current time as the build timestamp. The format is a unix
timestamp. This enables reproducible build output.
PROTOBUFC_CFLAGS
C compiler flags for PROTOBUFC, overriding pkg-config
PROTOBUFC_LIBS
@ -1824,7 +1832,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
unbound configure 1.22.1
unbound configure 1.23.2
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@ -2481,7 +2489,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by unbound $as_me 1.22.1, which was
It was created by unbound $as_me 1.23.2, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@ -3243,13 +3251,13 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
UNBOUND_VERSION_MAJOR=1
UNBOUND_VERSION_MINOR=22
UNBOUND_VERSION_MINOR=23
UNBOUND_VERSION_MICRO=1
UNBOUND_VERSION_MICRO=2
LIBUNBOUND_CURRENT=9
LIBUNBOUND_REVISION=31
LIBUNBOUND_REVISION=33
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@ -3347,7 +3355,9 @@ LIBUNBOUND_AGE=1
# 1.21.0 had 9:28:1
# 1.21.1 had 9:29:1
# 1.22.0 had 9:30:1
# 1.22.1 had 9:31:1
# 1.23.0 had 9:31:1
# 1.23.1 had 9:32:1
# 1.23.2 had 9:33:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
@ -16094,6 +16104,14 @@ then :
fi
ac_fn_c_check_header_compile "$LINENO" "stdatomic.h" "ac_cv_header_stdatomic_h" "$ac_includes_default
"
if test "x$ac_cv_header_stdatomic_h" = xyes
then :
printf "%s\n" "#define HAVE_STDATOMIC_H 1" >>confdefs.h
fi
# check for types.
# Using own tests for int64* because autoconf builtin only give 32bit.
@ -19894,7 +19912,26 @@ if test "`uname`" = "Linux"; then
GCC_DOCKER_LINTFLAGS='-syntax'
fi
CONFIG_DATE=`date +%Y%m%d`
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for build time" >&5
printf %s "checking for build time... " >&6; }
ax_date_fmt="%Y%m%d"
if test x"$SOURCE_DATE_EPOCH" = x
then :
CONFIG_DATE=`date "+$ax_date_fmt"`
else $as_nop
ax_build_date=`date -u -d "@$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null \
|| date -u -r "$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null`
if test x"$ax_build_date" = x
then :
as_fn_error $? "malformed SOURCE_DATE_EPOCH" "$LINENO" 5
else $as_nop
CONFIG_DATE=$ax_build_date
fi
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CONFIG_DATE" >&5
printf "%s\n" "$CONFIG_DATE" >&6; }
# Checks for libraries.
@ -20783,12 +20820,6 @@ then :
printf "%s\n" "#define HAVE_BIO_SET_CALLBACK_EX 1" >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "SSL_CTX_set_tmp_ecdh" "ac_cv_func_SSL_CTX_set_tmp_ecdh"
if test "x$ac_cv_func_SSL_CTX_set_tmp_ecdh" = xyes
then :
printf "%s\n" "#define HAVE_SSL_CTX_SET_TMP_ECDH 1" >>confdefs.h
fi
# these check_funcs need -lssl
@ -20947,6 +20978,34 @@ else $as_nop
ac_have_decl=0
fi
printf "%s\n" "#define HAVE_DECL_SSL_CTX_SET_ECDH_AUTO $ac_have_decl" >>confdefs.h
ac_fn_check_decl "$LINENO" "SSL_CTX_set_tmp_ecdh" "ac_cv_have_decl_SSL_CTX_set_tmp_ecdh" "
$ac_includes_default
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
#ifdef HAVE_OPENSSL_RAND_H
#include <openssl/rand.h>
#endif
#ifdef HAVE_OPENSSL_CONF_H
#include <openssl/conf.h>
#endif
#ifdef HAVE_OPENSSL_ENGINE_H
#include <openssl/engine.h>
#endif
#include <openssl/ssl.h>
#include <openssl/evp.h>
" "$ac_c_undeclared_builtin_options" "CFLAGS"
if test "x$ac_cv_have_decl_SSL_CTX_set_tmp_ecdh" = xyes
then :
ac_have_decl=1
else $as_nop
ac_have_decl=0
fi
printf "%s\n" "#define HAVE_DECL_SSL_CTX_SET_TMP_ECDH $ac_have_decl" >>confdefs.h
if test "$ac_cv_func_HMAC_Init_ex" = "yes"; then
@ -22250,6 +22309,13 @@ if test "x$ac_cv_header_ngtcp2_ngtcp2_h" = xyes
then :
printf "%s\n" "#define HAVE_NGTCP2_NGTCP2_H 1" >>confdefs.h
fi
ac_fn_c_check_header_compile "$LINENO" "ngtcp2/ngtcp2_crypto_ossl.h" "ac_cv_header_ngtcp2_ngtcp2_crypto_ossl_h" "$ac_includes_default
"
if test "x$ac_cv_header_ngtcp2_ngtcp2_crypto_ossl_h" = xyes
then :
printf "%s\n" "#define HAVE_NGTCP2_NGTCP2_CRYPTO_OSSL_H 1" >>confdefs.h
fi
ac_fn_c_check_header_compile "$LINENO" "ngtcp2/ngtcp2_crypto_openssl.h" "ac_cv_header_ngtcp2_ngtcp2_crypto_openssl_h" "$ac_includes_default
"
@ -22290,7 +22356,52 @@ else $as_nop
fi
printf "%s\n" "#define HAVE_DECL_NGTCP2_CRYPTO_ENCRYPT_CB $ac_have_decl" >>confdefs.h
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_openssl" >&5
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_ossl" >&5
printf %s "checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_ossl... " >&6; }
if test ${ac_cv_lib_ngtcp2_crypto_ossl_ngtcp2_crypto_encrypt_cb+y}
then :
printf %s "(cached) " >&6
else $as_nop
ac_check_lib_save_LIBS=$LIBS
LIBS="-lngtcp2_crypto_ossl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
char ngtcp2_crypto_encrypt_cb ();
int
main (void)
{
return ngtcp2_crypto_encrypt_cb ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"
then :
ac_cv_lib_ngtcp2_crypto_ossl_ngtcp2_crypto_encrypt_cb=yes
else $as_nop
ac_cv_lib_ngtcp2_crypto_ossl_ngtcp2_crypto_encrypt_cb=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ngtcp2_crypto_ossl_ngtcp2_crypto_encrypt_cb" >&5
printf "%s\n" "$ac_cv_lib_ngtcp2_crypto_ossl_ngtcp2_crypto_encrypt_cb" >&6; }
if test "x$ac_cv_lib_ngtcp2_crypto_ossl_ngtcp2_crypto_encrypt_cb" = xyes
then :
LIBS="$LIBS -lngtcp2_crypto_ossl"
printf "%s\n" "#define USE_NGTCP2_CRYPTO_OSSL 1" >>confdefs.h
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_openssl" >&5
printf %s "checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_openssl... " >&6; }
if test ${ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb+y}
then :
@ -22328,9 +22439,9 @@ printf "%s\n" "$ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb" >&6; }
if test "x$ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb" = xyes
then :
LIBS="$LIBS -lngtcp2_crypto_openssl"
fi
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_quictls" >&5
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_quictls" >&5
printf %s "checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_quictls... " >&6; }
if test ${ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb+y}
then :
@ -22368,6 +22479,12 @@ printf "%s\n" "$ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb" >&6; }
if test "x$ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb" = xyes
then :
LIBS="$LIBS -lngtcp2_crypto_quictls"
fi
fi
fi
ac_fn_c_check_func "$LINENO" "ngtcp2_crypto_encrypt_cb" "ac_cv_func_ngtcp2_crypto_encrypt_cb"
@ -22417,6 +22534,12 @@ if test "x$ac_cv_func_ngtcp2_crypto_quictls_configure_client_context" = xyes
then :
printf "%s\n" "#define HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_CLIENT_CONTEXT 1" >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "ngtcp2_crypto_quictls_init" "ac_cv_func_ngtcp2_crypto_quictls_init"
if test "x$ac_cv_func_ngtcp2_crypto_quictls_init" = xyes
then :
printf "%s\n" "#define HAVE_NGTCP2_CRYPTO_QUICTLS_INIT 1" >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "ngtcp2_conn_get_num_scid" "ac_cv_func_ngtcp2_conn_get_num_scid"
if test "x$ac_cv_func_ngtcp2_conn_get_num_scid" = xyes
@ -22438,6 +22561,10 @@ then :
fi
# these check_funcs need -lssl
BAKLIBS="$LIBS"
LIBS="-lssl $LIBS"
for ac_func in SSL_is_quic
do :
ac_fn_c_check_func "$LINENO" "SSL_is_quic" "ac_cv_func_SSL_is_quic"
@ -22450,6 +22577,8 @@ else $as_nop
fi
done
LIBS="$BAKLIBS"
ac_fn_c_check_type "$LINENO" "struct ngtcp2_version_cid" "ac_cv_type_struct_ngtcp2_version_cid" "$ac_includes_default
#include <ngtcp2/ngtcp2.h>
@ -23497,6 +23626,48 @@ if echo $host_os | grep darwin8 > /dev/null; then
printf "%s\n" "#define DARWIN_BROKEN_SETREUID 1" >>confdefs.h
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for atomic_store" >&5
printf %s "checking for atomic_store... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
#ifdef HAVE_STDATOMIC_H
#include <stdatomic.h>
#endif
int
main (void)
{
int newvar = 5, var = 0;
atomic_store((_Atomic int*)&var, newvar);
newvar = 0;
/* condition to use the variables. */
if(var == newvar) return 1;
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_LINK_ATOMIC_STORE 1" >>confdefs.h
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
ac_fn_check_decl "$LINENO" "inet_pton" "ac_cv_have_decl_inet_pton" "
$ac_includes_default
#ifdef HAVE_NETINET_IN_H
@ -24173,7 +24344,55 @@ fi
if test "x$opt_dnstap" != "xno"; then
# Extract the first word of "protoc-c", so it can be a program name with args.
# Extract the first word of "protoc", so it can be a program name with args.
set dummy protoc; ac_word=$2
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_path_PROTOC+y}
then :
printf %s "(cached) " >&6
else $as_nop
case $PROTOC in
[\\/]* | ?:[\\/]*)
ac_cv_path_PROTOC="$PROTOC" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
case $as_dir in #(((
'') as_dir=./ ;;
*/) ;;
*) as_dir=$as_dir/ ;;
esac
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_path_PROTOC="$as_dir$ac_word$ac_exec_ext"
printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
PROTOC=$ac_cv_path_PROTOC
if test -n "$PROTOC"; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PROTOC" >&5
printf "%s\n" "$PROTOC" >&6; }
else
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
fi
# 'protoc-c' is deprecated. We use 'protoc' instead. If it can not be
# found, try 'protoc-c'.
if test -z "$PROTOC"; then
# Extract the first word of "protoc-c", so it can be a program name with args.
set dummy protoc-c; ac_word=$2
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
printf %s "checking for $ac_word... " >&6; }
@ -24218,9 +24437,83 @@ printf "%s\n" "no" >&6; }
fi
if test -z "$PROTOC_C"; then
as_fn_error $? "The protoc-c program was not found. Please install protobuf-c!" "$LINENO" 5
fi
else
PROTOC_C="$PROTOC"
fi
if test -z "$PROTOC_C"; then
as_fn_error $? "The protoc or protoc-c program was not found. It is needed for dnstap, use --disable-dnstap, or install protobuf-c to provide protoc or protoc-c" "$LINENO" 5
fi
# Check for protoc-gen-c plugin
# Extract the first word of "protoc-gen-c", so it can be a program name with args.
set dummy protoc-gen-c; ac_word=$2
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_path_PROTOC_GEN_C+y}
then :
printf %s "(cached) " >&6
else $as_nop
case $PROTOC_GEN_C in
[\\/]* | ?:[\\/]*)
ac_cv_path_PROTOC_GEN_C="$PROTOC_GEN_C" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
case $as_dir in #(((
'') as_dir=./ ;;
*/) ;;
*) as_dir=$as_dir/ ;;
esac
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_path_PROTOC_GEN_C="$as_dir$ac_word$ac_exec_ext"
printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
PROTOC_GEN_C=$ac_cv_path_PROTOC_GEN_C
if test -n "$PROTOC_GEN_C"; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PROTOC_GEN_C" >&5
printf "%s\n" "$PROTOC_GEN_C" >&6; }
else
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
fi
if test -z "$PROTOC_GEN_C"; then
as_fn_error $? "The protoc-gen-c plugin was not found. It is needed for dnstap, use --disable-dnstap, or install protobuf-c-compiler to provide protoc-gen-c" "$LINENO" 5
fi
# Test that protoc-gen-c actually works
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if protoc-gen-c plugin works" >&5
printf %s "checking if protoc-gen-c plugin works... " >&6; }
cat > conftest.proto << EOF
syntax = "proto2";
message TestMessage {
optional string test_field = 1;
}
EOF
if $PROTOC_C --c_out=. conftest.proto >/dev/null 2>&1; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
rm -f conftest.proto conftest.pb-c.c conftest.pb-c.h
else
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
rm -f conftest.proto conftest.pb-c.c conftest.pb-c.h
as_fn_error $? "The protoc-gen-c plugin is not working properly. Please ensure protobuf-c-compiler is properly installed" "$LINENO" 5
fi
# Check whether --with-protobuf-c was given.
if test ${with_protobuf_c+y}
@ -24998,9 +25291,27 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 10240" >>confdefs.h
version=1.22.1
version=1.23.2
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for build time" >&5
printf %s "checking for build time... " >&6; }
ax_date_fmt="%b %e, %Y"
if test x"$SOURCE_DATE_EPOCH" = x
then :
date=`date "+$ax_date_fmt"`
else $as_nop
ax_build_date=`date -u -d "@$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null \
|| date -u -r "$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null`
if test x"$ax_build_date" = x
then :
as_fn_error $? "malformed SOURCE_DATE_EPOCH" "$LINENO" 5
else $as_nop
date=$ax_build_date
fi
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $date" >&5
printf "%s\n" "$date" >&6; }
date=`date +'%b %e, %Y'`
ac_config_files="$ac_config_files Makefile doc/example.conf doc/libunbound.3 doc/unbound.8 doc/unbound-anchor.8 doc/unbound-checkconf.8 doc/unbound.conf.5 doc/unbound-control.8 doc/unbound-host.1 smallapp/unbound-control-setup.sh dnstap/dnstap_config.h dnscrypt/dnscrypt_config.h contrib/libunbound.pc contrib/unbound.socket contrib/unbound.service contrib/unbound_portable.service"
@ -25510,7 +25821,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by unbound $as_me 1.22.1, which was
This file was extended by unbound $as_me 1.23.2, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@ -25578,7 +25889,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
unbound config.status 1.22.1
unbound config.status 1.23.2
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"

View file

@ -2,6 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.56])
sinclude(acx_nlnetlabs.m4)
sinclude(ax_build_date_epoch.m4)
sinclude(ax_pthread.m4)
sinclude(acx_python.m4)
sinclude(ax_pkg_swig.m4)
@ -10,15 +11,15 @@ sinclude(dnscrypt/dnscrypt.m4)
# must be numbers. ac_defun because of later processing
m4_define([VERSION_MAJOR],[1])
m4_define([VERSION_MINOR],[22])
m4_define([VERSION_MICRO],[1])
m4_define([VERSION_MINOR],[23])
m4_define([VERSION_MICRO],[2])
AC_INIT([unbound],m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]),[unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues],[unbound])
AC_SUBST(UNBOUND_VERSION_MAJOR, [VERSION_MAJOR])
AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR])
AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO])
LIBUNBOUND_CURRENT=9
LIBUNBOUND_REVISION=31
LIBUNBOUND_REVISION=33
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@ -116,7 +117,9 @@ LIBUNBOUND_AGE=1
# 1.21.0 had 9:28:1
# 1.21.1 had 9:29:1
# 1.22.0 had 9:30:1
# 1.22.1 had 9:31:1
# 1.23.0 had 9:31:1
# 1.23.1 had 9:32:1
# 1.23.2 had 9:33:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
@ -523,6 +526,7 @@ AC_CHECK_HEADERS([netioapi.h],,, [AC_INCLUDES_DEFAULT
# Check for Linux timestamping headers
AC_CHECK_HEADERS([linux/net_tstamp.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_HEADERS([stdatomic.h],,, [AC_INCLUDES_DEFAULT])
# check for types.
# Using own tests for int64* because autoconf builtin only give 32bit.
@ -908,7 +912,8 @@ if test "`uname`" = "Linux"; then
GCC_DOCKER_LINTFLAGS='-syntax'
AC_SUBST(GCC_DOCKER_LINTFLAGS)
fi
CONFIG_DATE=`date +%Y%m%d`
AX_BUILD_DATE_EPOCH(CONFIG_DATE, [%Y%m%d])
AC_ARG_VAR(SOURCE_DATE_EPOCH, [If it is set, it uses the value of that variable instead of the current time as the build timestamp. The format is a unix timestamp. This enables reproducible build output.])
AC_SUBST(CONFIG_DATE)
# Checks for libraries.
@ -992,7 +997,7 @@ else
AC_MSG_RESULT([no])
fi
AC_CHECK_HEADERS([openssl/conf.h openssl/engine.h openssl/bn.h openssl/dh.h openssl/dsa.h openssl/rsa.h openssl/core_names.h openssl/param_build.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_FUNCS([OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_default_properties_is_fips_enabled EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ENGINE_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup DSA_SIG_set0 EVP_dss1 EVP_DigestVerify EVP_aes_256_cbc EVP_EncryptInit_ex HMAC_Init_ex CRYPTO_THREADID_set_callback EVP_MAC_CTX_set_params OSSL_PARAM_BLD_new BIO_set_callback_ex SSL_CTX_set_tmp_ecdh])
AC_CHECK_FUNCS([OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_default_properties_is_fips_enabled EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ENGINE_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup DSA_SIG_set0 EVP_dss1 EVP_DigestVerify EVP_aes_256_cbc EVP_EncryptInit_ex HMAC_Init_ex CRYPTO_THREADID_set_callback EVP_MAC_CTX_set_params OSSL_PARAM_BLD_new BIO_set_callback_ex])
# these check_funcs need -lssl
BAKLIBS="$LIBS"
@ -1000,7 +1005,7 @@ LIBS="-lssl $LIBS"
AC_CHECK_FUNCS([OPENSSL_init_ssl SSL_CTX_set_security_level SSL_set1_host SSL_get0_peername X509_VERIFY_PARAM_set1_host SSL_CTX_set_ciphersuites SSL_CTX_set_tlsext_ticket_key_evp_cb SSL_CTX_set_alpn_select_cb SSL_get0_alpn_selected SSL_CTX_set_alpn_protos SSL_get1_peer_certificate])
LIBS="$BAKLIBS"
AC_CHECK_DECLS([SSL_COMP_get_compression_methods,sk_SSL_COMP_pop_free,SSL_CTX_set_ecdh_auto], [], [], [
AC_CHECK_DECLS([SSL_COMP_get_compression_methods,sk_SSL_COMP_pop_free,SSL_CTX_set_ecdh_auto,SSL_CTX_set_tmp_ecdh], [], [], [
AC_INCLUDES_DEFAULT
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
@ -1606,17 +1611,29 @@ if test x_$withval = x_yes -o x_$withval != x_no; then
if test x_$found_libngtcp2 != x_yes; then
AC_MSG_ERROR([Could not find libngtcp2, ngtcp2.h])
fi
AC_CHECK_HEADERS([ngtcp2/ngtcp2.h ngtcp2/ngtcp2_crypto_openssl.h ngtcp2/ngtcp2_crypto_quictls.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_HEADERS([ngtcp2/ngtcp2.h ngtcp2/ngtcp2_crypto_ossl.h ngtcp2/ngtcp2_crypto_openssl.h ngtcp2/ngtcp2_crypto_quictls.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_DECLS([ngtcp2_conn_server_new], [], [], [AC_INCLUDES_DEFAULT
#include <ngtcp2/ngtcp2.h>
])
AC_CHECK_DECLS([ngtcp2_crypto_encrypt_cb], [], [], [AC_INCLUDES_DEFAULT
#include <ngtcp2/ngtcp2_crypto.h>
])
AC_CHECK_LIB([ngtcp2_crypto_openssl], [ngtcp2_crypto_encrypt_cb], [ LIBS="$LIBS -lngtcp2_crypto_openssl" ])
AC_CHECK_LIB([ngtcp2_crypto_quictls], [ngtcp2_crypto_encrypt_cb], [ LIBS="$LIBS -lngtcp2_crypto_quictls" ])
AC_CHECK_FUNCS([ngtcp2_crypto_encrypt_cb ngtcp2_ccerr_default ngtcp2_conn_in_closing_period ngtcp2_conn_in_draining_period ngtcp2_conn_get_max_local_streams_uni ngtcp2_crypto_quictls_from_ossl_encryption_level ngtcp2_crypto_quictls_configure_server_context ngtcp2_crypto_quictls_configure_client_context ngtcp2_conn_get_num_scid ngtcp2_conn_tls_early_data_rejected ngtcp2_conn_encode_0rtt_transport_params])
AC_CHECK_LIB([ngtcp2_crypto_ossl], [ngtcp2_crypto_encrypt_cb], [
LIBS="$LIBS -lngtcp2_crypto_ossl"
AC_DEFINE(USE_NGTCP2_CRYPTO_OSSL, 1, [Define this to use ngtcp2_crypto_ossl.])
], [
AC_CHECK_LIB([ngtcp2_crypto_openssl], [ngtcp2_crypto_encrypt_cb], [ LIBS="$LIBS -lngtcp2_crypto_openssl" ], [
AC_CHECK_LIB([ngtcp2_crypto_quictls], [ngtcp2_crypto_encrypt_cb], [ LIBS="$LIBS -lngtcp2_crypto_quictls" ])
])
])
AC_CHECK_FUNCS([ngtcp2_crypto_encrypt_cb ngtcp2_ccerr_default ngtcp2_conn_in_closing_period ngtcp2_conn_in_draining_period ngtcp2_conn_get_max_local_streams_uni ngtcp2_crypto_quictls_from_ossl_encryption_level ngtcp2_crypto_quictls_configure_server_context ngtcp2_crypto_quictls_configure_client_context ngtcp2_crypto_quictls_init ngtcp2_conn_get_num_scid ngtcp2_conn_tls_early_data_rejected ngtcp2_conn_encode_0rtt_transport_params])
# these check_funcs need -lssl
BAKLIBS="$LIBS"
LIBS="-lssl $LIBS"
AC_CHECK_FUNCS([SSL_is_quic], [], [AC_MSG_ERROR([No QUIC support detected in OpenSSL. Need OpenSSL version with QUIC support to enable DNS over QUIC with libngtcp2.])])
LIBS="$BAKLIBS"
AC_CHECK_TYPES([struct ngtcp2_version_cid, ngtcp2_encryption_level],,,[AC_INCLUDES_DEFAULT
#include <ngtcp2/ngtcp2.h>
])
@ -1815,6 +1832,25 @@ AC_CHECK_FUNCS([setresgid],,[AC_CHECK_FUNCS([setregid])])
if echo $host_os | grep darwin8 > /dev/null; then
AC_DEFINE(DARWIN_BROKEN_SETREUID, 1, [Define this if on macOSX10.4-darwin8 and setreuid and setregid do not work])
fi
AC_MSG_CHECKING([for atomic_store])
AC_LINK_IFELSE([AC_LANG_PROGRAM(AC_INCLUDES_DEFAULT [[
#ifdef HAVE_STDATOMIC_H
#include <stdatomic.h>
#endif
]], [[
int newvar = 5, var = 0;
atomic_store((_Atomic int*)&var, newvar);
newvar = 0;
/* condition to use the variables. */
if(var == newvar) return 1;
]])], [
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_LINK_ATOMIC_STORE, 1, [If we have atomic_store])
], [
AC_MSG_RESULT([no])
])
AC_CHECK_DECLS([inet_pton,inet_ntop], [], [], [
AC_INCLUDES_DEFAULT
#ifdef HAVE_NETINET_IN_H
@ -2435,7 +2471,8 @@ char *unbound_stat_strdup_log(const char *s, const char* file, int line,
dnl if we build from source tree, the man pages need @date@ and @version@
dnl if this is a distro tarball, that was already done by makedist.sh
AC_SUBST(version, [VERSION_MAJOR.VERSION_MINOR.VERSION_MICRO])
AC_SUBST(date, [`date +'%b %e, %Y'`])
AX_BUILD_DATE_EPOCH(date, [[%b %e, %Y]])
AC_SUBST(date)
AC_CONFIG_FILES([Makefile doc/example.conf doc/libunbound.3 doc/unbound.8 doc/unbound-anchor.8 doc/unbound-checkconf.8 doc/unbound.conf.5 doc/unbound-control.8 doc/unbound-host.1 smallapp/unbound-control-setup.sh dnstap/dnstap_config.h dnscrypt/dnscrypt_config.h contrib/libunbound.pc contrib/unbound.socket contrib/unbound.service contrib/unbound_portable.service])
AC_CONFIG_HEADERS([config.h])

View file

@ -1,21 +1,24 @@
#!/usr/bin/env bash
LIBEXPAT_FNAME=expat-2.7.0
LIBEXPAT_VERSION_DIR=R_2_7_0
echo "Downloading Expat"
if ! curl -L -k -s -o expat-2.2.9.tar.gz https://github.com/libexpat/libexpat/releases/download/R_2_2_9/expat-2.2.9.tar.gz;
if ! curl -L -k -s -o $LIBEXPAT_FNAME.tar.gz https://github.com/libexpat/libexpat/releases/download/$LIBEXPAT_VERSION_DIR/$LIBEXPAT_FNAME.tar.gz;
then
echo "Failed to download Expat"
exit 1
fi
echo "Unpacking Expat"
rm -rf ./expat-2.2.9
if ! tar -xf expat-2.2.9.tar.gz;
rm -rf ./$LIBEXPAT_FNAME
if ! tar -xf $LIBEXPAT_FNAME.tar.gz;
then
echo "Failed to unpack Expat"
exit 1
fi
cd expat-2.2.9 || exit 1
cd $LIBEXPAT_FNAME || exit 1
echo "Configuring Expat"
if ! ./configure --build="$AUTOTOOLS_BUILD" --host="$AUTOTOOLS_HOST" --prefix="$ANDROID_PREFIX"; then

View file

@ -1,28 +1,32 @@
#!/usr/bin/env bash
LIBEXPAT_FNAME=expat-2.7.0
LIBEXPAT_VERSION_DIR=R_2_7_0
echo "Downloading Expat"
if ! curl -L -k -s -o expat-2.2.9.tar.gz https://github.com/libexpat/libexpat/releases/download/R_2_2_9/expat-2.2.9.tar.gz;
if ! curl -L -k -s -o $LIBEXPAT_FNAME.tar.gz https://github.com/libexpat/libexpat/releases/download/$LIBEXPAT_VERSION_DIR/$LIBEXPAT_FNAME.tar.gz;
then
echo "Failed to download Expat"
exit 1
fi
echo "Unpacking Expat"
rm -rf ./expat-2.2.9
if ! tar -xf expat-2.2.9.tar.gz;
rm -rf ./$LIBEXPAT_FNAME
if ! tar -xf $LIBEXPAT_FNAME.tar.gz;
then
echo "Failed to unpack Expat"
exit 1
fi
cd expat-2.2.9 || exit 1
cd $LIBEXPAT_FNAME || exit 1
export PKG_CONFIG_PATH="$IOS_PREFIX/lib/pkgconfig"
echo "Configuring Expat"
if ! ./configure \
--build="$AUTOTOOLS_BUILD" --host="$AUTOTOOLS_HOST" \
--prefix="$IOS_PREFIX" ; then
if ! ./configure --without-tests \
--build="$AUTOTOOLS_BUILD" --host="$AUTOTOOLS_HOST" \
--prefix="$IOS_PREFIX" ;
then
echo "Error: Failed to configure Expat"
cat config.log
exit 1

View file

@ -38,11 +38,17 @@
; - `LockPersonality=yes` locks down the personality system call so that the
; kernel execution domain may not be changed from the default.
;
; - With /etc/systemd/network/*.network a setting to make sure the network
; is not considered online too early, can reduce network unreachable
; errors on server start:
; [Link]
; RequiredForOnline=routable
;
[Unit]
Description=Validating, recursive, and caching DNS resolver
Documentation=man:unbound(8)
After=network-online.target
Wants=network-online.target
Before=nss-lookup.target
[Install]

View file

@ -221,7 +221,9 @@ acl_interface_insert(struct acl_list* acl_interface,
struct sockaddr_storage* addr, socklen_t addrlen,
enum acl_access control)
{
return acl_find_or_create(acl_interface, addr, addrlen, control);
struct acl_addr* node = acl_find_or_create(acl_interface, addr, addrlen, control);
node->is_interface = 1;
return node;
}
/** apply acl_tag string */
@ -805,10 +807,23 @@ log_acl_action(const char* action, struct sockaddr_storage* addr,
addr_to_str(&acladdr->node.addr, acladdr->node.addrlen,
n, sizeof(n));
verbose(VERB_ALGO, "%s query from %s port %d because of "
"%s/%d %s", action, a, (int)port, n, acladdr->node.net,
"%s/%d %s%s", action, a, (int)port, n,
acladdr->node.net,
acladdr->is_interface?"(ACL on interface IP) ":"",
acl_access_to_str(acl));
} else {
verbose(VERB_ALGO, "%s query from %s port %d", action, a,
(int)port);
}
}
void acl_list_swap_tree(struct acl_list* acl, struct acl_list* data)
{
/* swap tree and region */
rbtree_type oldtree = acl->tree;
struct regional* oldregion = acl->region;
acl->tree = data->tree;
acl->region = data->region;
data->tree = oldtree;
data->region = oldregion;
}

View file

@ -107,6 +107,8 @@ struct acl_addr {
struct config_strlist** tag_datas;
/** size of the tag_datas array */
size_t tag_datas_size;
/* If the acl node is for an interface */
int is_interface;
/* view element, NULL if none */
struct view* view;
};
@ -199,4 +201,12 @@ const char* acl_access_to_str(enum acl_access acl);
void log_acl_action(const char* action, struct sockaddr_storage* addr,
socklen_t addrlen, enum acl_access acl, struct acl_addr* acladdr);
/**
* Swap internal tree with preallocated entries.
* @param acl: the acl structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void acl_list_swap_tree(struct acl_list* acl, struct acl_list* data);
#endif /* DAEMON_ACL_LIST_H */

View file

@ -62,84 +62,231 @@
#include "sldns/wire2str.h"
#include "sldns/str2wire.h"
static void spool_txt_printf(struct config_strlist_head* txt,
const char* format, ...) ATTR_FORMAT(printf, 2, 3);
/** Append to strlist at end, and log error if out of memory. */
static void
spool_txt_string(struct config_strlist_head* txt, char* str)
{
if(!cfg_strlist_append(txt, strdup(str))) {
log_err("out of memory in spool text");
}
}
/** Spool txt to spool list. */
static void
spool_txt_vmsg(struct config_strlist_head* txt, const char* format,
va_list args)
{
char msg[65535];
vsnprintf(msg, sizeof(msg), format, args);
spool_txt_string(txt, msg);
}
/** Print item to spool list. On alloc failure the list is as before. */
static void
spool_txt_printf(struct config_strlist_head* txt, const char* format, ...)
{
va_list args;
va_start(args, format);
spool_txt_vmsg(txt, format, args);
va_end(args);
}
/** dump one rrset zonefile line */
static int
dump_rrset_line(RES* ssl, struct ub_packed_rrset_key* k, time_t now, size_t i)
static void
dump_rrset_line(struct config_strlist_head* txt, struct ub_packed_rrset_key* k,
time_t now, size_t i)
{
char s[65535];
if(!packed_rr_to_string(k, i, now, s, sizeof(s))) {
return ssl_printf(ssl, "BADRR\n");
spool_txt_string(txt, "BADRR\n");
return;
}
return ssl_printf(ssl, "%s", s);
spool_txt_string(txt, s);
}
/** dump rrset key and data info */
static int
dump_rrset(RES* ssl, struct ub_packed_rrset_key* k,
static void
dump_rrset(struct config_strlist_head* txt, struct ub_packed_rrset_key* k,
struct packed_rrset_data* d, time_t now)
{
size_t i;
/* rd lock held by caller */
if(!k || !d) return 1;
if(k->id == 0) return 1; /* deleted */
if(d->ttl < now) return 1; /* expired */
if(!k || !d) return;
if(k->id == 0) return; /* deleted */
if(d->ttl < now) return; /* expired */
/* meta line */
if(!ssl_printf(ssl, ";rrset%s " ARG_LL "d %u %u %d %d\n",
spool_txt_printf(txt, ";rrset%s " ARG_LL "d %u %u %d %d\n",
(k->rk.flags & PACKED_RRSET_NSEC_AT_APEX)?" nsec_apex":"",
(long long)(d->ttl - now),
(unsigned)d->count, (unsigned)d->rrsig_count,
(int)d->trust, (int)d->security
))
return 0;
);
for(i=0; i<d->count + d->rrsig_count; i++) {
if(!dump_rrset_line(ssl, k, now, i))
dump_rrset_line(txt, k, now, i);
}
}
/** Spool strlist to the output. */
static int
spool_strlist(RES* ssl, struct config_strlist* list)
{
struct config_strlist* s;
for(s=list; s; s=s->next) {
if(!ssl_printf(ssl, "%s", s->str))
return 0;
}
return 1;
}
/** dump lruhash rrset cache */
/** dump lruhash cache and call callback for every item. */
static int
dump_rrset_lruhash(RES* ssl, struct lruhash* h, time_t now)
dump_lruhash(struct lruhash* table,
void (*func)(struct lruhash_entry*, struct config_strlist_head*, void*),
RES* ssl, void* arg)
{
struct lruhash_entry* e;
/* lruhash already locked by caller */
/* walk in order of lru; best first */
for(e=h->lru_start; e; e = e->lru_next) {
lock_rw_rdlock(&e->lock);
if(!dump_rrset(ssl, (struct ub_packed_rrset_key*)e->key,
(struct packed_rrset_data*)e->data, now)) {
lock_rw_unlock(&e->lock);
int just_started = 1;
int not_done = 1;
hashvalue_type hash;
size_t num = 0; /* number of entries processed. */
size_t max = 2; /* number of entries after which it unlocks. */
struct config_strlist_head txt; /* Text strings spooled. */
memset(&txt, 0, sizeof(txt));
while(not_done) {
size_t i; /* hash bin. */
/* Process a number of items. */
num = 0;
lock_quick_lock(&table->lock);
if(just_started) {
i = 0;
} else {
i = hash&table->size_mask;
}
while(num < max) {
/* Process bin. */
int found = 0;
size_t num_bin = 0;
struct lruhash_bin* bin = &table->array[i];
struct lruhash_entry* e;
lock_quick_lock(&bin->lock);
for(e = bin->overflow_list; e; e = e->overflow_next) {
/* Entry e is locked by the func. */
func(e, &txt, arg);
num_bin++;
}
lock_quick_unlock(&bin->lock);
/* This addition of bin number of entries may take
* it over the max. */
num += num_bin;
/* Move to next bin. */
/* Find one with an entry, with a hash value, so we
* can continue from the hash value. The hash value
* can be indexed also if the array changes size. */
i++;
while(i < table->size) {
bin = &table->array[i];
lock_quick_lock(&bin->lock);
if(bin->overflow_list) {
hash = bin->overflow_list->hash;
lock_quick_unlock(&bin->lock);
found = 1;
just_started = 0;
break;
}
lock_quick_unlock(&bin->lock);
i++;
}
if(!found) {
not_done = 0;
break;
}
}
lock_quick_unlock(&table->lock);
/* Print the spooled items, that are collected while the
* locks are locked. The print happens while they are not
* locked. */
if(txt.first) {
if(!spool_strlist(ssl, txt.first)) {
config_delstrlist(txt.first);
return 0;
}
config_delstrlist(txt.first);
memset(&txt, 0, sizeof(txt));
}
}
/* Print the final spooled items. */
if(txt.first) {
if(!spool_strlist(ssl, txt.first)) {
config_delstrlist(txt.first);
return 0;
}
lock_rw_unlock(&e->lock);
config_delstrlist(txt.first);
}
return 1;
}
/** dump slabhash cache and call callback for every item. */
static int
dump_slabhash(struct slabhash* sh,
void (*func)(struct lruhash_entry*, struct config_strlist_head*, void*),
RES* ssl, void* arg)
{
/* Process a number of items at a time, then unlock the cache,
* so that ordinary processing can continue. Keep an iteration marker
* to continue the loop. That means the cache can change, items
* could be inserted and deleted. And, for example, the hash table
* can grow. */
size_t slab;
for(slab=0; slab<sh->size; slab++) {
if(!dump_lruhash(sh->array[slab], func, ssl, arg))
return 0;
}
return 1;
}
/** Struct for dump information. */
struct dump_info {
/** The worker. */
struct worker* worker;
/** The printout connection. */
RES* ssl;
};
/** Dump the rrset cache entry */
static void
dump_rrset_entry(struct lruhash_entry* e, struct config_strlist_head* txt,
void* arg)
{
struct dump_info* dump_info = (struct dump_info*)arg;
lock_rw_rdlock(&e->lock);
dump_rrset(txt, (struct ub_packed_rrset_key*)e->key,
(struct packed_rrset_data*)e->data,
*dump_info->worker->env.now);
lock_rw_unlock(&e->lock);
}
/** dump rrset cache */
static int
dump_rrset_cache(RES* ssl, struct worker* worker)
{
struct rrset_cache* r = worker->env.rrset_cache;
size_t slab;
struct dump_info dump_info;
dump_info.worker = worker;
dump_info.ssl = ssl;
if(!ssl_printf(ssl, "START_RRSET_CACHE\n")) return 0;
for(slab=0; slab<r->table.size; slab++) {
lock_quick_lock(&r->table.array[slab]->lock);
if(!dump_rrset_lruhash(ssl, r->table.array[slab],
*worker->env.now)) {
lock_quick_unlock(&r->table.array[slab]->lock);
return 0;
}
lock_quick_unlock(&r->table.array[slab]->lock);
}
if(!dump_slabhash(&r->table, &dump_rrset_entry, ssl, &dump_info))
return 0;
return ssl_printf(ssl, "END_RRSET_CACHE\n");
}
/** dump message to rrset reference */
static int
dump_msg_ref(RES* ssl, struct ub_packed_rrset_key* k)
static void
dump_msg_ref(struct config_strlist_head* txt, struct ub_packed_rrset_key* k)
{
char* nm, *tp, *cl;
nm = sldns_wire2str_dname(k->rk.dname, k->rk.dname_len);
@ -149,30 +296,25 @@ dump_msg_ref(RES* ssl, struct ub_packed_rrset_key* k)
free(nm);
free(tp);
free(cl);
return ssl_printf(ssl, "BADREF\n");
}
if(!ssl_printf(ssl, "%s %s %s %d\n", nm, cl, tp, (int)k->rk.flags)) {
free(nm);
free(tp);
free(cl);
return 0;
spool_txt_string(txt, "BADREF\n");
return;
}
spool_txt_printf(txt, "%s %s %s %d\n", nm, cl, tp, (int)k->rk.flags);
free(nm);
free(tp);
free(cl);
return 1;
}
/** dump message entry */
static int
dump_msg(RES* ssl, struct query_info* k, struct reply_info* d, time_t now)
static void
dump_msg(struct config_strlist_head* txt, struct query_info* k,
struct reply_info* d, time_t now)
{
size_t i;
char* nm, *tp, *cl;
if(!k || !d) return 1;
if(d->ttl < now) return 1; /* expired */
if(!k || !d) return;
if(d->ttl < now) return; /* expired */
nm = sldns_wire2str_dname(k->qname, k->qname_len);
tp = sldns_wire2str_type(k->qtype);
cl = sldns_wire2str_class(k->qclass);
@ -180,45 +322,35 @@ dump_msg(RES* ssl, struct query_info* k, struct reply_info* d, time_t now)
free(nm);
free(tp);
free(cl);
return 1; /* skip this entry */
return; /* skip this entry */
}
if(!rrset_array_lock(d->ref, d->rrset_count, now)) {
/* rrsets have timed out or do not exist */
free(nm);
free(tp);
free(cl);
return 1; /* skip this entry */
return; /* skip this entry */
}
/* meta line */
if(!ssl_printf(ssl, "msg %s %s %s %d %d " ARG_LL "d %d %u %u %u %d %s\n",
nm, cl, tp,
(int)d->flags, (int)d->qdcount,
(long long)(d->ttl-now), (int)d->security,
(unsigned)d->an_numrrsets,
(unsigned)d->ns_numrrsets,
(unsigned)d->ar_numrrsets,
(int)d->reason_bogus,
d->reason_bogus_str?d->reason_bogus_str:"")) {
free(nm);
free(tp);
free(cl);
rrset_array_unlock(d->ref, d->rrset_count);
return 0;
}
spool_txt_printf(txt,
"msg %s %s %s %d %d " ARG_LL "d %d %u %u %u %d %s\n",
nm, cl, tp,
(int)d->flags, (int)d->qdcount,
(long long)(d->ttl-now), (int)d->security,
(unsigned)d->an_numrrsets,
(unsigned)d->ns_numrrsets,
(unsigned)d->ar_numrrsets,
(int)d->reason_bogus,
d->reason_bogus_str?d->reason_bogus_str:"");
free(nm);
free(tp);
free(cl);
for(i=0; i<d->rrset_count; i++) {
if(!dump_msg_ref(ssl, d->rrsets[i])) {
rrset_array_unlock(d->ref, d->rrset_count);
return 0;
}
dump_msg_ref(txt, d->rrsets[i]);
}
rrset_array_unlock(d->ref, d->rrset_count);
return 1;
}
/** copy msg to worker pad */
@ -247,49 +379,40 @@ copy_msg(struct regional* region, struct lruhash_entry* e,
return (*k)->qname != NULL;
}
/** dump lruhash msg cache */
static int
dump_msg_lruhash(RES* ssl, struct worker* worker, struct lruhash* h)
/** Dump the msg entry. */
static void
dump_msg_entry(struct lruhash_entry* e, struct config_strlist_head* txt,
void* arg)
{
struct lruhash_entry* e;
struct dump_info* dump_info = (struct dump_info*)arg;
struct query_info* k;
struct reply_info* d;
/* lruhash already locked by caller */
/* walk in order of lru; best first */
for(e=h->lru_start; e; e = e->lru_next) {
regional_free_all(worker->scratchpad);
lock_rw_rdlock(&e->lock);
/* make copy of rrset in worker buffer */
if(!copy_msg(worker->scratchpad, e, &k, &d)) {
lock_rw_unlock(&e->lock);
return 0;
}
regional_free_all(dump_info->worker->scratchpad);
/* Make copy of rrset in worker buffer. */
lock_rw_rdlock(&e->lock);
if(!copy_msg(dump_info->worker->scratchpad, e, &k, &d)) {
lock_rw_unlock(&e->lock);
/* release lock so we can lookup the rrset references
* in the rrset cache */
if(!dump_msg(ssl, k, d, *worker->env.now)) {
return 0;
}
log_err("out of memory in dump_msg_entry");
return;
}
return 1;
lock_rw_unlock(&e->lock);
/* Release lock so we can lookup the rrset references
* in the rrset cache. */
dump_msg(txt, k, d, *dump_info->worker->env.now);
}
/** dump msg cache */
static int
dump_msg_cache(RES* ssl, struct worker* worker)
{
struct slabhash* sh = worker->env.msg_cache;
size_t slab;
struct dump_info dump_info;
dump_info.worker = worker;
dump_info.ssl = ssl;
if(!ssl_printf(ssl, "START_MSG_CACHE\n")) return 0;
for(slab=0; slab<sh->size; slab++) {
lock_quick_lock(&sh->array[slab]->lock);
if(!dump_msg_lruhash(ssl, worker, sh->array[slab])) {
lock_quick_unlock(&sh->array[slab]->lock);
return 0;
}
lock_quick_unlock(&sh->array[slab]->lock);
}
if(!dump_slabhash(worker->env.msg_cache, &dump_msg_entry, ssl,
&dump_info))
return 0;
return ssl_printf(ssl, "END_MSG_CACHE\n");
}
@ -811,12 +934,18 @@ print_dp_main(RES* ssl, struct delegpt* dp, struct dns_msg* msg)
struct ub_packed_rrset_key* k = msg->rep->rrsets[i];
struct packed_rrset_data* d =
(struct packed_rrset_data*)k->entry.data;
struct config_strlist_head txt;
memset(&txt, 0, sizeof(txt));
if(d->security == sec_status_bogus) {
if(!ssl_printf(ssl, "Address is BOGUS:\n"))
return;
}
if(!dump_rrset(ssl, k, d, 0))
dump_rrset(&txt, k, d, 0);
if(!spool_strlist(ssl, txt.first)) {
config_delstrlist(txt.first);
return;
}
config_delstrlist(txt.first);
}
delegpt_count_ns(dp, &n_ns, &n_miss);
delegpt_count_addr(dp, &n_addr, &n_res, &n_avail);
@ -836,7 +965,7 @@ int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm,
struct delegpt* dp;
struct dns_msg* msg;
struct regional* region = worker->scratchpad;
char b[260];
char b[LDNS_MAX_DOMAINLEN];
struct query_info qinfo;
struct iter_hints_stub* stub;
int nolock = 0;

View file

@ -323,8 +323,7 @@ daemon_init(void)
return daemon;
}
static int setup_acl_for_ports(struct acl_list* list,
struct listen_port* port_list)
int setup_acl_for_ports(struct acl_list* list, struct listen_port* port_list)
{
struct acl_addr* acl_node;
for(; port_list; port_list=port_list->next) {
@ -717,16 +716,16 @@ daemon_fork(struct daemon* daemon)
#endif
log_assert(daemon);
if(!(daemon->views = views_create()))
if(!(daemon->env->views = views_create()))
fatal_exit("Could not create views: out of memory");
/* create individual views and their localzone/data trees */
if(!views_apply_cfg(daemon->views, daemon->cfg))
if(!views_apply_cfg(daemon->env->views, daemon->cfg))
fatal_exit("Could not set up views");
if(!acl_list_apply_cfg(daemon->acl, daemon->cfg, daemon->views))
if(!acl_list_apply_cfg(daemon->acl, daemon->cfg, daemon->env->views))
fatal_exit("Could not setup access control list");
if(!acl_interface_apply_cfg(daemon->acl_interface, daemon->cfg,
daemon->views))
daemon->env->views))
fatal_exit("Could not setup interface control list");
if(!tcl_list_apply_cfg(daemon->tcl, daemon->cfg))
fatal_exit("Could not setup TCP connection limits");
@ -762,15 +761,15 @@ daemon_fork(struct daemon* daemon)
fatal_exit("Could not set root or stub hints");
/* process raw response-ip configuration data */
if(!(daemon->respip_set = respip_set_create()))
if(!(daemon->env->respip_set = respip_set_create()))
fatal_exit("Could not create response IP set");
if(!respip_global_apply_cfg(daemon->respip_set, daemon->cfg))
if(!respip_global_apply_cfg(daemon->env->respip_set, daemon->cfg))
fatal_exit("Could not set up response IP set");
if(!respip_views_apply_cfg(daemon->views, daemon->cfg,
if(!respip_views_apply_cfg(daemon->env->views, daemon->cfg,
&have_view_respip_cfg))
fatal_exit("Could not set up per-view response IP sets");
daemon->use_response_ip = !respip_set_is_empty(daemon->respip_set) ||
have_view_respip_cfg;
daemon->use_response_ip = !respip_set_is_empty(
daemon->env->respip_set) || have_view_respip_cfg;
/* setup modules */
daemon_setup_modules(daemon);
@ -886,14 +885,18 @@ daemon_cleanup(struct daemon* daemon)
daemon->env->hints = NULL;
local_zones_delete(daemon->local_zones);
daemon->local_zones = NULL;
respip_set_delete(daemon->respip_set);
daemon->respip_set = NULL;
views_delete(daemon->views);
daemon->views = NULL;
respip_set_delete(daemon->env->respip_set);
daemon->env->respip_set = NULL;
views_delete(daemon->env->views);
daemon->env->views = NULL;
if(daemon->env->auth_zones)
auth_zones_cleanup(daemon->env->auth_zones);
/* key cache is cleared by module deinit during next daemon_fork() */
daemon_remote_clear(daemon->rc);
if(daemon->fast_reload_thread)
fast_reload_thread_stop(daemon->fast_reload_thread);
if(daemon->fast_reload_printq_list)
fast_reload_printq_list_delete(daemon->fast_reload_printq_list);
for(i=0; i<daemon->num; i++)
worker_delete(daemon->workers[i]);
free(daemon->workers);
@ -951,11 +954,16 @@ daemon_delete(struct daemon* daemon)
listen_desetup_locks();
free(daemon->chroot);
free(daemon->pidfile);
free(daemon->cfgfile);
free(daemon->env);
#ifdef HAVE_SSL
listen_sslctx_delete_ticket_keys();
SSL_CTX_free((SSL_CTX*)daemon->listen_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->connect_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->listen_dot_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->listen_doh_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->connect_dot_sslctx);
#endif
#ifdef HAVE_NGTCP2
SSL_CTX_free((SSL_CTX*)daemon->listen_quic_sslctx);
#endif
free(daemon);
/* lex cleanup */

View file

@ -60,6 +60,8 @@ struct respip_set;
struct shm_main_info;
struct doq_table;
struct cookie_secrets;
struct fast_reload_thread;
struct fast_reload_printq;
#include "dnstap/dnstap_config.h"
#ifdef USE_DNSTAP
@ -97,8 +99,14 @@ struct daemon {
struct listen_port* rc_ports;
/** remote control connections management (for first worker) */
struct daemon_remote* rc;
/** ssl context for listening to dnstcp over ssl, and connecting ssl */
void* listen_sslctx, *connect_sslctx;
/** ssl context for listening to dnstcp over ssl */
void* listen_dot_sslctx;
/** ssl context for connecting to dnstcp over ssl */
void* connect_dot_sslctx;
/** ssl context for listening to DoH */
void* listen_doh_sslctx;
/** ssl context for listening to quic */
void* listen_quic_sslctx;
/** num threads allocated */
int num;
/** num threads allocated in the previous config or 0 at first */
@ -131,15 +139,11 @@ struct daemon {
struct timeval time_last_stat;
/** time when daemon started */
struct timeval time_boot;
/** views structure containing view tree */
struct views* views;
#ifdef USE_DNSTAP
/** the dnstap environment master value, copied and changed by threads*/
struct dt_env* dtenv;
#endif
struct shm_main_info* shm_info;
/** response-ip set with associated actions and tags. */
struct respip_set* respip_set;
/** some response-ip tags or actions are configured if true */
int use_response_ip;
/** some RPZ policies are configured */
@ -154,6 +158,17 @@ struct daemon {
int reuse_cache;
/** the EDNS cookie secrets from the cookie-secret-file */
struct cookie_secrets* cookie_secrets;
/** the fast reload thread, or NULL */
struct fast_reload_thread* fast_reload_thread;
/** the fast reload printq list */
struct fast_reload_printq* fast_reload_printq_list;
/** the fast reload option to drop mesh queries, true if so. */
int fast_reload_drop_mesh;
/** for fast reload, if the tcl, tcp connection limits, has
* changes for workers */
int fast_reload_tcl_has_changes;
/** config file name */
char* cfgfile;
};
/**
@ -206,4 +221,12 @@ void daemon_delete(struct daemon* daemon);
*/
void daemon_apply_cfg(struct daemon* daemon, struct config_file* cfg);
/**
* Setup acl list to have entries for the port list.
* @param list: the acl interface
* @param port_list: list of open ports, or none.
* @return false on failure
*/
int setup_acl_for_ports(struct acl_list* list, struct listen_port* port_list);
#endif /* DAEMON_H */

File diff suppressed because it is too large Load diff

View file

@ -48,6 +48,7 @@
#ifdef HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
#endif
#include "util/locks.h"
struct config_file;
struct listen_list;
struct listen_port;
@ -55,6 +56,7 @@ struct worker;
struct comm_reply;
struct comm_point;
struct daemon_remote;
struct config_strlist_head;
/** number of milliseconds timeout on incoming remote control handshake */
#define REMOTE_CONTROL_TCP_TIMEOUT 120000
@ -118,6 +120,137 @@ struct remote_stream {
};
typedef struct remote_stream RES;
/**
* Notification status. This is exchanged between the fast reload thread
* and the server thread, over the commpair sockets.
*/
enum fast_reload_notification {
/** nothing, not used */
fast_reload_notification_none = 0,
/** the fast reload thread is done */
fast_reload_notification_done = 1,
/** the fast reload thread is done but with an error, it failed */
fast_reload_notification_done_error = 2,
/** the fast reload thread is told to exit by the server thread.
* Sent on server quit while the reload is running. */
fast_reload_notification_exit = 3,
/** the fast reload thread has exited, after being told to exit */
fast_reload_notification_exited = 4,
/** the fast reload thread has information to print out */
fast_reload_notification_printout = 5,
/** stop as part of the reload the thread and other threads */
fast_reload_notification_reload_stop = 6,
/** ack the stop as part of the reload, and also ack start */
fast_reload_notification_reload_ack = 7,
/** resume from stop as part of the reload */
fast_reload_notification_reload_start = 8,
/** the fast reload thread wants the mainthread to poll workers,
* after the reload, sent when nopause is used */
fast_reload_notification_reload_nopause_poll = 9
};
/**
* Fast reload printout queue. Contains a list of strings, that need to be
* printed over the file descriptor.
*/
struct fast_reload_printq {
/** if this item is in a list, the previous and next */
struct fast_reload_printq *prev, *next;
/** if this item is in a list, it is true. */
int in_list;
/** list of strings to printout */
struct config_strlist_head* to_print;
/** the current item to print. It is malloced. NULL if none. */
char* client_item;
/** The length, strlen, of the client_item, that has to be sent. */
int client_len;
/** The number of bytes sent of client_item. */
int client_byte_count;
/** the comm point for the client connection, the remote control
* client. */
struct comm_point* client_cp;
/** the remote control connection to print output to. */
struct remote_stream remote;
/** the worker that the event is added in */
struct worker* worker;
};
/**
* Fast reload auth zone change. Keeps track if an auth zone was removed,
* added or changed. This is needed because workers can have events for
* dealing with auth zones, like transfers, and those have to be removed
* too, not just the auth zone structure from the tree. */
struct fast_reload_auth_change {
/** next in the list of auth zone changes. */
struct fast_reload_auth_change* next;
/** the zone in the old config */
struct auth_zone* old_z;
/** the zone in the new config */
struct auth_zone* new_z;
/** if the zone was deleted */
int is_deleted;
/** if the zone was added */
int is_added;
/** if the zone has been changed */
int is_changed;
};
/**
* Fast reload thread structure
*/
struct fast_reload_thread {
/** the thread number for the dtio thread,
* must be first to cast thread arg to int* in checklock code. */
int threadnum;
/** communication socket pair, that sends commands */
int commpair[2];
/** thread id, of the io thread */
ub_thread_type tid;
/** if the io processing has started */
int started;
/** if the thread has to quit */
int need_to_quit;
/** verbosity of the fast_reload command, the number of +v options */
int fr_verb;
/** option to not pause threads during reload */
int fr_nopause;
/** option to drop mesh queries */
int fr_drop_mesh;
/** the event that listens on the remote service worker to the
* commpair, it receives content from the fast reload thread. */
void* service_event;
/** if the event that listens on the remote service worker has
* been added to the comm base. */
int service_event_is_added;
/** the service event can read a cmd, nonblocking, so it can
* save the partial read cmd here */
uint32_t service_read_cmd;
/** the number of bytes in service_read_cmd */
int service_read_cmd_count;
/** the worker that the service_event is added in */
struct worker* worker;
/** the printout of output to the remote client. */
struct fast_reload_printq *printq;
/** lock on fr_output, to stop race when both remote control thread
* and fast reload thread use fr_output list. */
lock_basic_type fr_output_lock;
/** list of strings, that the fast reload thread produces that have
* to be printed. The remote control thread can pick them up with
* the lock. */
struct config_strlist_head* fr_output;
/** communication socket pair, to respond to the reload request */
int commreload[2];
/** the list of auth zone changes. */
struct fast_reload_auth_change* auth_zone_change_list;
/** the old tree of auth zones, to lookup. */
struct auth_zones* old_auth_zones;
};
/**
* Create new remote control state for the daemon.
* @param cfg: config file with key file settings.
@ -203,4 +336,38 @@ int ssl_printf(RES* ssl, const char* format, ...)
int ssl_read_line(RES* ssl, char* buf, size_t max);
#endif /* HAVE_SSL */
/**
* Start fast reload thread
* @param ssl: the RES connection to print to.
* @param worker: the remote servicing worker.
* @param s: the rc_state that is servicing the remote control connection to
* the remote control client. It needs to be moved away to stay connected
* while the fast reload is running.
* @param fr_verb: verbosity to print output at. 0 is nothing, 1 is some
* and 2 is more detail.
* @param fr_nopause: option to not pause threads during reload.
* @param fr_drop_mesh: option to drop mesh queries.
*/
void fast_reload_thread_start(RES* ssl, struct worker* worker,
struct rc_state* s, int fr_verb, int fr_nopause, int fr_drop_mesh);
/**
* Stop fast reload thread
* @param fast_reload_thread: the thread struct.
*/
void fast_reload_thread_stop(struct fast_reload_thread* fast_reload_thread);
/** fast reload thread commands to remote service thread event callback */
void fast_reload_service_cb(int fd, short bits, void* arg);
/** fast reload callback for the remote control client connection */
int fast_reload_client_callback(struct comm_point* c, void* arg, int err,
struct comm_reply* rep);
/** fast reload printq delete list */
void fast_reload_printq_list_delete(struct fast_reload_printq* list);
/** Pick up per worker changes after a fast reload. */
void fast_reload_worker_pickup_changes(struct worker* worker);
#endif /* DAEMON_REMOTE_H */

View file

@ -273,6 +273,7 @@ server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset)
/* add in the values from the mesh */
s->svr.ans_secure += (long long)worker->env.mesh->ans_secure;
s->svr.ans_bogus += (long long)worker->env.mesh->ans_bogus;
s->svr.val_ops += (long long)worker->env.mesh->val_ops;
s->svr.ans_rcode_nodata += (long long)worker->env.mesh->ans_nodata;
s->svr.ans_expired += (long long)worker->env.mesh->ans_expired;
for(i=0; i<UB_STATS_RCODE_NUM; i++)
@ -285,6 +286,8 @@ server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset)
(long long)worker->env.mesh->num_queries_discard_timeout;
s->svr.num_queries_wait_limit +=
(long long)worker->env.mesh->num_queries_wait_limit;
s->svr.num_dns_error_reports +=
(long long)worker->env.mesh->num_dns_error_reports;
/* values from outside network */
s->svr.unwanted_replies = (long long)worker->back->unwanted_replies;
s->svr.qtcp_outgoing = (long long)worker->back->num_tcp_outgoing;
@ -329,20 +332,8 @@ server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset)
s->svr.num_query_dnscrypt_replay = 0;
#endif /* USE_DNSCRYPT */
if(worker->env.auth_zones) {
if(reset && !worker->env.cfg->stat_cumulative) {
lock_rw_wrlock(&worker->env.auth_zones->lock);
} else {
lock_rw_rdlock(&worker->env.auth_zones->lock);
}
s->svr.num_query_authzone_up = (long long)worker->env.
auth_zones->num_query_up;
s->svr.num_query_authzone_down = (long long)worker->env.
auth_zones->num_query_down;
if(reset && !worker->env.cfg->stat_cumulative) {
worker->env.auth_zones->num_query_up = 0;
worker->env.auth_zones->num_query_down = 0;
}
lock_rw_unlock(&worker->env.auth_zones->lock);
s->svr.num_query_authzone_up += (long long)worker->env.mesh->num_query_authzone_up;
s->svr.num_query_authzone_down += (long long)worker->env.mesh->num_query_authzone_down;
}
s->svr.mem_stream_wait =
(long long)tcp_req_info_get_stream_buffer_size();
@ -458,9 +449,12 @@ void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a)
total->svr.num_queries_discard_timeout +=
a->svr.num_queries_discard_timeout;
total->svr.num_queries_wait_limit += a->svr.num_queries_wait_limit;
total->svr.num_dns_error_reports += a->svr.num_dns_error_reports;
total->svr.num_queries_missed_cache += a->svr.num_queries_missed_cache;
total->svr.num_queries_prefetch += a->svr.num_queries_prefetch;
total->svr.num_queries_timed_out += a->svr.num_queries_timed_out;
total->svr.num_query_authzone_up += a->svr.num_query_authzone_up;
total->svr.num_query_authzone_down += a->svr.num_query_authzone_down;
if (total->svr.max_query_time_us < a->svr.max_query_time_us)
total->svr.max_query_time_us = a->svr.max_query_time_us;
total->svr.sum_query_list_size += a->svr.sum_query_list_size;
@ -468,9 +462,9 @@ void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a)
#ifdef USE_DNSCRYPT
total->svr.num_query_dnscrypt_crypted += a->svr.num_query_dnscrypt_crypted;
total->svr.num_query_dnscrypt_cert += a->svr.num_query_dnscrypt_cert;
total->svr.num_query_dnscrypt_cleartext += \
total->svr.num_query_dnscrypt_cleartext +=
a->svr.num_query_dnscrypt_cleartext;
total->svr.num_query_dnscrypt_crypted_malformed += \
total->svr.num_query_dnscrypt_crypted_malformed +=
a->svr.num_query_dnscrypt_crypted_malformed;
#endif /* USE_DNSCRYPT */
/* the max size reached is upped to higher of both */
@ -502,6 +496,7 @@ void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a)
total->svr.ans_rcode_nodata += a->svr.ans_rcode_nodata;
total->svr.ans_secure += a->svr.ans_secure;
total->svr.ans_bogus += a->svr.ans_bogus;
total->svr.val_ops += a->svr.val_ops;
total->svr.unwanted_replies += a->svr.unwanted_replies;
total->svr.unwanted_queries += a->svr.unwanted_queries;
total->svr.tcp_accept_usage += a->svr.tcp_accept_usage;

View file

@ -174,7 +174,7 @@ static void
checkrlimits(struct config_file* cfg)
{
#ifndef S_SPLINT_S
#ifdef HAVE_GETRLIMIT
#if defined(HAVE_GETRLIMIT) && !defined(unbound_testbound)
/* list has number of ports to listen to, ifs number addresses */
int list = ((cfg->do_udp?1:0) + (cfg->do_tcp?1 +
(int)cfg->incoming_num_tcp:0));
@ -463,6 +463,62 @@ detach(void)
#endif /* HAVE_DAEMON */
}
#ifdef HAVE_SSL
/* setup a listening ssl context, fatal_exit() on any failure */
static void
setup_listen_sslctx(void** ctx, int is_dot, int is_doh, struct config_file* cfg)
{
if(!(*ctx = listen_sslctx_create(
cfg->ssl_service_key, cfg->ssl_service_pem, NULL,
cfg->tls_ciphers, cfg->tls_ciphersuites,
(cfg->tls_session_ticket_keys.first &&
cfg->tls_session_ticket_keys.first->str[0] != 0),
is_dot, is_doh))) {
fatal_exit("could not set up listen SSL_CTX");
}
}
#endif /* HAVE_SSL */
/* setups the needed ssl contexts, fatal_exit() on any failure */
static void
setup_sslctxs(struct daemon* daemon, struct config_file* cfg)
{
#ifdef HAVE_SSL
if(!(daemon->rc = daemon_remote_create(cfg)))
fatal_exit("could not set up remote-control");
if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
/* setup the session keys; the callback to use them will be
* attached to each sslctx separately */
if(cfg->tls_session_ticket_keys.first &&
cfg->tls_session_ticket_keys.first->str[0] != 0) {
if(!listen_sslctx_setup_ticket_keys(
cfg->tls_session_ticket_keys.first)) {
fatal_exit("could not set session ticket SSL_CTX");
}
}
(void)setup_listen_sslctx(&daemon->listen_dot_sslctx, 1, 0, cfg);
#ifdef HAVE_NGHTTP2_NGHTTP2_H
if(cfg_has_https(cfg)) {
(void)setup_listen_sslctx(&daemon->listen_doh_sslctx, 0, 1, cfg);
}
#endif
#ifdef HAVE_NGTCP2
if(cfg_has_quic(cfg)) {
if(!(daemon->listen_quic_sslctx = quic_sslctx_create(
cfg->ssl_service_key, cfg->ssl_service_pem, NULL))) {
fatal_exit("could not set up quic SSL_CTX");
}
}
#endif /* HAVE_NGTCP2 */
}
if(!(daemon->connect_dot_sslctx = connect_sslctx_create(NULL, NULL,
cfg->tls_cert_bundle, cfg->tls_win_cert)))
fatal_exit("could not set up connect SSL_CTX");
#else /* HAVE_SSL */
(void)daemon;(void)cfg;
#endif /* HAVE_SSL */
}
/** daemonize, drop user privileges and chroot if needed */
static void
perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
@ -489,36 +545,7 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
#endif
/* read ssl keys while superuser and outside chroot */
#ifdef HAVE_SSL
if(!(daemon->rc = daemon_remote_create(cfg)))
fatal_exit("could not set up remote-control");
if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
if(!(daemon->listen_sslctx = listen_sslctx_create(
cfg->ssl_service_key, cfg->ssl_service_pem, NULL)))
fatal_exit("could not set up listen SSL_CTX");
if(cfg->tls_ciphers && cfg->tls_ciphers[0]) {
if (!SSL_CTX_set_cipher_list(daemon->listen_sslctx, cfg->tls_ciphers)) {
fatal_exit("failed to set tls-cipher %s", cfg->tls_ciphers);
}
}
#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
if(cfg->tls_ciphersuites && cfg->tls_ciphersuites[0]) {
if (!SSL_CTX_set_ciphersuites(daemon->listen_sslctx, cfg->tls_ciphersuites)) {
fatal_exit("failed to set tls-ciphersuites %s", cfg->tls_ciphersuites);
}
}
#endif
if(cfg->tls_session_ticket_keys.first &&
cfg->tls_session_ticket_keys.first->str[0] != 0) {
if(!listen_sslctx_setup_ticket_keys(daemon->listen_sslctx, cfg->tls_session_ticket_keys.first)) {
fatal_exit("could not set session ticket SSL_CTX");
}
}
}
if(!(daemon->connect_sslctx = connect_sslctx_create(NULL, NULL,
cfg->tls_cert_bundle, cfg->tls_win_cert)))
fatal_exit("could not set up connect SSL_CTX");
#endif
(void)setup_sslctxs(daemon, cfg);
/* init syslog (as root) if needed, before daemonize, otherwise
* a fork error could not be printed since daemonize closed stderr.*/
@ -681,6 +708,9 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
* it would succeed on SIGHUP as well */
if(!cfg->use_syslog)
log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
daemon->cfgfile = strdup(*cfgfile);
if(!daemon->cfgfile)
fatal_exit("out of memory in daemon cfgfile strdup");
}
/**
@ -715,6 +745,7 @@ run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode, int need_pi
"the commandline to see more errors, "
"or unbound-checkconf", cfgfile);
log_warn("Continuing with default config settings");
config_auto_slab_values(cfg);
}
apply_settings(daemon, cfg, cmdline_verbose, debug_mode);
if(!done_setup)

View file

@ -371,6 +371,84 @@ worker_check_request(sldns_buffer* pkt, struct worker* worker,
return;
}
/**
* Send fast-reload acknowledgement to the mainthread in one byte.
* This signals that this worker has received the previous command.
* The worker is waiting if that is after a reload_stop command.
* Or the worker has briefly processed the event itself, and in doing so
* released data pointers to old config, after a reload_poll command.
*/
static void
worker_send_reload_ack(struct worker* worker)
{
/* If this is clipped to 8 bits because thread_num>255, then that
* is not a problem, the receiver counts the number of bytes received.
* The number is informative only. */
uint8_t c = (uint8_t)worker->thread_num;
ssize_t ret;
while(1) {
ret = send(worker->daemon->fast_reload_thread->commreload[1],
(void*)&c, 1, 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)
continue; /* Try again. */
log_err("worker reload ack reply: send failed: %s",
sock_strerror(errno));
break;
}
break;
}
}
/** stop and wait to resume the worker */
static void
worker_stop_and_wait(struct worker* worker)
{
uint8_t* buf = NULL;
uint32_t len = 0, cmd;
worker_send_reload_ack(worker);
/* wait for reload */
if(!tube_read_msg(worker->cmd, &buf, &len, 0)) {
log_err("worker reload read reply failed");
return;
}
if(len != sizeof(uint32_t)) {
log_err("worker reload reply, bad control msg length %d",
(int)len);
free(buf);
return;
}
cmd = sldns_read_uint32(buf);
free(buf);
if(cmd == worker_cmd_quit) {
/* quit anyway */
verbose(VERB_ALGO, "reload reply, control cmd quit");
comm_base_exit(worker->base);
return;
}
if(cmd != worker_cmd_reload_start) {
log_err("worker reload reply, wrong reply command");
}
if(worker->daemon->fast_reload_drop_mesh) {
verbose(VERB_ALGO, "worker: drop mesh queries after reload");
mesh_delete_all(worker->env.mesh);
}
fast_reload_worker_pickup_changes(worker);
worker_send_reload_ack(worker);
verbose(VERB_ALGO, "worker resume after reload");
}
void
worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg,
size_t len, int error, void* arg)
@ -406,6 +484,15 @@ worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg,
verbose(VERB_ALGO, "got control cmd remote");
daemon_remote_exec(worker);
break;
case worker_cmd_reload_stop:
verbose(VERB_ALGO, "got control cmd reload_stop");
worker_stop_and_wait(worker);
break;
case worker_cmd_reload_poll:
verbose(VERB_ALGO, "got control cmd reload_poll");
fast_reload_worker_pickup_changes(worker);
worker_send_reload_ack(worker);
break;
default:
log_err("bad command %d", (int)cmd);
break;
@ -600,7 +687,8 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo,
return 1;
if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, &actinfo,
alias_rrset, 0, worker->scratchpad, az, NULL))
alias_rrset, 0, worker->scratchpad, az, NULL,
worker->env.views, worker->env.respip_set))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
@ -761,7 +849,8 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
} else if(partial_rep &&
!respip_merge_cname(partial_rep, qinfo, rep, cinfo,
must_validate, &encode_rep, worker->scratchpad,
worker->env.auth_zones)) {
worker->env.auth_zones, worker->env.views,
worker->env.respip_set)) {
goto bail_out;
}
if(encode_rep != rep) {
@ -1082,7 +1171,7 @@ answer_notify(struct worker* w, struct query_info* qinfo,
if(verbosity >= VERB_DETAIL) {
char buf[380];
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
char sr[25];
dname_str(qinfo->qname, zname);
sr[0]=0;
@ -1413,7 +1502,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
return 0;
}
if(c->dnscrypt && !repinfo->is_dnscrypted) {
char buf[LDNS_MAX_DOMAINLEN+1];
char buf[LDNS_MAX_DOMAINLEN];
/* Check if this is unencrypted and asking for certs */
worker_check_request(c->buffer, worker, &check_result);
if(check_result.value != 0) {
@ -1618,6 +1707,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
repinfo->client_addrlen, edns.cookie_valid,
c->buffer)) {
worker->stats.num_queries_ip_ratelimited++;
regional_free_all(worker->scratchpad);
comm_point_drop_reply(repinfo);
return 0;
}
@ -1729,8 +1819,9 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
goto send_reply;
}
if(worker->env.auth_zones &&
auth_zones_answer(worker->env.auth_zones, &worker->env,
&qinfo, &edns, repinfo, c->buffer, worker->scratchpad)) {
auth_zones_downstream_answer(worker->env.auth_zones,
&worker->env, &qinfo, &edns, repinfo, c->buffer,
worker->scratchpad)) {
regional_free_all(worker->scratchpad);
if(sldns_buffer_limit(c->buffer) == 0) {
comm_point_drop_reply(repinfo);
@ -1783,20 +1874,11 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
/* If we've found a local alias, replace the qname with the alias
* target before resolving it. */
if(qinfo.local_alias) {
struct ub_packed_rrset_key* rrset = qinfo.local_alias->rrset;
struct packed_rrset_data* d = rrset->entry.data;
/* Sanity check: our current implementation only supports
* a single CNAME RRset as a local alias. */
if(qinfo.local_alias->next ||
rrset->rk.type != htons(LDNS_RR_TYPE_CNAME) ||
d->count != 1) {
log_err("assumption failure: unexpected local alias");
if(!local_alias_shallow_copy_qname(qinfo.local_alias, &qinfo.qname,
&qinfo.qname_len)) {
regional_free_all(worker->scratchpad);
return 0; /* drop it */
}
qinfo.qname = d->rr_data[0] + 2;
qinfo.qname_len = d->rr_len[0] - 2;
}
/* If we may apply IP-based actions to the answer, build the client
@ -1813,7 +1895,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
cinfo_tmp.tag_datas = acladdr->tag_datas;
cinfo_tmp.tag_datas_size = acladdr->tag_datas_size;
cinfo_tmp.view = acladdr->view;
cinfo_tmp.respip_set = worker->daemon->respip_set;
cinfo_tmp.view_name = NULL;
cinfo = &cinfo_tmp;
}
@ -1845,10 +1927,10 @@ lookup_cache:
* its qname must be that used for cache
* lookup. */
if((worker->env.cfg->prefetch &&
*worker->env.now >= rep->prefetch_ttl) ||
rep->prefetch_ttl <= *worker->env.now) ||
(worker->env.cfg->serve_expired &&
*worker->env.now > rep->ttl)) {
rep->ttl < *worker->env.now &&
!(*worker->env.now < rep->serve_expired_norec_ttl))) {
time_t leeway = rep->ttl - *worker->env.now;
if(rep->ttl < *worker->env.now)
leeway = 0;
@ -1966,13 +2048,13 @@ send_reply_rc:
&repinfo->client_addr, repinfo->client_addrlen,
tv, 1, c->buffer,
(worker->env.cfg->log_destaddr?(void*)repinfo->c->socket->addr:NULL),
c->type);
c->type, c->ssl);
} else {
log_reply_info(NO_VERBOSE, &qinfo,
&repinfo->client_addr, repinfo->client_addrlen,
tv, 1, c->buffer,
(worker->env.cfg->log_destaddr?(void*)repinfo->c->socket->addr:NULL),
c->type);
c->type, c->ssl);
}
}
#ifdef USE_DNSCRYPT
@ -2173,10 +2255,11 @@ worker_init(struct worker* worker, struct config_file *cfg,
: cfg->tcp_idle_timeout,
cfg->harden_large_queries, cfg->http_max_streams,
cfg->http_endpoint, cfg->http_notls_downstream,
worker->daemon->tcl, worker->daemon->listen_sslctx,
worker->daemon->tcl, worker->daemon->listen_dot_sslctx,
worker->daemon->listen_doh_sslctx,
worker->daemon->listen_quic_sslctx,
dtenv, worker->daemon->doq_table, worker->env.rnd,
cfg->ssl_service_key, cfg->ssl_service_pem, cfg,
worker_handle_request, worker);
cfg, worker_handle_request, worker);
if(!worker->front) {
log_err("could not create listening sockets");
worker_delete(worker);
@ -2191,7 +2274,7 @@ worker_init(struct worker* worker, struct config_file *cfg,
cfg->unwanted_threshold, cfg->outgoing_tcp_mss,
&worker_alloc_cleanup, worker,
cfg->do_udp || cfg->udp_upstream_without_downstream,
worker->daemon->connect_sslctx, cfg->delay_close,
worker->daemon->connect_dot_sslctx, cfg->delay_close,
cfg->tls_use_sni, dtenv, cfg->udp_connect,
cfg->max_reuse_tcp_queries, cfg->tcp_reuse_timeout,
cfg->tcp_auth_query_timeout);

View file

@ -72,7 +72,13 @@ enum worker_commands {
/** obtain statistics without statsclear */
worker_cmd_stats_noreset,
/** execute remote control command */
worker_cmd_remote
worker_cmd_remote,
/** for fast-reload, perform stop */
worker_cmd_reload_stop,
/** for fast-reload, start again */
worker_cmd_reload_start,
/** for fast-reload, poll to make sure worker has released data */
worker_cmd_reload_poll
};
/**

View file

@ -631,7 +631,7 @@ handle_event_moddone(struct module_qstate* qstate, int id)
/* When an AAAA query completes check if we want to perform DNS64
* synthesis. We skip queries with DNSSEC enabled (!CD) and
* ones generated by us to retrive the A/PTR record to use for
* ones generated by us to retrieve the A/PTR record to use for
* synth. */
int could_synth =
qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA &&

View file

@ -192,8 +192,11 @@ static void
dt_apply_identity(struct dt_env *env, struct config_file *cfg)
{
char buf[MAXHOSTNAMELEN+1];
if (!cfg->dnstap_send_identity)
if (!cfg->dnstap_send_identity) {
free(env->identity);
env->identity = NULL;
return;
}
free(env->identity);
if (cfg->dnstap_identity == NULL || cfg->dnstap_identity[0] == 0) {
if (gethostname(buf, MAXHOSTNAMELEN) == 0) {
@ -215,8 +218,11 @@ dt_apply_identity(struct dt_env *env, struct config_file *cfg)
static void
dt_apply_version(struct dt_env *env, struct config_file *cfg)
{
if (!cfg->dnstap_send_version)
if (!cfg->dnstap_send_version) {
free(env->version);
env->version = NULL;
return;
}
free(env->version);
if (cfg->dnstap_version == NULL || cfg->dnstap_version[0] == 0)
env->version = strdup(PACKAGE_STRING);
@ -230,13 +236,8 @@ dt_apply_version(struct dt_env *env, struct config_file *cfg)
}
void
dt_apply_cfg(struct dt_env *env, struct config_file *cfg)
dt_apply_logcfg(struct dt_env *env, struct config_file *cfg)
{
if (!cfg->dnstap)
return;
dt_apply_identity(env, cfg);
dt_apply_version(env, cfg);
if ((env->log_resolver_query_messages = (unsigned int)
cfg->dnstap_log_resolver_query_messages))
{
@ -275,6 +276,17 @@ dt_apply_cfg(struct dt_env *env, struct config_file *cfg)
lock_basic_unlock(&env->sample_lock);
}
void
dt_apply_cfg(struct dt_env *env, struct config_file *cfg)
{
if (!cfg->dnstap)
return;
dt_apply_identity(env, cfg);
dt_apply_version(env, cfg);
dt_apply_logcfg(env, cfg);
}
int
dt_init(struct dt_env *env, struct comm_base* base)
{
@ -530,7 +542,7 @@ dt_msg_send_outside_query(struct dt_env *env,
qflags = sldns_buffer_read_u16_at(qmsg, 2);
/* type */
if (qflags & BIT_RD) {
if ((qflags & BIT_RD)) {
if (!env->log_forwarder_query_messages)
return;
dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY);
@ -587,7 +599,7 @@ dt_msg_send_outside_response(struct dt_env *env,
qflags = ntohs(qflags);
/* type */
if (qflags & BIT_RD) {
if ((qflags & BIT_RD)) {
if (!env->log_forwarder_response_messages)
return;
dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE);

View file

@ -106,6 +106,13 @@ dt_create(struct config_file* cfg);
void
dt_apply_cfg(struct dt_env *env, struct config_file *cfg);
/**
* Apply config settings for log enable for message types.
* @param env: dnstap environment object.
* @param cfg: new config settings.
*/
void dt_apply_logcfg(struct dt_env *env, struct config_file *cfg);
/**
* Initialize per-worker state in dnstap environment object.
* @param env: dnstap environment object to initialize, created with dt_create().

View file

@ -18,10 +18,41 @@ AC_DEFUN([dt_DNSTAP],
[opt_dnstap_socket_path="$1"])
if test "x$opt_dnstap" != "xno"; then
AC_PATH_PROG([PROTOC_C], [protoc-c])
if test -z "$PROTOC_C"; then
AC_MSG_ERROR([The protoc-c program was not found. Please install protobuf-c!])
fi
AC_PATH_PROG([PROTOC], [protoc])
# 'protoc-c' is deprecated. We use 'protoc' instead. If it can not be
# found, try 'protoc-c'.
if test -z "$PROTOC"; then
AC_PATH_PROG([PROTOC_C], [protoc-c])
else
PROTOC_C="$PROTOC"
fi
if test -z "$PROTOC_C"; then
AC_MSG_ERROR([[The protoc or protoc-c program was not found. It is needed for dnstap, use --disable-dnstap, or install protobuf-c to provide protoc or protoc-c]])
fi
# Check for protoc-gen-c plugin
AC_PATH_PROG([PROTOC_GEN_C], [protoc-gen-c])
if test -z "$PROTOC_GEN_C"; then
AC_MSG_ERROR([[The protoc-gen-c plugin was not found. It is needed for dnstap, use --disable-dnstap, or install protobuf-c-compiler to provide protoc-gen-c]])
fi
# Test that protoc-gen-c actually works
AC_MSG_CHECKING([if protoc-gen-c plugin works])
cat > conftest.proto << EOF
syntax = "proto2";
message TestMessage {
optional string test_field = 1;
}
EOF
if $PROTOC_C --c_out=. conftest.proto >/dev/null 2>&1; then
AC_MSG_RESULT([yes])
rm -f conftest.proto conftest.pb-c.c conftest.pb-c.h
else
AC_MSG_RESULT([no])
rm -f conftest.proto conftest.pb-c.c conftest.pb-c.h
AC_MSG_ERROR([[The protoc-gen-c plugin is not working properly. Please ensure protobuf-c-compiler is properly installed]])
fi
AC_ARG_WITH([protobuf-c],
AS_HELP_STRING([--with-protobuf-c=path], [Path where protobuf-c is installed, for dnstap]),
[

View file

@ -98,7 +98,7 @@ message Policy {
// rule: the rule matched by the message.
//
// In a RPZ context, this is the owner name of the rule in
// the Reponse Policy Zone in wire format.
// the Response Policy Zone in wire format.
optional bytes rule = 2;
// action: the policy action taken in response to the

View file

@ -1509,7 +1509,7 @@ void dtio_output_cb(int ATTR_UNUSED(fd), short bits, void* arg)
}
#endif
if((bits&UB_EV_READ || dtio->ssl_brief_write)) {
if((bits&UB_EV_READ) || dtio->ssl_brief_write) {
#ifdef HAVE_SSL
if(dtio->ssl_brief_write)
(void)dtio_disable_brief_write(dtio);

View file

@ -346,7 +346,8 @@ static struct tap_socket* tap_socket_new_tlsaccept(char* ip,
s->fd = -1;
s->ev_cb = ev_cb;
s->data = data;
s->sslctx = listen_sslctx_create(server_key, server_cert, verifypem);
s->sslctx = listen_sslctx_create(server_key, server_cert, verifypem,
NULL, NULL, 0, 0, 0);
if(!s->sslctx) {
log_err("could not create ssl context");
free(s->ip);
@ -1786,6 +1787,20 @@ void remote_get_opt_ssl(char* ATTR_UNUSED(str), void* ATTR_UNUSED(arg))
log_assert(0);
}
void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
#ifdef HAVE_NGTCP2
void doq_client_event_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))

View file

@ -1,3 +1,470 @@
8 September 2025: Yorgos
- Update documentation for using "SET ... EX" in Redis.
- Document max buffer sizes for Redis commands.
- Update man pages.
3 September 2025: Wouter
- For #1328: make depend.
2 September 2025: Wouter
- Fix #1235: Outdated Python2 code in
unbound/pythonmod/examples/log.py.
- Fix #1324: Memory leak in 'msgparse.c' in
'parse_edns_options_from_query(...)'.
- Fix indentation in tcp-mss option parsing.
1 September 2025: Wouter
- Fix for #1324: Fix to free edns options scratch in ratelimit case.
29 August 2025: Yorgos
- Limit the number of consecutive reads on an HTTP/2 session.
Thanks to Gal Bar Nahum for exposing the possibility of infinite
reads on the session.
28 August 2025: Wouter
- Fix setup_listen_sslctx warning for nettle compile.
27 August 2025: Wouter
- Fix unbound-control dump_cache for double unlock of lruhash table.
26 August 2025: Wouter
- Fix ports workflow to install expat for macos.
22 August 2025: Wouter
- For #1318: Fix compile warnings for DoH compile on windows.
- Fix sha1 enable environment variable in test code on windows.
- Fix #1319: [FR] zone status for Unbound auth-zones.
- Fix that the zone acquired timestamp is set after the
zonefile is read.
21 August 2025: Wouter
- Fix to check for extraneous command arguments for unbound-control,
when the command takes no arguments but there are arguments present.
- Fix #1317: Unbound starts too early. Add
Wants=network-online.target under [Unit] in unbound.service.
- Fix for #1317: Fix contrib/unbound.service comment path for
systemd network configuration.
15 August 2025: Wouter
- unbound-control cache_lookup +t allows tld and root names. And
subnet cache contents are printed.
- Fix cache_lookup subnet printout to wipe zero part of the prefix.
- Fix cache_lookup subnet print to not print messages without rrsets
and perform in-depth check on node in the addrtree.
14 August 2025: Wouter
- Fix to increase responsiveness of dump_cache.
- Fix to decouple file descriptor activity and cache lookups in
dump_cache.
13 August 2025: Wouter
- unbound-control cache_lookup <domains> prints the cached rrsets
and messages for those.
- Fix to remove debug from cache_lookup.
- Fix to unlock cache_lookup message for malformed records.
12 August 2025: Wouter
- Fix that unbound-control dump_cache releases the cache locks
every so often, so that the server stays responsive.
7 August 2025: Wouter
- Fix dname_str for printout of long names. Thanks to Jan Komissar
for the fix.
- Fix that edns-subnet failure to create a subquery errors as
servfail, and not formerror.
- Fix to whitespace in dname_str.
6 August 2025: Wouter
- Fix edns subnet, so that the subquery without subnet is stored in
global cache if the querier used 0.0.0.0/0 and the name and address
do not receive subnet treatment. If the name and address are
configured for subnet, it is stored in the subnet cache.
5 August 2025: Wouter
- Fix #1309: incorrectly reclaimed tcp handler can cause data
corruption and segfault.
- Fix to use assertions for consistency checks in #1309 reclaimed
tcp handlers.
1 August 2025: Wouter
- Fix testbound test program to accurately output packets from hex.
28 July 2025: Wouter
- Fix redis cachedb module gettimeofday init failure.
24 July 2025: Wouter
- Redis checks for server down and throttles reconnects.
17 July 2025: Wouter
- Fix to not set rlimits in the unit tests.
- Fix #1303: [FR] Disable TLSv1.2.
- iana portlist updated.
16 July 2025: Wouter
- Fix for RebirthDay Attack CVE-2025-5994, reported by Xiang Li
from AOSP Lab Nankai University.
- Tag for 1.23.1 with the release of 1.23.0 and the CVE fix, the
repository continues with the previous fixes, with 1.23.2.
- Add unit tests for non-ecs aggregation.
12 July 2025: Yorgos
- Merge #1289 from Roland van Rijswijk-Deij: Add extra statistic to
track the number of signature validation operations.
Adds 'num.valops' to extended statistics.
- For #1289: test num.valops in existing stat_values.tdir.
- For #1289: add num.valops in the unbound-control man page.
11 July 2025: Wouter
- Fix detection of SSL_CTX_set_tmp_ecdh function.
- For #1301: configure cant find SSL_is_quic in OpenSSL 3.5.1.
8 July 2025: Wouter
- Fix to improve dnstap discovery on Fedora.
3 July 2025: Wouter
- Fix #1300: Is 'sock-queue-timeout' a linux only feature.
- For #1300: implement sock-queue-timeout for FreeBSD as well.
- Fix layout of comm_point_udp_ancil_callback.
2 July 2025: Wouter
- Merge #1299: Fix typos.
- Generate ltmain.sh and configure again.
25 June 2025: Yorgos
- Fix #1247: forward-first: ssl handshake failed on root nameservers.
- For #1247, turn off fetch-policy for delegation when looking into
parent side name servers that may not update the addresses and hit
NXNS limits.
- For #1247, replay test (added tcp_transport to
outnet_serviced_query).
20 June 2025: Yorgos
- Fix #1293: EDE 6 is attached to insecure cached answers when client
sends the CD bit.
19 June 2025: Wouter
- Fix #1296: DNS over QUIC depends on a very outdated version of
ngtcp2. Fixed so it works with ngtcp2 1.13.0 and OpenSSL 3.5.0.
- Merge #1297: edns-subnet: fix NULL_AFTER_DEREF on subnetmod.
- Fix rrset cache create allocation failure case.
17 June 2025: Yorgos
- Fix for consistent use of local zone CNAME alias for configured auth
zones. Now it also applies to downstream configured auth zones.
16 June 2025: Wouter
- Fix to check control-interface addresses in unbound-checkconf.
- Fix #1295: Windows 32-bit binaries download seems to be missing dll
dependency.
12 June 2025: Wouter
- Fix header return value description for skip_pkt_rrs and
parse_edns_from_query_pkt.
11 June 2025: Wouter
- Fix bitwise operators in conditional expressions with parentheses.
- Fix conditional expressions with parentheses for bitwise and.
5 June 2025: Wouter
- Fix unbound-anchor certificate file read for line ends and end of
file.
- Fix comment for the dname_remove_label_limit_len function.
- iana portlist updated.
3 June 2025: Yorgos
- Small manpage corrections for the 'disable-dnssec-lame-check' option.
21 May 2025: Wouter
- Fix #1288: [FR] Improve fuzzing of unbound by adapting the netbound
program.
20 May 2025: Yorgos
- Merge #1285: RST man pages. It introduces restructuredText man pages
to sync the online and source code man page documentation.
The templated man pages (*.in) are still part of the repo but
generated with docutils from their .rst counterpart.
Documentation on how to generate those (mainly for core developers)
is in README.man.
- Add more checks about respip in unbound-checkconf.
Also fixes #310: unbound-checkconf not reporting RPZ configuration
error.
19 May 2025: Wouter
- Fix for cname chain length with qtype ANY and qname minimisation.
Thanks to Jim Greenwood from Nominet for the report.
15 May 2025: Wouter
- Fix config of slab values when there is no config file.
13 May 2025: Yorgos
- Fix #1284: NULL pointer deref in az_find_nsec_cover() (latent bug)
by adding a log_assert() to safeguard future development.
- Fix #1282: log-destaddr fail on long ipv6 addresses.
13 May 2025: Wouter
- Change default for so-sndbuf to 1m, to mitigate a cross-layer
issue where the UDP socket send buffers are exhausted waiting
for ARP/NDP resolution. Thanks to Reflyable for the report.
- Adjusted so-sndbuf default to 4m.
12 May 2025: Yorgos
- Merge #1280: Fix auth nsec3 code. Fixes NSEC3 code to not break on
broken auth zones that include unsigned out of zone (above apex)
data. Could lead to hang while trying to prove a wildcard answer.
12 May 2025: Wouter
- Fix #1283: Unsafe usage of atoi() while parsing the configuration
file.
9 May 2025: Wouter
- Fix #1281: forward-zone "name: ." conflicts with auth-zone "name: ."
in 1.23.0, but worked in 1.22.0.
5 May 2025: Yorgos
- Sync unbound and unbound-checkconf log output for unknown modules.
29 April 2025: Wouter
- Fix for parallel build of dnstap protoc-c output.
- Fix dnstap to use protoc.
29 April 2025: Yorgos
- Merge #1276: Auto-configure '-slabs' values.
28 April 2025: Yorgos
- Merge #1275: Use macros for the fr_check_changed* functions.
25 April 2025: Wouter
- Fix #1272: assertion failure testcode/unitverify.c:202.
16 April 2025: Wouter
- Increase default to `num-queries-per-thread: 2048`, when unbound is
compiled with libevent. It makes saturation of the task queue more
resource intensive and less practical. Thanks to Shiming Liu,
Network and Information Security Lab, Tsinghua University for the
report.
11 April 2025: Wouter
- Tag for 1.23.0rc2. This became the release of 1.23.0 on 24 April
2025. The code repository continues with 1.23.1 in development.
11 April 2025: Yorgos
- Merge #1265: Fix WSAPoll.
10 April 2025: Wouter
- Fix for print of connection type in log-replies for dot and doh.
9 April 2025: Wouter
- Fix to detect if atomic_store links in configure.
- Fix #1264: unbound 1.22.0 leaks memory when doing DoH.
8 April 2025: Wouter
- Tag for 1.23.0rc1.
- Fix fast_reload to print chroot with config file name.
7 April 2025: Yorgos
- Merge #902: DNS Error Reporting (RFC 9567). Introduces new
configuration option 'dns-error-reporting' and new statistics for
'num.dns_error_reports'.
4 April 2025: Wouter
- Fix mesh_copy_client_info to omit null contents from copy.
- Fix comment name in the rpz nsdname test.
- Fix nettle compile for warnings and ticket keys.
- Fix redis_replica test for unused option defaults and log printout.
- Fix test to speed up common.sh script kill_pid.
- Fix to update common.sh for speed of kill_pid.
4 April 2025: Yorgos
- Merge #1019: Redis read-only replica support.
Introduces new 'redis-replica-*' options for the Redis cache backend.
3 April 2025: Wouter
- Fix #1263: Exempt loopback addresses from wait-limit.
- Fix wait-limit-netblock and wait-limit-cookie-netblock config parse
to allow two arguments.
- Fix ub_event and include dnstap and win_svc headers.
- Fix test for stat_values for wait limit defaults for localhost.
- Fix parameter unused warning in net_help.c.
2 April 2025: Yorgos
- Merge #1262 from markyang92, fix build with
'gcc-15 -Wbuiltin-declaration-mismatch' error in compat/malloc.c.
- For #1262, ifdef is no longer needed.
2 April 2025: Wouter
- Fix unbound-control test so it counts the new flush_negative output,
also answers the _ta probe from testns and prints command output
and skip a thread specific test when no threads are available.
- Fix that ub_event has the facility to deal with callbacks for
fast reload, doq, windows-stop and dnstap.
- Fix fast reload test to check if pid exists before acting on it.
1 April 2025: Wouter
- Fix escape more characters when printing an RR type with an unquoted
string.
- Enable the auth_tls.tdir and auth_tls_failcert.tdir tests.
31 March 2025: Wouter
- iana portlist update.
- Merge #1042: Fast Reload. The unbound-control fast_reload is added.
It reads changed config in a thread, then only briefly pauses the
service threads, that keep running. DNS service is only interrupted
briefly, less than a second.
- Skip the unit tests for auth_tls.tdir and auth_tls_failcert.tdir.
27 March 2025: Wouter
- Fix unit test dname log printout typecast.
- Fix for ci test, expat is installed on the osx image.
26 March 2025: Yorgos
- Fix #1255: Multiple pinnings to vulnerable copies of libexpat.
- For #1255, for ios use an older expat version that does not require
C++11 language features.
- For #1255, for ios disable building tests that require C++11.
- For #1255, for ios try the latest expat version again.
24 March 2025: Wouter
- Fix #1254: `send failed: Socket is not connected` and
`remote address is 0.0.0.0 port 53`.
21 March 2025: Wouter
- Fix #1253: Cache entries fail to be removed from Redis cachedb
backend with unbound-control flush* +c.
- Fix for #1253: Fix for redis cachedb backend to expect an integer
reply for the EXPIRE command.
20 March 2025: Wouter
- Fix print of RR type NSAP-PTR, it is an unquoted string.
18 March 2025: Wouter
- Fix #1251: WSAPoll first argument cannot be NULL.
- Fix for windows compile create ssl contexts.
17 March 2025: Wouter
- Fix representation of types GPOS and RESINFO, add rdf type for
unquoted str.
16 March 2025: Yorgos
- Fix 'unbound-control flush_negative' when reporting removed data;
reported by David 'eqvinox' Lamparter.
28 February 2025: Wouter
- Merge #1238: Prefer SOURCE_DATE_EPOCH over actual time.
Add --help output description for the SOURCE_DATE_EPOCH variable.
25 February 2025: Wouter
- Merge #1243: Do not shadow tm on line 236.
24 February 2025: Yorgos
- Fix hash calculation for cachedb to ignore case. Previously, cached
records there were only relevant for same case queries (if not
already in Unbound's internal cache).
19 February 2025: Yorgos
- Fix static analysis report about unhandled EOF on error conditions
when reading anchor key files.
- Merge #1241: Fix infra-keep-probing for low infra-cache-max-rtt
values.
17 February 2025: Yorgos
- Consider reconfigurations when calculating the still_useful_timeout
for servers in the infrastructure cache.
30 January 2025: Wouter
- Fix #986: Resolving sas.com with dnssec-validation fails though
signed delegations seem to be (mostly) correct.
29 January 2025: Yorgos
- Make the default value of module-config "validator iterator"
regardless of compilation options. --enable-subnet would implicitly
change the value to enable the subnetcache module by default in the
past.
24 January 2025: Yorgos
- Merge #1220 from Petr Menšík, Add unbound members group access to
control key.
21 January 2025: Yorgos
- Use the same interface listening port discovery code for all needed
protocols.
- Port to string only when needed before getaddrinfo().
- Do not open unencrypted channels next to encrypted ones on the same
port.
- Merge #1224 from Theo Buehler: Do not use DSA API unless USE_DSA is
set.
21 January 2025: Wouter
- Fix compile of interface check code when dnscrypt or quic is
disabled.
- Fix encoding of RR type ATMA.
- Fix to check length in ATMA string to wire.
- Merge #1229: check before use daemon->shm_info.
20 January 2025: Yorgos
- Merge #1222: Unique DoT and DoH SSL contexts to allow for different
ALPN.
- Create the quic SSL listening context only when needed.
15 January 2025: Yorgos
- Merge #1221: Consider auth zones when checking for forwarders.
14 January 2025: Yorgos
- Add resolver.arpa and service.arpa to the default locally served
zones.
13 January 2025: Yorgos
- Fix #1213: Misleading error message on default access control causing
refuse.
10 January 2025: Yorgos
- Merge #1214: Use TCP_NODELAY on TLS sockets to speed up the TLS
handshake.
31 December 2024: Yorgos
- Merge #1174: Serve expired cache update fixes. Fixes a regression bug
with serve-expired that appeared in 1.22.0 and would not allow the
iterator to update the cache with not-yet-validated entries resulting
in increased outgoing traffic.
20 December 2024: Yorgos
- For #1207: [FR] Support for RESINFO RRType 261 (RFC9606), add
LDNS_RR_TYPE_RESINFO similar to LDNS_RR_TYPE_TXT.
13 December 2024: Yorgos
- Merge #1204: ci: set persist-credentials: false for actions/checkout
per zizmor suggestion.
3 December 2024: Yorgos
- Merge #1189: Fix the dname_str method to cause conversion errors
when the domain name length is 255.
- Merge #1197: dname_str() fixes.
- For #1175, the default value of serve-expired-ttl is set to 86400
(1 day) as suggested by RFC8767.
- Merge #1198: Fix log-servfail with serve expired and no useful cache
contents.
- Safeguard alias loop while looking in the cache for expired answers.
- Merge #1187: Create the SSL_CTX for QUIC before chroot and privilege
drop.
- Fix typo in log_servfail.tdir test.
22 November 2024: Yorgos
- Fix #1175: serve-expired does not adhere to secure-by-default
principle. The default value of serve-expired-client-timeout
is set to 1800 as suggested by RFC8767.
- For #1175, update serve-expired tests.
20 November 2024: Yorgos
- Fix comparison to help static analyzer.
19 November 2024: Yorgos
- Merge #1169 from Sergey Kacheev, fix: lock-free counters for
auth_zone up/down queries.
15 November 2024: Wouter
- Fix #1183: the data being used is released in method
nsec3_hash_test_entry.
- Fix for #1183: release nsec3 hashes per test file.
8 November 2024: Yorgos
- More descriptive text for 'harden-algo-downgrade'.
- Complete fix for max-global-quota to 200.
@ -433,7 +900,7 @@
now checks both single and multi process/thread operation.
16 May 2024: Yorgos
- Merge #1070: Fix rtt assignement for low values of
- Merge #1070: Fix rtt assignment for low values of
infra-cache-max-rtt.
16 May 2024: Wouter
@ -841,7 +1308,7 @@
13 October 2023: George
- Better fix for infinite loop when reading multiple lines of input on
a broken remote control socket, by treating a zero byte line the
same as transmission end. Addesses #947 and #948.
same as transmission end. Addresses #947 and #948.
12 October 2023: Wouter
- Merge #944: Disable EDNS DO.
@ -864,7 +1331,7 @@
10 October 2023: George
- Fix infinite loop when reading multiple lines of input on a broken
remote control socket. Addesses #947 and #948.
remote control socket. Addresses #947 and #948.
9 October 2023: Wouter
- Fix edns subnet so that queries with a source prefix of zero cause
@ -1297,7 +1764,7 @@
- Ignore expired error responses.
11 November 2022: Wouter
- Fix #779: [doc] Missing documention in ub_resolve_event() for
- Fix #779: [doc] Missing documentation in ub_resolve_event() for
callback parameter was_ratelimited.
9 November 2022: George
@ -2261,7 +2728,7 @@
not hang. removed trailing slashes from configure paths. Moved iOS
tests to allow-failure.
- travis, analyzer disabled on test without debug, that does not
run anway. Turn off failing tests except one. Update iOS test
run anyway. Turn off failing tests except one. Update iOS test
to xcode image 12.2.
22 March 2021: George
@ -2350,7 +2817,7 @@
- Fix build on Python 3.10.
10 February 2021: Wouter
- Merge PR #420 from dyunwei: DOH not responsing with
- Merge PR #420 from dyunwei: DOH not responding with
"http2_query_read_done failure" logged.
9 February 2021: Wouter
@ -2750,7 +3217,7 @@
6 August 2020: Wouter
- Merge PR #284 and Fix #246: Remove DLV entirely from Unbound.
The DLV has been decommisioned and in unbound 1.5.4, in 2015, there
The DLV has been decommissioned and in unbound 1.5.4, in 2015, there
was advise to stop using it. The current code base does not contain
DLV code any more. The use of dlv options displays a warning.
@ -3299,7 +3766,7 @@
3 December 2019: Wouter
- Merge pull request #124 from rmetrich: Changed log lock
from 'quick' to 'basic' because this is an I/O lock.
- Fix text around serial arithmatic used for RRSIG times to refer
- Fix text around serial arithmetic used for RRSIG times to refer
to correct RFC number.
- Fix Assert Causing DoS in synth_cname(),
reported by X41 D-Sec.
@ -3562,7 +4029,7 @@
- For #52 #53, second context does not close logfile override.
- Fix #52 #53, fix for example fail program.
- Fix to return after failed auth zone http chunk write.
- Fix to remove unused test for task_probe existance.
- Fix to remove unused test for task_probe existence.
- Fix to timeval_add for remaining second in microseconds.
- Check repinfo in worker_handle_request, if null, drop it.
@ -4819,7 +5286,7 @@
1 February 2018: Wouter
- fix unaligned structure making a false positive in checklock
unitialised memory.
uninitialised memory.
29 January 2018: Ralph
- Use NSEC with longest ce to prove wildcard absence.
@ -5422,8 +5889,8 @@
- Remove (now unused) event2 include from dnscrypt code.
24 March 2017: George
- Fix to prevent non-referal query from being cached as referal when the
no_cache_store flag was set.
- Fix to prevent non-referral query from being cached as referral when
the no_cache_store flag was set.
23 March 2017: Wouter
- Fix #1239: configure fails to find python distutils if python
@ -5486,7 +5953,7 @@
7 March 2017: Wouter
- Fix #1230: swig version 2.0.0 is required for pythonmod, with
1.3.40 it crashes when running repeatly unbound-control reload.
1.3.40 it crashes when running repeatedly unbound-control reload.
- Response actions based on IP address from Jinmei Tatuya (Infoblox).
6 March 2017: Wouter
@ -5502,7 +5969,7 @@
known vulns.
27 February 2017: Wouter
- Fix #1227: Fix that Unbound control allows weak ciphersuits.
- Fix #1227: Fix that Unbound control allows weak ciphersuites.
- Fix #1226: provide official 32bit binary for windows.
24 February 2017: Wouter
@ -6491,7 +6958,7 @@
- Fix #674: Do not free pointers given by getenv.
29 May 2015: Wouter
- Fix that unparseable error responses are ratelimited.
- Fix that unparsable error responses are ratelimited.
- SOA negative TTL is capped at minimumttl in its rdata section.
- cache-max-negative-ttl config option, default 3600.
@ -6509,7 +6976,7 @@
10 May 2015: Wouter
- Change syntax of particular validator error to be easier for
machine parse, swap rrset and ip adres info so it looks like:
machine parse, swap rrset and ip address info so it looks like:
validation failure <www.example.nl. TXT IN>: signature crypto
failed from 2001:DB8:7:bba4::53 for <*.example.nl. NSEC IN>
@ -8089,7 +8556,7 @@
- fix that --enable-static-exe does not complain about it unknown.
30 June 2011: Wouter
- tag relase 1.4.11, trunk is 1.4.12 development.
- tag release 1.4.11, trunk is 1.4.12 development.
- iana portlist updated.
- fix bug#395: id bits of other query may leak out under conditions
- fix replyaddr count wrong after jostled queries, which leads to
@ -9419,7 +9886,7 @@
8 June 2009: Wouter
- Removed RFC5011 REVOKE flag support. Partial 5011 support may cause
inadvertant behaviour.
inadvertent behaviour.
- 1.3.0 tarball for release created.
- 1.3.1 development in svn trunk.
- iana portlist updated.
@ -9768,7 +10235,7 @@
- initgroups(3) is called to drop secondary group permissions, if
applicable.
- configure option --with-ldns-builtin forces the use of the
inluded ldns package with the unbound source. The -I include
included ldns package with the unbound source. The -I include
is put before the others, so it avoids bad include files from
an older ldns install.
- daemon(3) posix call is used when available.
@ -10073,7 +10540,7 @@
please ranlib, stop file without symbols warning.
- harden referral path now also validates the root after priming.
It looks up the root NS authoritatively as well as the root servers
and attemps to validate the entries.
and attempts to validate the entries.
16 October 2008: Wouter
- Fixup negative TTL values appearing (reported by Attila Nagy).
@ -10852,7 +11319,7 @@
- please doxygen, put doxygen comment in one place.
- asynclook -b blocking mode and test.
- refactor asynclook, nicer code.
- fixup race problems from opensll in rand init from library, with
- fixup race problems from openssl in rand init from library, with
a mutex around the rand init.
- fix pass async_id=NULL to _async resolve().
- rewrote _wait() routine, so that it is threadsafe.
@ -11825,7 +12292,7 @@
11 June 2007: Wouter
- replies on TCP queries have the address field set in replyinfo,
for serviced queries, because the initiator does not know that
a TCP fallback has occured.
a TCP fallback has occurred.
- omit DNSSEC types from nonDO replies, except if qtype is ANY or
if qtype directly queries for the type (and then only show that
'unknown type' in the answer section).

16
doc/README.man Normal file
View file

@ -0,0 +1,16 @@
After Unbound 1.23.0, the source of the man pages is in reStructuredText format.
This helps with the online documentation at https://unbound.docs.nlnetlabs.nl
and makes it easier to maintain and contribute to the documentation.
The templated man pages (*.in) are still part of the code repository as to not
alter current procedures that could be in place by users/packagers.
The templated man pages (*.in) are generated by Sphinx (used for the online
documentation).
The online documentation has its own repository at
https://github.com/NLnetLabs/unbound-manual.
In the README.md there (branch test-auto for now), there are further simple
instructions on how to generate the templated man pages there and update them
in this repository.

View file

@ -116,8 +116,8 @@ server:
# so-rcvbuf: 0
# buffer size for UDP port 53 outgoing (SO_SNDBUF socket option).
# 0 is system default. Use 4m to handle spikes on very busy servers.
# so-sndbuf: 0
# 0 is system default. Set larger to handle spikes on very busy servers.
# so-sndbuf: 4m
# use SO_REUSEPORT to distribute queries over threads.
# at extreme load it could be better to turn it off to distribute even.
@ -163,7 +163,7 @@ server:
# msg-cache-slabs: 4
# the number of queries that a thread gets to service.
# num-queries-per-thread: 1024
# num-queries-per-thread: 2048
# if very busy, 50% queries run to completion, 50% get timeout in msec
# jostle-timeout: 200
@ -215,6 +215,12 @@ server:
# Apart from the default, the wait limit with cookie can be adjusted.
# wait-limit-cookie-netblock: 192.0.2.0/24 50000
# Defaults for loopback, it has no wait limit.
# wait-limit-netblock: 127.0.0.0/8 -1
# wait-limit-netblock: ::1/128 -1
# wait-limit-cookie-netblock: 127.0.0.0/8 -1
# wait-limit-cookie-netblock: ::1/128 -1
# the amount of memory to use for the RRset cache.
# plain value in bytes or you can append k, m or G. default is "4Mb".
# rrset-cache-size: 4m
@ -273,7 +279,7 @@ server:
# do-ip6: yes
# If running unbound on an IPv6-only host, domains that only have
# IPv4 servers would become unresolveable. If NAT64 is available in
# IPv4 servers would become unresolvable. If NAT64 is available in
# the network, unbound can use NAT64 to reach these servers with
# the following option. This is NOT needed for enabling DNS64 on a
# system that has IPv4 connectivity.
@ -731,12 +737,13 @@ server:
# disable-edns-do: no
# Serve expired responses from cache, with serve-expired-reply-ttl in
# the response, and then attempt to fetch the data afresh.
# the response. By default it first tries to refresh an expired answer.
# Can be configured with serve-expired-client-timeout.
# serve-expired: no
#
# Limit serving of expired responses to configured seconds after
# expiration. 0 disables the limit.
# serve-expired-ttl: 0
# serve-expired-ttl: 86400
#
# Set the TTL of expired records to the serve-expired-ttl value after a
# failed attempt to retrieve the record from upstream. This makes sure
@ -749,10 +756,9 @@ server:
#
# Time in milliseconds before replying to the client with expired data.
# This essentially enables the serve-stale behavior as specified in
# RFC 8767 that first tries to resolve before
# immediately responding with expired data. 0 disables this behavior.
# A recommended value is 1800.
# serve-expired-client-timeout: 0
# RFC 8767 that first tries to resolve before immediately responding
# with expired data. 0 disables this behavior.
# serve-expired-client-timeout: 1800
# Return the original TTL as received from the upstream name server rather
# than the decrementing TTL as stored in the cache. Enabling this feature
@ -811,6 +817,8 @@ server:
# local-zone: "127.in-addr.arpa." nodefault
# local-zone: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." nodefault
# local-zone: "home.arpa." nodefault
# local-zone: "resolver.arpa." nodefault
# local-zone: "service.arpa." nodefault
# local-zone: "onion." nodefault
# local-zone: "test." nodefault
# local-zone: "invalid." nodefault
@ -1078,6 +1086,11 @@ server:
# Note that the ede option above needs to be enabled for this to work.
# ede-serve-expired: no
# Enable DNS Error Reporting (RFC9567).
# qname-minimisation is advised to be turned on as well to increase
# privacy on the outgoing reports.
# dns-error-reporting: no
# Specific options for ipsecmod. Unbound needs to be configured with
# --enable-ipsecmod for these to take effect.
#
@ -1305,9 +1318,9 @@ remote-control:
# # redis server's TCP port
# redis-server-port: 6379
# # if the server uses a unix socket, set its path, or "" when not used.
# # redis-server-path: "/var/lib/redis/redis-server.sock"
# redis-server-path: "/var/lib/redis/redis-server.sock"
# # if the server uses an AUTH password, specify here, or "" when not used.
# # redis-server-password: ""
# redis-server-password: ""
# # timeout (in ms) for communication with the redis server
# redis-timeout: 100
# # timeout (in ms) for commands, if 0, uses redis-timeout.
@ -1318,6 +1331,22 @@ remote-control:
# redis-expire-records: no
# # redis logical database to use, 0 is the default database.
# redis-logical-db: 0
# # redis replica server's IP address or host name
# redis-replica-server-host: 127.0.0.1
# # redis replica server's TCP port
# redis-replica-server-port: 6379
# # if the replica server uses a unix socket, set its path, or "" when not used.
# redis-replica-server-path: "/var/lib/redis/redis-server.sock"
# # if the replica server uses an AUTH password, specify here, or "" when not used.
# redis-replica-server-password: ""
# # timeout (in ms) for communication with the redis replica server
# redis-replica-timeout: 100
# # timeout (in ms) for redis replica commands, if 0, uses redis-replica-timeout.
# redis-replica-command-timeout: 0
# # timeout (in ms) for redis replica connection set up, if 0, uses redis-replica-timeout.
# redis-replica-connect-timeout: 0
# # redis logical database to use for the replica server, 0 is the default database.
# redis-replica-logical-db: 0
# IPSet
# Add specify domain into set via ipset.

View file

@ -1,335 +1,306 @@
.TH "libunbound" "3" "@date@" "NLnet Labs" "unbound @version@"
.\"
.\" libunbound.3 -- unbound library functions manual
.\"
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
.\"
.\" See LICENSE for the license.
.\"
.\"
.SH "NAME"
.B libunbound,
.B unbound.h,
.B ub_ctx,
.B ub_result,
.B ub_callback_type,
.B ub_ctx_create,
.B ub_ctx_delete,
.B ub_ctx_set_option,
.B ub_ctx_get_option,
.B ub_ctx_config,
.B ub_ctx_set_fwd,
.B ub_ctx_set_stub,
.B ub_ctx_set_tls,
.B ub_ctx_resolvconf,
.B ub_ctx_hosts,
.B ub_ctx_add_ta,
.B ub_ctx_add_ta_autr,
.B ub_ctx_add_ta_file,
.B ub_ctx_trustedkeys,
.B ub_ctx_debugout,
.B ub_ctx_debuglevel,
.B ub_ctx_async,
.B ub_poll,
.B ub_wait,
.B ub_fd,
.B ub_process,
.B ub_resolve,
.B ub_resolve_async,
.B ub_cancel,
.B ub_resolve_free,
.B ub_strerror,
.B ub_ctx_print_local_zones,
.B ub_ctx_zone_add,
.B ub_ctx_zone_remove,
.B ub_ctx_data_add,
.B ub_ctx_data_remove
\- Unbound DNS validating resolver @version@ functions.
.SH "SYNOPSIS"
.B #include <unbound.h>
.LP
\fIstruct ub_ctx *\fR
\fBub_ctx_create\fR(\fIvoid\fR);
.LP
\fIvoid\fR
\fBub_ctx_delete\fR(\fIstruct ub_ctx*\fR ctx);
.LP
\fIint\fR
\fBub_ctx_set_option\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR opt, \fIchar*\fR val);
.LP
\fIint\fR
\fBub_ctx_get_option\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR opt, \fIchar**\fR val);
.LP
\fIint\fR
\fBub_ctx_config\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
.LP
\fIint\fR
\fBub_ctx_set_fwd\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR addr);
.LP
\fIint\fR
\fBub_ctx_set_stub\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR zone,
\fIchar*\fR addr,
.br
\fIint\fR isprime);
.LP
\fIint\fR
\fBub_ctx_set_tls\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR tls);
.LP
\fIint\fR
\fBub_ctx_resolvconf\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
.LP
\fIint\fR
\fBub_ctx_hosts\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
.LP
\fIint\fR
\fBub_ctx_add_ta\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR ta);
.LP
\fIint\fR
\fBub_ctx_add_ta_autr\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
.LP
\fIint\fR
\fBub_ctx_add_ta_file\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
.LP
\fIint\fR
\fBub_ctx_trustedkeys\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
.LP
\fIint\fR
\fBub_ctx_debugout\fR(\fIstruct ub_ctx*\fR ctx, \fIFILE*\fR out);
.LP
\fIint\fR
\fBub_ctx_debuglevel\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR d);
.LP
\fIint\fR
\fBub_ctx_async\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR dothread);
.LP
\fIint\fR
\fBub_poll\fR(\fIstruct ub_ctx*\fR ctx);
.LP
\fIint\fR
\fBub_wait\fR(\fIstruct ub_ctx*\fR ctx);
.LP
\fIint\fR
\fBub_fd\fR(\fIstruct ub_ctx*\fR ctx);
.LP
\fIint\fR
\fBub_process\fR(\fIstruct ub_ctx*\fR ctx);
.LP
\fIint\fR
\fBub_resolve\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR name,
.br
\fIint\fR rrtype, \fIint\fR rrclass, \fIstruct ub_result**\fR result);
.LP
\fIint\fR
\fBub_resolve_async\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR name,
.br
\fIint\fR rrtype, \fIint\fR rrclass, \fIvoid*\fR mydata,
.br
\fIub_callback_type\fR callback, \fIint*\fR async_id);
.LP
\fIint\fR
\fBub_cancel\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR async_id);
.LP
\fIvoid\fR
\fBub_resolve_free\fR(\fIstruct ub_result*\fR result);
.LP
\fIconst char *\fR
\fBub_strerror\fR(\fIint\fR err);
.LP
\fIint\fR
\fBub_ctx_print_local_zones\fR(\fIstruct ub_ctx*\fR ctx);
.LP
\fIint\fR
\fBub_ctx_zone_add\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR zone_name, \fIchar*\fR zone_type);
.LP
\fIint\fR
\fBub_ctx_zone_remove\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR zone_name);
.LP
\fIint\fR
\fBub_ctx_data_add\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR data);
.LP
\fIint\fR
\fBub_ctx_data_remove\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR data);
.SH "DESCRIPTION"
.B Unbound
is an implementation of a DNS resolver, that does caching and
DNSSEC validation. This is the library API, for using the \-lunbound library.
The server daemon is described in \fIunbound\fR(8).
The library works independent from a running unbound server, and
can be used to convert hostnames to ip addresses, and back,
and obtain other information from the DNS. The library performs public\-key
validation of results with DNSSEC.
.P
The library uses a variable of type \fIstruct ub_ctx\fR to keep context
between calls. The user must maintain it, creating it with
.B ub_ctx_create
and deleting it with
.B ub_ctx_delete\fR.
It can be created and deleted at any time. Creating it anew removes any
previous configuration (such as trusted keys) and clears any cached results.
.P
The functions are thread\-safe, and a context can be used in a threaded (as
well as in a non\-threaded) environment. Also resolution (and validation)
can be performed blocking and non\-blocking (also called asynchronous).
The async method returns from the call immediately, so that processing
can go on, while the results become available later.
.P
.\" Man page generated from reStructuredText.
.
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "LIBUNBOUND" "3" "@date@" "@version@" "Unbound"
.SH NAME
libunbound \- Unbound DNS validating resolver @version@ functions.
.SH SYNOPSIS
.sp
\fB#include <unbound.h>\fP
.sp
struct ub_ctx * \fBub_ctx_create\fP(void);
.sp
void \fBub_ctx_delete\fP(struct ub_ctx* ctx);
.sp
int \fBub_ctx_set_option\fP(struct ub_ctx* ctx, char* opt, char* val);
.sp
int \fBub_ctx_get_option\fP(struct ub_ctx* ctx, char* opt, char** val);
.sp
int \fBub_ctx_config\fP(struct ub_ctx* ctx, char* fname);
.sp
int \fBub_ctx_set_fwd\fP(struct ub_ctx* ctx, char* addr);
.INDENT 0.0
.TP
int \fBub_ctx_set_stub\fP(struct ub_ctx* ctx, char* zone, char* addr,
int isprime);
.UNINDENT
.sp
int \fBub_ctx_set_tls\fP(struct ub_ctx* ctx, int tls);
.sp
int \fBub_ctx_resolvconf\fP(struct ub_ctx* ctx, char* fname);
.sp
int \fBub_ctx_hosts\fP(struct ub_ctx* ctx, char* fname);
.sp
int \fBub_ctx_add_ta\fP(struct ub_ctx* ctx, char* ta);
.sp
int \fBub_ctx_add_ta_autr\fP(struct ub_ctx* ctx, char* fname);
.sp
int \fBub_ctx_add_ta_file\fP(struct ub_ctx* ctx, char* fname);
.sp
int \fBub_ctx_trustedkeys\fP(struct ub_ctx* ctx, char* fname);
.sp
int \fBub_ctx_debugout\fP(struct ub_ctx* ctx, FILE* out);
.sp
int \fBub_ctx_debuglevel\fP(struct ub_ctx* ctx, int d);
.sp
int \fBub_ctx_async\fP(struct ub_ctx* ctx, int dothread);
.sp
int \fBub_poll\fP(struct ub_ctx* ctx);
.sp
int \fBub_wait\fP(struct ub_ctx* ctx);
.sp
int \fBub_fd\fP(struct ub_ctx* ctx);
.sp
int \fBub_process\fP(struct ub_ctx* ctx);
.INDENT 0.0
.TP
int \fBub_resolve\fP(struct ub_ctx* ctx, char* name,
int rrtype, int rrclass, struct ub_result** result);
.TP
int \fBub_resolve_async\fP(struct ub_ctx* ctx, char* name,
int rrtype, int rrclass, void* mydata,
ub_callback_type* callback, int* async_id);
.UNINDENT
.sp
int \fBub_cancel\fP(struct ub_ctx* ctx, int async_id);
.sp
void \fBub_resolve_free\fP(struct ub_result* result);
.sp
const char * \fBub_strerror\fP(int err);
.sp
int \fBub_ctx_print_local_zones\fP(struct ub_ctx* ctx);
.sp
int \fBub_ctx_zone_add\fP(struct ub_ctx* ctx, char* zone_name, char* zone_type);
.sp
int \fBub_ctx_zone_remove\fP(struct ub_ctx* ctx, char* zone_name);
.sp
int \fBub_ctx_data_add\fP(struct ub_ctx* ctx, char* data);
.sp
int \fBub_ctx_data_remove\fP(struct ub_ctx* ctx, char* data);
.SH DESCRIPTION
.sp
Unbound is an implementation of a DNS resolver, that does caching and DNSSEC
validation.
This is the library API, for using the \fB\-lunbound\fP library.
The server daemon is described in \fI\%unbound(8)\fP\&.
The library works independent from a running unbound server, and can be used to
convert hostnames to ip addresses, and back, and obtain other information from
the DNS.
The library performs public\-key validation of results with DNSSEC.
.sp
The library uses a variable of type \fIstruct ub_ctx\fP to keep context between
calls.
The user must maintain it, creating it with \fBub_ctx_create\fP and deleting it
with \fBub_ctx_delete\fP\&.
It can be created and deleted at any time.
Creating it anew removes any previous configuration (such as trusted keys) and
clears any cached results.
.sp
The functions are thread\-safe, and a context can be used in a threaded (as well
as in a non\-threaded) environment.
Also resolution (and validation) can be performed blocking and non\-blocking
(also called asynchronous).
The async method returns from the call immediately, so that processing can go
on, while the results become available later.
.sp
The functions are discussed in turn below.
.SH "FUNCTIONS"
.TP
.SH FUNCTIONS
.INDENT 0.0
.TP
.B ub_ctx_create
Create a new context, initialised with defaults.
The information from /etc/resolv.conf and /etc/hosts is not utilised
by default. Use
.B ub_ctx_resolvconf
and
.B ub_ctx_hosts
to read them.
Before you call this, use the openssl functions CRYPTO_set_id_callback and
CRYPTO_set_locking_callback to set up asynchronous operation if you use
lib openssl (the application calls these functions once for initialisation).
Openssl 1.0.0 or later uses the CRYPTO_THREADID_set_callback function.
The information from \fB/etc/resolv.conf\fP and \fB/etc/hosts\fP is
not utilised by default.
Use \fBub_ctx_resolvconf\fP and \fBub_ctx_hosts\fP to read them.
Before you call this, use the openssl functions
\fBCRYPTO_set_id_callback\fP and \fBCRYPTO_set_locking_callback\fP to set
up asynchronous operation if you use lib openssl (the application calls
these functions once for initialisation).
Openssl 1.0.0 or later uses the \fBCRYPTO_THREADID_set_callback\fP
function.
.TP
.B ub_ctx_delete
Delete validation context and free associated resources.
Outstanding async queries are killed and callbacks are not called for them.
Outstanding async queries are killed and callbacks are not called for
them.
.TP
.B ub_ctx_set_option
A power\-user interface that lets you specify one of the options from the
config file format, see \fIunbound.conf\fR(5). Not all options are
relevant. For some specific options, such as adding trust anchors, special
routines exist. Pass the option name with the trailing ':'.
A power\-user interface that lets you specify one of the options from
the config file format, see \fI\%unbound.conf(5)\fP\&.
Not all options are relevant.
For some specific options, such as adding trust anchors, special
routines exist.
Pass the option name with the trailing \fB\(aq:\(aq\fP\&.
.TP
.B ub_ctx_get_option
A power\-user interface that gets an option value. Some options cannot be
gotten, and others return a newline separated list. Pass the option name
without trailing ':'. The returned value must be free(2)d by the caller.
A power\-user interface that gets an option value.
Some options cannot be gotten, and others return a newline separated
list.
Pass the option name without trailing \fB\(aq:\(aq\fP\&.
The returned value must be free(2)d by the caller.
.TP
.B ub_ctx_config
A power\-user interface that lets you specify an unbound config file, see
\fIunbound.conf\fR(5), which is read for configuration. Not all options are
relevant. For some specific options, such as adding trust anchors, special
routines exist. This function is thread\-safe only if a single instance of
ub_ctx* exists in the application. If several instances exist the
application has to ensure that ub_ctx_config is not called in parallel by
the different instances.
A power\-user interface that lets you specify an unbound config file,
see \fI\%unbound.conf(5)\fP, which is read for
configuration.
Not all options are relevant.
For some specific options, such as adding trust anchors, special
routines exist.
This function is thread\-safe only if a single instance of \fBub_ctx\fP*
exists in the application.
If several instances exist the application has to ensure that
\fBub_ctx_config\fP is not called in parallel by the different instances.
.TP
.B ub_ctx_set_fwd
Set machine to forward DNS queries to, the caching resolver to use.
IP4 or IP6 address. Forwards all DNS requests to that machine, which
is expected to run a recursive resolver. If the proxy is not
DNSSEC capable, validation may fail. Can be called several times, in
that case the addresses are used as backup servers.
At this time it is only possible to set configuration before the
first resolve is done.
Set machine to forward DNS queries to, the caching resolver to use.
IP4 or IP6 address.
Forwards all DNS requests to that machine, which is expected to run a
recursive resolver.
If the proxy is not DNSSEC capable, validation may fail.
Can be called several times, in that case the addresses are used as
backup servers.
At this time it is only possible to set configuration before the first
resolve is done.
.TP
.B ub_ctx_set_stub
Set a stub zone, authoritative dns servers to use for a particular zone.
IP4 or IP6 address. If the address is NULL the stub entry is removed.
Set isprime true if you configure root hints with it. Otherwise similar to
the stub zone item from unbound's config file. Can be called several times,
for different zones, or to add multiple addresses for a particular zone.
At this time it is only possible to set configuration before the
first resolve is done.
Set a stub zone, authoritative dns servers to use for a particular
zone.
IP4 or IP6 address.
If the address is NULL the stub entry is removed.
Set isprime true if you configure root hints with it.
Otherwise similar to the stub zone item from unbound\(aqs config file.
Can be called several times, for different zones, or to add multiple
addresses for a particular zone.
At this time it is only possible to set configuration before the first
resolve is done.
.TP
.B ub_ctx_set_tls
Enable DNS over TLS (DoT) for machines set with
.B ub_ctx_set_fwd.
At this time it is only possible to set configuration before the
first resolve is done.
Enable DNS over TLS (DoT) for machines set with \fBub_ctx_set_fwd\fP\&.
At this time it is only possible to set configuration before the first
resolve is done.
.TP
.B ub_ctx_resolvconf
By default the root servers are queried and full resolver mode is used, but
you can use this call to read the list of nameservers to use from the
filename given.
Usually "/etc/resolv.conf". Uses those nameservers as caching proxies.
By default the root servers are queried and full resolver mode is used,
but you can use this call to read the list of nameservers to use from
the filename given.
Usually \fB\(dq/etc/resolv.conf\(dq\fP\&.
Uses those nameservers as caching proxies.
If they do not support DNSSEC, validation may fail.
Only nameservers are picked up, the searchdomain, ndots and other
settings from \fIresolv.conf\fR(5) are ignored.
If fname NULL is passed, "/etc/resolv.conf" is used (if on Windows,
the system\-wide configured nameserver is picked instead).
At this time it is only possible to set configuration before the
first resolve is done.
settings from \fIresolv.conf(5)\fP are ignored.
If fname NULL is passed, \fB\(dq/etc/resolv.conf\(dq\fP is used (if on
Windows, the system\-wide configured nameserver is picked instead).
At this time it is only possible to set configuration before the first
resolve is done.
.TP
.B ub_ctx_hosts
Read list of hosts from the filename given.
Usually "/etc/hosts". When queried for, these addresses are not marked
DNSSEC secure. If fname NULL is passed, "/etc/hosts" is used
(if on Windows, etc/hosts from WINDIR is picked instead).
At this time it is only possible to set configuration before the
first resolve is done.
Usually \fB\(dq/etc/hosts\(dq\fP\&.
When queried for, these addresses are not marked DNSSEC secure.
If fname NULL is passed, \fB\(dq/etc/hosts\(dq\fP is used (if on Windows,
\fBetc/hosts\fP from WINDIR is picked instead).
At this time it is only possible to set configuration before the first
resolve is done.
.TP
.B
ub_ctx_add_ta
.B ub_ctx_add_ta
Add a trust anchor to the given context.
At this time it is only possible to add trusted keys before the
first resolve is done.
At this time it is only possible to add trusted keys before the first
resolve is done.
The format is a string, similar to the zone\-file format,
[domainname] [type] [rdata contents]. Both DS and DNSKEY records are accepted.
\fB[domainname]\fP \fB[type]\fP \fB[rdata contents]\fP\&.
Both DS and DNSKEY records are accepted.
.TP
.B ub_ctx_add_ta_autr
Add filename with automatically tracked trust anchor to the given context.
Pass name of a file with the managed trust anchor. You can create this
file with \fIunbound\-anchor\fR(8) for the root anchor. You can also
create it with an initial file with one line with a DNSKEY or DS record.
Add filename with automatically tracked trust anchor to the given
context.
Pass name of a file with the managed trust anchor.
You can create this file with
\fI\%unbound\-anchor(8)\fP for the root anchor.
You can also create it with an initial file with one line with a DNSKEY
or DS record.
If the file is writable, it is updated when the trust anchor changes.
At this time it is only possible to add trusted keys before the
first resolve is done.
At this time it is only possible to add trusted keys before the first
resolve is done.
.TP
.B ub_ctx_add_ta_file
Add trust anchors to the given context.
Pass name of a file with DS and DNSKEY records in zone file format.
At this time it is only possible to add trusted keys before the
first resolve is done.
At this time it is only possible to add trusted keys before the first
resolve is done.
.TP
.B ub_ctx_trustedkeys
Add trust anchors to the given context.
Pass the name of a bind\-style config file with trusted\-keys{}.
At this time it is only possible to add trusted keys before the
first resolve is done.
Pass the name of a bind\-style config file with \fBtrusted\-keys{}\fP\&.
At this time it is only possible to add trusted keys before the first
resolve is done.
.TP
.B ub_ctx_debugout
Set debug and error log output to the given stream. Pass NULL to disable
output. Default is stderr. File\-names or using syslog can be enabled
using config options, this routine is for using your own stream.
Set debug and error log output to the given stream.
Pass NULL to disable output.
Default is stderr.
File\-names or using syslog can be enabled using config options, this
routine is for using your own stream.
.TP
.B ub_ctx_debuglevel
Set debug verbosity for the context. Output is directed to stderr.
Set debug verbosity for the context.
Output is directed to stderr.
Higher debug level gives more output.
.TP
.B ub_ctx_async
Set a context behaviour for asynchronous action.
if set to true, enables threading and a call to
.B ub_resolve_async
if set to true, enables threading and a call to \fBub_resolve_async\fP
creates a thread to handle work in the background.
If false, a process is forked to handle work in the background.
Changes to this setting after
.B ub_resolve_async
calls have been made have no effect (delete and re\-create the context
to change).
Changes to this setting after \fBub_resolve_async\fP calls have been made
have no effect (delete and re\-create the context to change).
.TP
.B ub_poll
Poll a context to see if it has any new results.
Do not poll in a loop, instead extract the fd below to poll for readiness,
and then check, or wait using the wait routine.
Do not poll in a loop, instead extract the \fBfd\fP below to poll for
readiness, and then check, or wait using the wait routine.
Returns 0 if nothing to read, or nonzero if a result is available.
If nonzero, call
.B ub_process
to do callbacks.
If nonzero, call \fBub_process\fP to do callbacks.
.TP
.B ub_wait
Wait for a context to finish with results. Calls
.B ub_process
after the wait for you. After the wait, there are no more outstanding
asynchronous queries.
Wait for a context to finish with results.
Calls \fBub_process\fP after the wait for you.
After the wait, there are no more outstanding asynchronous queries.
.TP
.B ub_fd
Get file descriptor. Wait for it to become readable, at this point
answers are returned from the asynchronous validating resolver.
Then call the \fBub_process\fR to continue processing.
Get file descriptor.
Wait for it to become readable, at this point answers are returned from
the asynchronous validating resolver.
Then call the \fBub_process\fP to continue processing.
.TP
.B ub_process
Call this routine to continue processing results from the validating
resolver (when the fd becomes readable).
resolver (when the \fBfd\fP becomes readable).
Will perform necessary callbacks.
.TP
.B ub_resolve
@ -340,95 +311,111 @@ The result structure is newly allocated with the resulting data.
.TP
.B ub_resolve_async
Perform asynchronous resolution and validation of the target name.
Arguments mean the same as for \fBub_resolve\fR except no
data is returned immediately, instead a callback is called later.
The callback receives a copy of the mydata pointer, that you can use to pass
information to the callback. The callback type is a function pointer to
a function declared as
.IP
void my_callback_function(void* my_arg, int err,
.br
struct ub_result* result);
.IP
The async_id is returned so you can (at your option) decide to track it
and cancel the request if needed. If you pass a NULL pointer the async_id
is not returned.
Arguments mean the same as for \fBub_resolve\fP except no data is
returned immediately, instead a callback is called later.
The callback receives a copy of the mydata pointer, that you can use to
pass information to the callback.
The callback type is a function pointer to a function declared as:
.INDENT 7.0
.INDENT 3.5
.sp
.nf
.ft C
void my_callback_function(void* my_arg, int err,
struct ub_result* result);
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
The \fBasync_id\fP is returned so you can (at your option) decide to
track it and cancel the request if needed.
If you pass a NULL pointer the \fBasync_id\fP is not returned.
.TP
.B ub_cancel
Cancel an async query in progress. This may return an error if the query
does not exist, or the query is already being delivered, in that case you
may still get a callback for the query.
Cancel an async query in progress.
This may return an error if the query does not exist, or the query is
already being delivered, in that case you may still get a callback for
the query.
.TP
.B ub_resolve_free
Free struct ub_result contents after use.
Free struct \fBub_result\fP contents after use.
.TP
.B ub_strerror
Convert error value from one of the unbound library functions
to a human readable string.
Convert error value from one of the unbound library functions to a
human readable string.
.TP
.B ub_ctx_print_local_zones
Debug printout the local authority information to debug output.
.TP
.B ub_ctx_zone_add
Add new zone to local authority info, like local\-zone \fIunbound.conf\fR(5)
statement.
Add new zone to local authority info, like local\-zone
\fI\%unbound.conf(5)\fP statement.
.TP
.B ub_ctx_zone_remove
Delete zone from local authority info.
.TP
.B ub_ctx_data_add
Add resource record data to local authority info, like local\-data
\fIunbound.conf\fR(5) statement.
\fI\%unbound.conf(5)\fP statement.
.TP
.B ub_ctx_data_remove
Delete local authority data from the name given.
.SH "RESULT DATA STRUCTURE"
The result of the DNS resolution and validation is returned as
\fIstruct ub_result\fR. The result structure contains the following entries.
.P
.UNINDENT
.SH RESULT DATA STRUCTURE
.sp
The result of the DNS resolution and validation is returned as \fIstruct
ub_result\fP\&.
The result structure contains the following entries:
.INDENT 0.0
.INDENT 3.5
.sp
.nf
struct ub_result {
char* qname; /* text string, original question */
int qtype; /* type code asked for */
int qclass; /* class code asked for */
char** data; /* array of rdata items, NULL terminated*/
int* len; /* array with lengths of rdata items */
char* canonname; /* canonical name of result */
int rcode; /* additional error code in case of no data */
void* answer_packet; /* full network format answer packet */
int answer_len; /* length of packet in octets */
int havedata; /* true if there is data */
int nxdomain; /* true if nodata because name does not exist */
int secure; /* true if result is secure */
int bogus; /* true if a security failure happened */
char* why_bogus; /* string with error if bogus */
int was_ratelimited; /* true if the query was ratelimited (SERVFAIL) by unbound */
int ttl; /* number of seconds the result is valid */
};
.ft C
struct ub_result {
char* qname; /* text string, original question */
int qtype; /* type code asked for */
int qclass; /* class code asked for */
char** data; /* array of rdata items, NULL terminated*/
int* len; /* array with lengths of rdata items */
char* canonname; /* canonical name of result */
int rcode; /* additional error code in case of no data */
void* answer_packet; /* full network format answer packet */
int answer_len; /* length of packet in octets */
int havedata; /* true if there is data */
int nxdomain; /* true if nodata because name does not exist */
int secure; /* true if result is secure */
int bogus; /* true if a security failure happened */
char* why_bogus; /* string with error if bogus */
int was_ratelimited; /* true if the query was ratelimited (SERVFAIL) by unbound */
int ttl; /* number of seconds the result is valid */
};
.ft P
.fi
.P
If both secure and bogus are false, security was not enabled for the
domain of the query. Else, they are not both true, one of them is true.
.SH "RETURN VALUES"
Many routines return an error code. The value 0 (zero) denotes no error
happened. Other values can be passed to
.B ub_strerror
to obtain a readable error string.
.B ub_strerror
returns a zero terminated string.
.B ub_ctx_create
returns NULL on an error (a malloc failure).
.B ub_poll
returns true if some information may be available, false otherwise.
.B ub_fd
returns a file descriptor or \-1 on error.
.B ub_ctx_config
and
.B ub_ctx_resolvconf
attempt to leave errno informative on a function return with file read failure.
.SH "SEE ALSO"
\fIunbound.conf\fR(5),
\fIunbound\fR(8).
.SH "AUTHORS"
.B Unbound
developers are mentioned in the CREDITS file in the distribution.
.UNINDENT
.UNINDENT
.sp
If both secure and bogus are false, security was not enabled for the domain of
the query.
Else, they are not both true, one of them is true.
.SH RETURN VALUES
.sp
Many routines return an error code.
The value 0 (zero) denotes no error happened.
Other values can be passed to \fBub_strerror\fP to obtain a readable error
string.
\fBub_strerror\fP returns a zero terminated string.
\fBub_ctx_create\fP returns NULL on an error (a malloc failure).
\fBub_poll\fP returns true if some information may be available, false otherwise.
\fBub_fd\fP returns a file descriptor or \-1 on error.
\fBub_ctx_config\fP and \fBub_ctx_resolvconf\fP attempt to leave errno informative
on a function return with file read failure.
.SH SEE ALSO
.sp
\fI\%unbound.conf(5)\fP, \fI\%unbound(8)\fP\&.
.SH AUTHOR
Unbound developers are mentioned in the CREDITS file in the distribution.
.SH COPYRIGHT
1999-2025, NLnet Labs
.\" Generated by docutils manpage writer.
.

491
doc/libunbound.rst Normal file
View file

@ -0,0 +1,491 @@
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
libunbound(3)
=============
Synopsis
--------
.. only:: html
.. code-block:: c
#include <unbound.h>
struct ub_ctx * ub_ctx_create(void);
void ub_ctx_delete(struct ub_ctx* ctx);
int ub_ctx_set_option(struct ub_ctx* ctx, char* opt, char* val);
int ub_ctx_get_option(struct ub_ctx* ctx, char* opt, char** val);
int ub_ctx_config(struct ub_ctx* ctx, char* fname);
int ub_ctx_set_fwd(struct ub_ctx* ctx, char* addr);
int ub_ctx_set_stub(struct ub_ctx* ctx, char* zone, char* addr,
int isprime);
int ub_ctx_set_tls(struct ub_ctx* ctx, int tls);
int ub_ctx_resolvconf(struct ub_ctx* ctx, char* fname);
int ub_ctx_hosts(struct ub_ctx* ctx, char* fname);
int ub_ctx_add_ta(struct ub_ctx* ctx, char* ta);
int ub_ctx_add_ta_autr(struct ub_ctx* ctx, char* fname);
int ub_ctx_add_ta_file(struct ub_ctx* ctx, char* fname);
int ub_ctx_trustedkeys(struct ub_ctx* ctx, char* fname);
int ub_ctx_debugout(struct ub_ctx* ctx, FILE* out);
int ub_ctx_debuglevel(struct ub_ctx* ctx, int d);
int ub_ctx_async(struct ub_ctx* ctx, int dothread);
int ub_poll(struct ub_ctx* ctx);
int ub_wait(struct ub_ctx* ctx);
int ub_fd(struct ub_ctx* ctx);
int ub_process(struct ub_ctx* ctx);
int ub_resolve(struct ub_ctx* ctx, char* name, int rrtype,
int rrclass, struct ub_result** result);
int ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype,
int rrclass, void* mydata, ub_callback_type callback,
int* async_id);
int ub_cancel(struct ub_ctx* ctx, int async_id);
void ub_resolve_free(struct ub_result* result);
const char * ub_strerror(int err);
int ub_ctx_print_local_zones(struct ub_ctx* ctx);
int ub_ctx_zone_add(struct ub_ctx* ctx, char* zone_name, char* zone_type);
int ub_ctx_zone_remove(struct ub_ctx* ctx, char* zone_name);
int ub_ctx_data_add(struct ub_ctx* ctx, char* data);
int ub_ctx_data_remove(struct ub_ctx* ctx, char* data);
.. only:: man
**#include <unbound.h>**
struct ub_ctx \* **ub_ctx_create**\ (void);
void **ub_ctx_delete**\ (struct ub_ctx\* ctx);
int **ub_ctx_set_option**\ (struct ub_ctx\* ctx, char\* opt, char\* val);
int **ub_ctx_get_option**\ (struct ub_ctx\* ctx, char\* opt, char\*\* val);
int **ub_ctx_config**\ (struct ub_ctx\* ctx, char* fname);
int **ub_ctx_set_fwd**\ (struct ub_ctx\* ctx, char\* addr);
int **ub_ctx_set_stub**\ (struct ub_ctx\* ctx, char\* zone, char\* addr,
int isprime);
int **ub_ctx_set_tls**\ (struct ub_ctx\* ctx, int tls);
int **ub_ctx_resolvconf**\ (struct ub_ctx\* ctx, char\* fname);
int **ub_ctx_hosts**\ (struct ub_ctx\* ctx, char\* fname);
int **ub_ctx_add_ta**\ (struct ub_ctx\* ctx, char\* ta);
int **ub_ctx_add_ta_autr**\ (struct ub_ctx\* ctx, char\* fname);
int **ub_ctx_add_ta_file**\ (struct ub_ctx\* ctx, char\* fname);
int **ub_ctx_trustedkeys**\ (struct ub_ctx\* ctx, char\* fname);
int **ub_ctx_debugout**\ (struct ub_ctx\* ctx, FILE\* out);
int **ub_ctx_debuglevel**\ (struct ub_ctx\* ctx, int d);
int **ub_ctx_async**\ (struct ub_ctx\* ctx, int dothread);
int **ub_poll**\ (struct ub_ctx\* ctx);
int **ub_wait**\ (struct ub_ctx\* ctx);
int **ub_fd**\ (struct ub_ctx\* ctx);
int **ub_process**\ (struct ub_ctx\* ctx);
int **ub_resolve**\ (struct ub_ctx\* ctx, char\* name,
int rrtype, int rrclass, struct ub_result\*\* result);
int **ub_resolve_async**\ (struct ub_ctx\* ctx, char\* name,
int rrtype, int rrclass, void\* mydata,
ub_callback_type\* callback, int\* async_id);
int **ub_cancel**\ (struct ub_ctx\* ctx, int async_id);
void **ub_resolve_free**\ (struct ub_result\* result);
const char \* **ub_strerror**\ (int err);
int **ub_ctx_print_local_zones**\ (struct ub_ctx\* ctx);
int **ub_ctx_zone_add**\ (struct ub_ctx\* ctx, char\* zone_name, char\* zone_type);
int **ub_ctx_zone_remove**\ (struct ub_ctx\* ctx, char\* zone_name);
int **ub_ctx_data_add**\ (struct ub_ctx\* ctx, char\* data);
int **ub_ctx_data_remove**\ (struct ub_ctx\* ctx, char\* data);
Description
-----------
Unbound is an implementation of a DNS resolver, that does caching and DNSSEC
validation.
This is the library API, for using the ``-lunbound`` library.
The server daemon is described in :doc:`unbound(8)</manpages/unbound>`.
The library works independent from a running unbound server, and can be used to
convert hostnames to ip addresses, and back, and obtain other information from
the DNS.
The library performs public-key validation of results with DNSSEC.
The library uses a variable of type *struct ub_ctx* to keep context between
calls.
The user must maintain it, creating it with **ub_ctx_create** and deleting it
with **ub_ctx_delete**.
It can be created and deleted at any time.
Creating it anew removes any previous configuration (such as trusted keys) and
clears any cached results.
The functions are thread-safe, and a context can be used in a threaded (as well
as in a non-threaded) environment.
Also resolution (and validation) can be performed blocking and non-blocking
(also called asynchronous).
The async method returns from the call immediately, so that processing can go
on, while the results become available later.
The functions are discussed in turn below.
Functions
---------
.. glossary::
ub_ctx_create
Create a new context, initialised with defaults.
The information from :file:`/etc/resolv.conf` and :file:`/etc/hosts` is
not utilised by default.
Use **ub_ctx_resolvconf** and **ub_ctx_hosts** to read them.
Before you call this, use the openssl functions
**CRYPTO_set_id_callback** and **CRYPTO_set_locking_callback** to set
up asynchronous operation if you use lib openssl (the application calls
these functions once for initialisation).
Openssl 1.0.0 or later uses the **CRYPTO_THREADID_set_callback**
function.
ub_ctx_delete
Delete validation context and free associated resources.
Outstanding async queries are killed and callbacks are not called for
them.
ub_ctx_set_option
A power-user interface that lets you specify one of the options from
the config file format, see :doc:`unbound.conf(5)</manpages/unbound.conf>`.
Not all options are relevant.
For some specific options, such as adding trust anchors, special
routines exist.
Pass the option name with the trailing ``':'``.
ub_ctx_get_option
A power-user interface that gets an option value.
Some options cannot be gotten, and others return a newline separated
list.
Pass the option name without trailing ``':'``.
The returned value must be free(2)d by the caller.
ub_ctx_config
A power-user interface that lets you specify an unbound config file,
see :doc:`unbound.conf(5)</manpages/unbound.conf>`, which is read for
configuration.
Not all options are relevant.
For some specific options, such as adding trust anchors, special
routines exist.
This function is thread-safe only if a single instance of **ub_ctx**\*
exists in the application.
If several instances exist the application has to ensure that
**ub_ctx_config** is not called in parallel by the different instances.
ub_ctx_set_fwd
Set machine to forward DNS queries to, the caching resolver to use.
IP4 or IP6 address.
Forwards all DNS requests to that machine, which is expected to run a
recursive resolver.
If the proxy is not DNSSEC capable, validation may fail.
Can be called several times, in that case the addresses are used as
backup servers.
At this time it is only possible to set configuration before the first
resolve is done.
ub_ctx_set_stub
Set a stub zone, authoritative dns servers to use for a particular
zone.
IP4 or IP6 address.
If the address is NULL the stub entry is removed.
Set isprime true if you configure root hints with it.
Otherwise similar to the stub zone item from unbound's config file.
Can be called several times, for different zones, or to add multiple
addresses for a particular zone.
At this time it is only possible to set configuration before the first
resolve is done.
ub_ctx_set_tls
Enable DNS over TLS (DoT) for machines set with **ub_ctx_set_fwd**.
At this time it is only possible to set configuration before the first
resolve is done.
ub_ctx_resolvconf
By default the root servers are queried and full resolver mode is used,
but you can use this call to read the list of nameservers to use from
the filename given.
Usually :file:`"/etc/resolv.conf"`.
Uses those nameservers as caching proxies.
If they do not support DNSSEC, validation may fail.
Only nameservers are picked up, the searchdomain, ndots and other
settings from *resolv.conf(5)* are ignored.
If fname NULL is passed, :file:`"/etc/resolv.conf"` is used (if on
Windows, the system-wide configured nameserver is picked instead).
At this time it is only possible to set configuration before the first
resolve is done.
ub_ctx_hosts
Read list of hosts from the filename given.
Usually :file:`"/etc/hosts"`.
When queried for, these addresses are not marked DNSSEC secure.
If fname NULL is passed, :file:`"/etc/hosts"` is used (if on Windows,
:file:`etc/hosts` from WINDIR is picked instead).
At this time it is only possible to set configuration before the first
resolve is done.
ub_ctx_add_ta
Add a trust anchor to the given context.
At this time it is only possible to add trusted keys before the first
resolve is done.
The format is a string, similar to the zone-file format,
**[domainname]** **[type]** **[rdata contents]**.
Both DS and DNSKEY records are accepted.
ub_ctx_add_ta_autr
Add filename with automatically tracked trust anchor to the given
context.
Pass name of a file with the managed trust anchor.
You can create this file with
:doc:`unbound-anchor(8)</manpages/unbound-anchor>` for the root anchor.
You can also create it with an initial file with one line with a DNSKEY
or DS record.
If the file is writable, it is updated when the trust anchor changes.
At this time it is only possible to add trusted keys before the first
resolve is done.
ub_ctx_add_ta_file
Add trust anchors to the given context.
Pass name of a file with DS and DNSKEY records in zone file format.
At this time it is only possible to add trusted keys before the first
resolve is done.
ub_ctx_trustedkeys
Add trust anchors to the given context.
Pass the name of a bind-style config file with ``trusted-keys{}``.
At this time it is only possible to add trusted keys before the first
resolve is done.
ub_ctx_debugout
Set debug and error log output to the given stream.
Pass NULL to disable output.
Default is stderr.
File-names or using syslog can be enabled using config options, this
routine is for using your own stream.
ub_ctx_debuglevel
Set debug verbosity for the context.
Output is directed to stderr.
Higher debug level gives more output.
ub_ctx_async
Set a context behaviour for asynchronous action.
if set to true, enables threading and a call to **ub_resolve_async**
creates a thread to handle work in the background.
If false, a process is forked to handle work in the background.
Changes to this setting after **ub_resolve_async** calls have been made
have no effect (delete and re-create the context to change).
ub_poll
Poll a context to see if it has any new results.
Do not poll in a loop, instead extract the **fd** below to poll for
readiness, and then check, or wait using the wait routine.
Returns 0 if nothing to read, or nonzero if a result is available.
If nonzero, call **ub_process** to do callbacks.
ub_wait
Wait for a context to finish with results.
Calls **ub_process** after the wait for you.
After the wait, there are no more outstanding asynchronous queries.
ub_fd
Get file descriptor.
Wait for it to become readable, at this point answers are returned from
the asynchronous validating resolver.
Then call the **ub_process** to continue processing.
ub_process
Call this routine to continue processing results from the validating
resolver (when the **fd** becomes readable).
Will perform necessary callbacks.
ub_resolve
Perform resolution and validation of the target name.
The name is a domain name in a zero terminated text string.
The rrtype and rrclass are DNS type and class codes.
The result structure is newly allocated with the resulting data.
ub_resolve_async
Perform asynchronous resolution and validation of the target name.
Arguments mean the same as for **ub_resolve** except no data is
returned immediately, instead a callback is called later.
The callback receives a copy of the mydata pointer, that you can use to
pass information to the callback.
The callback type is a function pointer to a function declared as:
.. code-block:: c
void my_callback_function(void* my_arg, int err,
struct ub_result* result);
The **async_id** is returned so you can (at your option) decide to
track it and cancel the request if needed.
If you pass a NULL pointer the **async_id** is not returned.
ub_cancel
Cancel an async query in progress.
This may return an error if the query does not exist, or the query is
already being delivered, in that case you may still get a callback for
the query.
ub_resolve_free
Free struct **ub_result** contents after use.
ub_strerror
Convert error value from one of the unbound library functions to a
human readable string.
ub_ctx_print_local_zones
Debug printout the local authority information to debug output.
ub_ctx_zone_add
Add new zone to local authority info, like local-zone
:doc:`unbound.conf(5)</manpages/unbound.conf>` statement.
ub_ctx_zone_remove
Delete zone from local authority info.
ub_ctx_data_add
Add resource record data to local authority info, like local-data
:doc:`unbound.conf(5)</manpages/unbound.conf>` statement.
ub_ctx_data_remove
Delete local authority data from the name given.
Result Data structure
---------------------
The result of the DNS resolution and validation is returned as *struct
ub_result*.
The result structure contains the following entries:
.. code-block:: c
struct ub_result {
char* qname; /* text string, original question */
int qtype; /* type code asked for */
int qclass; /* class code asked for */
char** data; /* array of rdata items, NULL terminated*/
int* len; /* array with lengths of rdata items */
char* canonname; /* canonical name of result */
int rcode; /* additional error code in case of no data */
void* answer_packet; /* full network format answer packet */
int answer_len; /* length of packet in octets */
int havedata; /* true if there is data */
int nxdomain; /* true if nodata because name does not exist */
int secure; /* true if result is secure */
int bogus; /* true if a security failure happened */
char* why_bogus; /* string with error if bogus */
int was_ratelimited; /* true if the query was ratelimited (SERVFAIL) by unbound */
int ttl; /* number of seconds the result is valid */
};
If both secure and bogus are false, security was not enabled for the domain of
the query.
Else, they are not both true, one of them is true.
Return Values
-------------
Many routines return an error code.
The value 0 (zero) denotes no error happened.
Other values can be passed to **ub_strerror** to obtain a readable error
string.
**ub_strerror** returns a zero terminated string.
**ub_ctx_create** returns NULL on an error (a malloc failure).
**ub_poll** returns true if some information may be available, false otherwise.
**ub_fd** returns a file descriptor or -1 on error.
**ub_ctx_config** and **ub_ctx_resolvconf** attempt to leave errno informative
on a function return with file read failure.
See Also
--------
:doc:`unbound.conf(5)</manpages/unbound.conf>`, :doc:`unbound(8)</manpages/unbound>`.

View file

@ -1,189 +1,300 @@
.TH "unbound-anchor" "8" "@date@" "NLnet Labs" "unbound @version@"
.\"
.\" unbound-anchor.8 -- unbound anchor maintenance utility manual
.\"
.\" Copyright (c) 2008, NLnet Labs. All rights reserved.
.\"
.\" See LICENSE for the license.
.\"
.\"
.SH "NAME"
.B unbound\-anchor
\- Unbound anchor utility.
.SH "SYNOPSIS"
.B unbound\-anchor
.RB [ opts ]
.SH "DESCRIPTION"
.B Unbound\-anchor
performs setup or update of the root trust anchor for DNSSEC validation.
The program fetches the trust anchor with the method from RFC7958 when
regular RFC5011 update fails to bring it up to date.
.\" Man page generated from reStructuredText.
.
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "UNBOUND-ANCHOR" "8" "@date@" "@version@" "Unbound"
.SH NAME
unbound-anchor \- Unbound @version@ anchor utility.
.SH SYNOPSIS
.sp
\fBunbound\-anchor\fP [\fBopts\fP]
.SH DESCRIPTION
.sp
\fBunbound\-anchor\fP performs setup or update of the root trust anchor for DNSSEC
validation.
The program fetches the trust anchor with the method from \fI\%RFC 7958\fP when
regular \fI\%RFC 5011\fP update fails to bring it up to date.
It can be run (as root) from the commandline, or run as part of startup
scripts. Before you start the \fIunbound\fR(8) DNS server.
.P
scripts.
Before you start the \fI\%unbound(8)\fP DNS server.
.sp
Suggested usage:
.P
.INDENT 0.0
.INDENT 3.5
.sp
.nf
# in the init scripts.
# provide or update the root anchor (if necessary)
unbound-anchor \-a "@UNBOUND_ROOTKEY_FILE@"
# Please note usage of this root anchor is at your own risk
# and under the terms of our LICENSE (see source).
#
# start validating resolver
# the unbound.conf contains:
# auto-trust-anchor-file: "@UNBOUND_ROOTKEY_FILE@"
unbound \-c unbound.conf
.ft C
# in the init scripts.
# provide or update the root anchor (if necessary)
unbound\-anchor \-a \(dq@UNBOUND_ROOTKEY_FILE@\(dq
# Please note usage of this root anchor is at your own risk
# and under the terms of our LICENSE (see source).
#
# start validating resolver
# the unbound.conf contains:
# auto\-trust\-anchor\-file: \(dq@UNBOUND_ROOTKEY_FILE@\(dq
unbound \-c unbound.conf
.ft P
.fi
.P
This tool provides builtin default contents for the root anchor and root
update certificate files.
.P
.UNINDENT
.UNINDENT
.sp
This tool provides builtin default contents for the root anchor and root update
certificate files.
.sp
It tests if the root anchor file works, and if not, and an update is possible,
attempts to update the root anchor using the root update certificate.
It performs a https fetch of root-anchors.xml and checks the results (RFC7958),
if all checks are successful, it updates the root anchor file. Otherwise
the root anchor file is unchanged. It performs RFC5011 tracking if the
DNSSEC information available via the DNS makes that possible.
.P
It does not perform an update if the certificate is expired, if the network
is down or other errors occur.
.P
It performs a https fetch of
\fI\%root\-anchors.xml\fP
and checks the results (\fI\%RFC 7958\fP); if all checks are successful, it updates
the root anchor file.
Otherwise the root anchor file is unchanged.
It performs \fI\%RFC 5011\fP tracking if the DNSSEC information available via the
DNS makes that possible.
.sp
It does not perform an update if the certificate is expired, if the network is
down or other errors occur.
.sp
The available options are:
.INDENT 0.0
.TP
.B \-a \fIfile
.B \-a <file>
The root anchor key file, that is read in and written out.
Default is @UNBOUND_ROOTKEY_FILE@.
If the file does not exist, or is empty, a builtin root key is written to it.
Default is \fB@UNBOUND_ROOTKEY_FILE@\fP\&.
If the file does not exist, or is empty, a builtin root key is written
to it.
.UNINDENT
.INDENT 0.0
.TP
.B \-c \fIfile
.B \-c <file>
The root update certificate file, that is read in.
Default is @UNBOUND_ROOTCERT_FILE@.
Default is \fB@UNBOUND_ROOTCERT_FILE@\fP\&.
If the file does not exist, or is empty, a builtin certificate is used.
.UNINDENT
.INDENT 0.0
.TP
.B \-l
List the builtin root key and builtin root update certificate on stdout.
.UNINDENT
.INDENT 0.0
.TP
.B \-u \fIname
The server name, it connects to https://name. Specify without https:// prefix.
The default is "data.iana.org". It connects to the port specified with \-P.
.B \-u <name>
The server name, it connects to \fBhttps://name\fP\&.
Specify without \fBhttps://\fP prefix.
The default is \fB\(dqdata.iana.org\(dq\fP\&.
It connects to the port specified with \fI\%\-P\fP\&.
You can pass an IPv4 address or IPv6 address (no brackets) if you want.
.UNINDENT
.INDENT 0.0
.TP
.B \-S
Do not use SNI for the HTTPS connection. Default is to use SNI.
Do not use SNI for the HTTPS connection.
Default is to use SNI.
.UNINDENT
.INDENT 0.0
.TP
.B \-b \fIaddress
The source address to bind to for domain resolution and contacting the server
on https. May be either an IPv4 address or IPv6 address (no brackets).
.B \-b <address>
The source address to bind to for domain resolution and contacting the
server on https.
May be either an IPv4 address or IPv6 address (no brackets).
.UNINDENT
.INDENT 0.0
.TP
.B \-x \fIpath
The pathname to the root\-anchors.xml file on the server. (forms URL with \-u).
The default is /root\-anchors/root\-anchors.xml.
.B \-x <path>
The pathname to the root\-anchors.xml file on the server.
(forms URL with \fI\%\-u\fP).
The default is \fB/root\-anchors/root\-anchors.xml\fP\&.
.UNINDENT
.INDENT 0.0
.TP
.B \-s \fIpath
The pathname to the root\-anchors.p7s file on the server. (forms URL with \-u).
The default is /root\-anchors/root\-anchors.p7s. This file has to be a PKCS7
signature over the xml file, using the pem file (\-c) as trust anchor.
.B \-s <path>
The pathname to the root\-anchors.p7s file on the server.
(forms URL with \fI\%\-u\fP).
The default is \fB/root\-anchors/root\-anchors.p7s\fP\&.
This file has to be a PKCS7 signature over the xml file, using the pem
file (\fI\%\-c\fP) as trust anchor.
.UNINDENT
.INDENT 0.0
.TP
.B \-n \fIname
The emailAddress for the Subject of the signer's certificate from the p7s
signature file. Only signatures from this name are allowed. default is
dnssec@iana.org. If you pass "" then the emailAddress is not checked.
.B \-n <name>
The emailAddress for the Subject of the signer\(aqs certificate from the
p7s signature file.
Only signatures from this name are allowed.
The default is \fBdnssec@iana.org\fP\&.
If you pass \fB\(dq\(dq\fP then the emailAddress is not checked.
.UNINDENT
.INDENT 0.0
.TP
.B \-4
Use IPv4 for domain resolution and contacting the server on https. Default is
to use IPv4 and IPv6 where appropriate.
Use IPv4 for domain resolution and contacting the server on
https.
Default is to use IPv4 and IPv6 where appropriate.
.UNINDENT
.INDENT 0.0
.TP
.B \-6
Use IPv6 for domain resolution and contacting the server on https. Default is
to use IPv4 and IPv6 where appropriate.
Use IPv6 for domain resolution and contacting the server on https.
Default is to use IPv4 and IPv6 where appropriate.
.UNINDENT
.INDENT 0.0
.TP
.B \-f \fIresolv.conf
Use the given resolv.conf file. Not enabled by default, but you could try to
pass /etc/resolv.conf on some systems. It contains the IP addresses of the
recursive nameservers to use. However, since this tool could be used to
bootstrap that very recursive nameserver, it would not be useful (since
that server is not up yet, since we are bootstrapping it). It could be
useful in a situation where you know an upstream cache is deployed (and
running) and in captive portal situations.
.B \-f <resolv.conf>
Use the given resolv.conf file.
Not enabled by default, but you could try to pass
\fB/etc/resolv.conf\fP on some systems.
It contains the IP addresses of the recursive nameservers to use.
However, since this tool could be used to bootstrap that very recursive
nameserver, it would not be useful (since that server is not up yet,
since we are bootstrapping it).
It could be useful in a situation where you know an upstream cache is
deployed (and running) and in captive portal situations.
.UNINDENT
.INDENT 0.0
.TP
.B \-r \fIroot.hints
Use the given root.hints file (same syntax as the BIND and Unbound root hints
file) to bootstrap domain resolution. By default a list of builtin root
hints is used. Unbound\-anchor goes to the network itself for these roots,
to resolve the server (\-u option) and to check the root DNSKEY records.
.B \-r <root.hints>
Use the given root.hints file (same syntax as the BIND and Unbound root
hints file) to bootstrap domain resolution.
By default a list of builtin root hints is used.
unbound\-anchor goes to the network itself for these roots, to resolve
the server (\fI\%\-u\fP option) and to check the root DNSKEY records.
It does so, because the tool when used for bootstrapping the recursive
resolver, cannot use that recursive resolver itself because it is bootstrapping
that server.
resolver, cannot use that recursive resolver itself because it is
bootstrapping that server.
.UNINDENT
.INDENT 0.0
.TP
.B \-R
Allow fallback from \-f resolv.conf file to direct root servers query.
It allows you to prefer local resolvers, but fallback automatically
to direct root query if they do not respond or do not support DNSSEC.
Allow fallback from \fI\%\-f\fP \fB<resolv.conf>\fP file to direct root
servers query.
It allows you to prefer local resolvers, but fallback automatically to
direct root query if they do not respond or do not support DNSSEC.
.UNINDENT
.INDENT 0.0
.TP
.B \-v
More verbose. Once prints informational messages, multiple times may enable
large debug amounts (such as full certificates or byte\-dumps of downloaded
files). By default it prints almost nothing. It also prints nothing on
errors by default; in that case the original root anchor file is simply
left undisturbed, so that a recursive server can start right after it.
More verbose.
Once prints informational messages, multiple times may enable large
debug amounts (such as full certificates or byte\-dumps of downloaded
files).
By default it prints almost nothing.
It also prints nothing on errors by default; in that case the original
root anchor file is simply left undisturbed, so that a recursive server
can start right after it.
.UNINDENT
.INDENT 0.0
.TP
.B \-C \fIunbound.conf
Debug option to read unbound.conf into the resolver process used.
.B \-C <unbound.conf>
Debug option to read \fB<unbound.conf>\fP into the resolver process
used.
.UNINDENT
.INDENT 0.0
.TP
.B \-P \fIport
Set the port number to use for the https connection. The default is 443.
.B \-P <port>
Set the port number to use for the https connection.
The default is 443.
.UNINDENT
.INDENT 0.0
.TP
.B \-F
Debug option to force update of the root anchor through downloading the xml
file and verifying it with the certificate. By default it first tries to
update by contacting the DNS, which uses much less bandwidth, is much
faster (200 msec not 2 sec), and is nicer to the deployed infrastructure.
With this option, it still attempts to do so (and may verbosely tell you),
but then ignores the result and goes on to use the xml fallback method.
Debug option to force update of the root anchor through downloading the
xml file and verifying it with the certificate.
By default it first tries to update by contacting the DNS, which uses
much less bandwidth, is much faster (200 msec not 2 sec), and is nicer
to the deployed infrastructure.
With this option, it still attempts to do so (and may verbosely tell
you), but then ignores the result and goes on to use the xml fallback
method.
.UNINDENT
.INDENT 0.0
.TP
.B \-h
Show the version and commandline option help.
.SH "EXIT CODE"
.UNINDENT
.SH EXIT CODE
.sp
This tool exits with value 1 if the root anchor was updated using the
certificate or if the builtin root-anchor was used. It exits with code
0 if no update was necessary, if the update was possible with RFC5011
tracking, or if an error occurred.
.P
certificate or if the builtin root\-anchor was used.
It exits with code 0 if no update was necessary, if the update was possible
with \fI\%RFC 5011\fP tracking, or if an error occurred.
.sp
You can check the exit value in this manner:
.INDENT 0.0
.INDENT 3.5
.sp
.nf
unbound-anchor \-a "root.key" || logger "Please check root.key"
.ft C
unbound\-anchor \-a \(dqroot.key\(dq || logger \(dqPlease check root.key\(dq
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
Or something more suitable for your operational environment.
.SH "TRUST"
The root keys and update certificate included in this tool
are provided for convenience and under the terms of our
license (see the LICENSE file in the source distribution or
https://github.com/NLnetLabs/unbound/blob/master/LICENSE) and might be stale or
not suitable to your purpose.
.P
By running "unbound\-anchor \-l" the keys and certificate that are
.SH TRUST
.sp
The root keys and update certificate included in this tool are provided for
convenience and under the terms of our license (see the LICENSE file in the
source distribution or \fI\%https://github.com/NLnetLabs/unbound/blob/master/LICENSE\fP
and might be stale or not suitable to your purpose.
.sp
By running \fI\%unbound\-anchor \-l\fP the keys and certificate that are
configured in the code are printed for your convenience.
.P
The build\-in configuration can be overridden by providing a root\-cert
file and a rootkey file.
.SH "FILES"
.sp
The built\-in configuration can be overridden by providing a root\-cert file and
a rootkey file.
.SH FILES
.INDENT 0.0
.TP
.I @UNBOUND_ROOTKEY_FILE@
The root anchor file, updated with 5011 tracking, and read and written to.
.B @UNBOUND_ROOTKEY_FILE@
The root anchor file, updated with 5011 tracking, and read and written
to.
The file is created if it does not exist.
.TP
.I @UNBOUND_ROOTCERT_FILE@
The trusted self\-signed certificate that is used to verify the downloaded
DNSSEC root trust anchor. You can update it by fetching it from
https://data.iana.org/root\-anchors/icannbundle.pem (and validate it).
.B @UNBOUND_ROOTCERT_FILE@
The trusted self\-signed certificate that is used to verify the
downloaded DNSSEC root trust anchor.
You can update it by fetching it from
\fI\%https://data.iana.org/root\-anchors/icannbundle.pem\fP (and validate it).
If the file does not exist or is empty, a builtin version is used.
.TP
.I https://data.iana.org/root\-anchors/root\-anchors.xml
.B \fI\%https://data.iana.org/root\-anchors/root\-anchors.xml\fP
Source for the root key information.
.TP
.I https://data.iana.org/root\-anchors/root\-anchors.p7s
.B \fI\%https://data.iana.org/root\-anchors/root\-anchors.p7s\fP
Signature on the root key information.
.SH "SEE ALSO"
\fIunbound.conf\fR(5),
\fIunbound\fR(8).
.UNINDENT
.SH SEE ALSO
.sp
\fI\%unbound.conf(5)\fP,
\fI\%unbound(8)\fP\&.
.SH AUTHOR
Unbound developers are mentioned in the CREDITS file in the distribution.
.SH COPYRIGHT
1999-2025, NLnet Labs
.\" Generated by docutils manpage writer.
.

281
doc/unbound-anchor.rst Normal file
View file

@ -0,0 +1,281 @@
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
.. program:: unbound-anchor
unbound-anchor(8)
=================
Synopsis
--------
**unbound-anchor** [``opts``]
Description
-----------
``unbound-anchor`` performs setup or update of the root trust anchor for DNSSEC
validation.
The program fetches the trust anchor with the method from :rfc:`7958` when
regular :rfc:`5011` update fails to bring it up to date.
It can be run (as root) from the commandline, or run as part of startup
scripts.
Before you start the :doc:`unbound(8)</manpages/unbound>` DNS server.
Suggested usage:
.. code-block:: text
# in the init scripts.
# provide or update the root anchor (if necessary)
unbound-anchor -a "@UNBOUND_ROOTKEY_FILE@"
# Please note usage of this root anchor is at your own risk
# and under the terms of our LICENSE (see source).
#
# start validating resolver
# the unbound.conf contains:
# auto-trust-anchor-file: "@UNBOUND_ROOTKEY_FILE@"
unbound -c unbound.conf
This tool provides builtin default contents for the root anchor and root update
certificate files.
It tests if the root anchor file works, and if not, and an update is possible,
attempts to update the root anchor using the root update certificate.
It performs a https fetch of
`root-anchors.xml <http://data.iana.org/root-anchors/root-anchors.xml>`__
and checks the results (:rfc:`7958`); if all checks are successful, it updates
the root anchor file.
Otherwise the root anchor file is unchanged.
It performs :rfc:`5011` tracking if the DNSSEC information available via the
DNS makes that possible.
It does not perform an update if the certificate is expired, if the network is
down or other errors occur.
The available options are:
.. option:: -a <file>
The root anchor key file, that is read in and written out.
Default is :file:`@UNBOUND_ROOTKEY_FILE@`.
If the file does not exist, or is empty, a builtin root key is written
to it.
.. option:: -c <file>
The root update certificate file, that is read in.
Default is :file:`@UNBOUND_ROOTCERT_FILE@`.
If the file does not exist, or is empty, a builtin certificate is used.
.. option:: -l
List the builtin root key and builtin root update certificate on stdout.
.. option:: -u <name>
The server name, it connects to ``https://name``.
Specify without ``https://`` prefix.
The default is ``"data.iana.org"``.
It connects to the port specified with :option:`-P`.
You can pass an IPv4 address or IPv6 address (no brackets) if you want.
.. option:: -S
Do not use SNI for the HTTPS connection.
Default is to use SNI.
.. option:: -b <address>
The source address to bind to for domain resolution and contacting the
server on https.
May be either an IPv4 address or IPv6 address (no brackets).
.. option:: -x <path>
The pathname to the root-anchors.xml file on the server.
(forms URL with :option:`-u`).
The default is :file:`/root-anchors/root-anchors.xml`.
.. option:: -s <path>
The pathname to the root-anchors.p7s file on the server.
(forms URL with :option:`-u`).
The default is :file:`/root-anchors/root-anchors.p7s`.
This file has to be a PKCS7 signature over the xml file, using the pem
file (:option:`-c`) as trust anchor.
.. option:: -n <name>
The emailAddress for the Subject of the signer's certificate from the
p7s signature file.
Only signatures from this name are allowed.
The default is ``dnssec@iana.org``.
If you pass ``""`` then the emailAddress is not checked.
.. option:: -4
Use IPv4 for domain resolution and contacting the server on
https.
Default is to use IPv4 and IPv6 where appropriate.
.. option:: -6
Use IPv6 for domain resolution and contacting the server on https.
Default is to use IPv4 and IPv6 where appropriate.
.. option:: -f <resolv.conf>
Use the given resolv.conf file.
Not enabled by default, but you could try to pass
:file:`/etc/resolv.conf` on some systems.
It contains the IP addresses of the recursive nameservers to use.
However, since this tool could be used to bootstrap that very recursive
nameserver, it would not be useful (since that server is not up yet,
since we are bootstrapping it).
It could be useful in a situation where you know an upstream cache is
deployed (and running) and in captive portal situations.
.. option:: -r <root.hints>
Use the given root.hints file (same syntax as the BIND and Unbound root
hints file) to bootstrap domain resolution.
By default a list of builtin root hints is used.
unbound-anchor goes to the network itself for these roots, to resolve
the server (:option:`-u` option) and to check the root DNSKEY records.
It does so, because the tool when used for bootstrapping the recursive
resolver, cannot use that recursive resolver itself because it is
bootstrapping that server.
.. option:: -R
Allow fallback from :option:`-f` ``<resolv.conf>`` file to direct root
servers query.
It allows you to prefer local resolvers, but fallback automatically to
direct root query if they do not respond or do not support DNSSEC.
.. option:: -v
More verbose.
Once prints informational messages, multiple times may enable large
debug amounts (such as full certificates or byte-dumps of downloaded
files).
By default it prints almost nothing.
It also prints nothing on errors by default; in that case the original
root anchor file is simply left undisturbed, so that a recursive server
can start right after it.
.. option:: -C <unbound.conf>
Debug option to read :file:`<unbound.conf>` into the resolver process
used.
.. option:: -P <port>
Set the port number to use for the https connection.
The default is 443.
.. option:: -F
Debug option to force update of the root anchor through downloading the
xml file and verifying it with the certificate.
By default it first tries to update by contacting the DNS, which uses
much less bandwidth, is much faster (200 msec not 2 sec), and is nicer
to the deployed infrastructure.
With this option, it still attempts to do so (and may verbosely tell
you), but then ignores the result and goes on to use the xml fallback
method.
.. option:: -h
Show the version and commandline option help.
Exit Code
---------
This tool exits with value 1 if the root anchor was updated using the
certificate or if the builtin root-anchor was used.
It exits with code 0 if no update was necessary, if the update was possible
with :rfc:`5011` tracking, or if an error occurred.
You can check the exit value in this manner:
.. code-block:: text
unbound-anchor -a "root.key" || logger "Please check root.key"
Or something more suitable for your operational environment.
Trust
-----
The root keys and update certificate included in this tool are provided for
convenience and under the terms of our license (see the LICENSE file in the
source distribution or https://github.com/NLnetLabs/unbound/blob/master/LICENSE
and might be stale or not suitable to your purpose.
By running :option:`unbound-anchor -l` the keys and certificate that are
configured in the code are printed for your convenience.
The built-in configuration can be overridden by providing a root-cert file and
a rootkey file.
Files
-----
@UNBOUND_ROOTKEY_FILE@
The root anchor file, updated with 5011 tracking, and read and written
to.
The file is created if it does not exist.
@UNBOUND_ROOTCERT_FILE@
The trusted self-signed certificate that is used to verify the
downloaded DNSSEC root trust anchor.
You can update it by fetching it from
https://data.iana.org/root-anchors/icannbundle.pem (and validate it).
If the file does not exist or is empty, a builtin version is used.
https://data.iana.org/root-anchors/root-anchors.xml
Source for the root key information.
https://data.iana.org/root-anchors/root-anchors.p7s
Signature on the root key information.
See Also
--------
:doc:`unbound.conf(5)</manpages/unbound.conf>`,
:doc:`unbound(8)</manpages/unbound>`.

View file

@ -1,56 +1,93 @@
.TH "unbound-checkconf" "8" "@date@" "NLnet Labs" "unbound @version@"
.\"
.\" unbound-checkconf.8 -- unbound configuration checker manual
.\"
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
.\"
.\" See LICENSE for the license.
.\"
.\"
.SH "NAME"
unbound\-checkconf
\- Check Unbound configuration file for errors.
.SH "SYNOPSIS"
.B unbound\-checkconf
.RB [ \-h ]
.RB [ \-f ]
.RB [ \-q ]
.RB [ \-o
.IR option ]
.RI [ cfgfile ]
.SH "DESCRIPTION"
.B Unbound\-checkconf
checks the configuration file for the
\fIunbound\fR(8)
DNS resolver for syntax and other errors.
.\" Man page generated from reStructuredText.
.
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "UNBOUND-CHECKCONF" "8" "@date@" "@version@" "Unbound"
.SH NAME
unbound-checkconf \- Check Unbound @version@ configuration file for errors.
.SH SYNOPSIS
.sp
\fBunbound\-checkconf\fP [\fB\-hf\fP] [\fB\-o option\fP] [cfgfile]
.SH DESCRIPTION
.sp
\fBunbound\-checkconf\fP checks the configuration file for the
\fI\%unbound(8)\fP DNS resolver for syntax and other errors.
The config file syntax is described in
\fIunbound.conf\fR(5).
.P
\fI\%unbound.conf(5)\fP\&.
.sp
The available options are:
.INDENT 0.0
.TP
.B \-h
Show the version and commandline option help.
.UNINDENT
.INDENT 0.0
.TP
.B \-f
Print full pathname, with chroot applied to it. Use with the \-o option.
.TP
.B \-o\fI option
If given, after checking the config file the value of this option is
printed to stdout. For "" (disabled) options an empty line is printed.
Print full pathname, with chroot applied to it.
Use with the \fI\%\-o\fP option.
.UNINDENT
.INDENT 0.0
.TP
.B \-q
Make the operation quiet, suppress output on success.
.UNINDENT
.INDENT 0.0
.TP
.I cfgfile
The config file to read with settings for Unbound. It is checked.
.B \-o <option>
If given, after checking the config file the value of this option is
printed to stdout.
For \fB\(dq\(dq\fP (disabled) options an empty line is printed.
.UNINDENT
.INDENT 0.0
.TP
.B cfgfile
The config file to read with settings for Unbound.
It is checked.
If omitted, the config file at the default location is checked.
.SH "EXIT CODE"
The unbound\-checkconf program exits with status code 1 on error,
0 for a correct config file.
.SH "FILES"
.UNINDENT
.SH EXIT CODE
.sp
The \fBunbound\-checkconf\fP program exits with status code 1 on error, 0 for a
correct config file.
.SH FILES
.INDENT 0.0
.TP
.I @ub_conf_file@
.B @ub_conf_file@
Unbound configuration file.
.SH "SEE ALSO"
\fIunbound.conf\fR(5),
\fIunbound\fR(8).
.UNINDENT
.SH SEE ALSO
.sp
\fI\%unbound.conf(5)\fP,
\fI\%unbound(8)\fP\&.
.SH AUTHOR
Unbound developers are mentioned in the CREDITS file in the distribution.
.SH COPYRIGHT
1999-2025, NLnet Labs
.\" Generated by docutils manpage writer.
.

98
doc/unbound-checkconf.rst Normal file
View file

@ -0,0 +1,98 @@
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
.. program:: unbound-checkconf
unbound-checkconf(8)
====================
Synopsis
--------
**unbound-checkconf** [``-hf``] [``-o option``] [cfgfile]
Description
-----------
``unbound-checkconf`` checks the configuration file for the
:doc:`unbound(8)</manpages/unbound>` DNS resolver for syntax and other errors.
The config file syntax is described in
:doc:`unbound.conf(5)</manpages/unbound.conf>`.
The available options are:
.. option:: -h
Show the version and commandline option help.
.. option:: -f
Print full pathname, with chroot applied to it.
Use with the :option:`-o` option.
.. option:: -q
Make the operation quiet, suppress output on success.
.. option:: -o <option>
If given, after checking the config file the value of this option is
printed to stdout.
For ``""`` (disabled) options an empty line is printed.
.. option:: cfgfile
The config file to read with settings for Unbound.
It is checked.
If omitted, the config file at the default location is checked.
Exit Code
---------
The ``unbound-checkconf`` program exits with status code 1 on error, 0 for a
correct config file.
Files
-----
@ub_conf_file@
Unbound configuration file.
See Also
--------
:doc:`unbound.conf(5)</manpages/unbound.conf>`,
:doc:`unbound(8)</manpages/unbound>`.

File diff suppressed because it is too large Load diff

1374
doc/unbound-control.rst Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,118 +1,190 @@
.TH "unbound\-host" "1" "@date@" "NLnet Labs" "unbound @version@"
.\"
.\" unbound-host.1 -- unbound DNS lookup utility
.\"
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
.\"
.\" See LICENSE for the license.
.\"
.\"
.SH "NAME"
.B unbound\-host
\- unbound DNS lookup utility
.SH "SYNOPSIS"
.B unbound\-host
.RB [ \-C
.IR configfile ]
.RB [ \-vdhr46D ]
.RB [ \-c
.IR class ]
.RB [ \-t
.IR type ]
.RB [ \-y
.IR key ]
.RB [ \-f
.IR keyfile ]
.RB [ \-F
.IR namedkeyfile ]
.I hostname
.SH "DESCRIPTION"
.B Unbound\-host
uses the Unbound validating resolver to query for the hostname and display
results. With the \fB\-v\fR option it displays validation
status: secure, insecure, bogus (security failure).
.P
By default it reads no configuration file whatsoever. It attempts to reach
the internet root servers. With \fB\-C\fR an Unbound config file and with
\fB\-r\fR resolv.conf can be read.
.P
.\" Man page generated from reStructuredText.
.
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "UNBOUND-HOST" "1" "@date@" "@version@" "Unbound"
.SH NAME
unbound-host \- Unbound @version@ DNS lookup utility.
.SH SYNOPSIS
.sp
\fBunbound\-host\fP [\fB\-C configfile\fP] [\fB\-vdhr46D\fP] [\fB\-c class\fP]
[\fB\-t type\fP] [\fB\-y key\fP] [\fB\-f keyfile\fP] [\fB\-F namedkeyfile\fP] hostname
.SH DESCRIPTION
.sp
\fBunbound\-host\fP uses the Unbound validating resolver to query for the hostname
and display results.
With the \fI\%\-v\fP option it displays validation status: secure, insecure,
bogus (security failure).
.sp
By default it reads no configuration file whatsoever.
It attempts to reach the internet root servers.
With \fI\%\-C\fP an unbound config file and with \fI\%\-r\fP \fBresolv.conf\fP
can be read.
.sp
The available options are:
.INDENT 0.0
.TP
.I hostname
.B hostname
This name is resolved (looked up in the DNS).
If a IPv4 or IPv6 address is given, a reverse lookup is performed.
.UNINDENT
.INDENT 0.0
.TP
.B \-h
Show the version and commandline option help.
.UNINDENT
.INDENT 0.0
.TP
.B \-v
Enable verbose output and it shows validation results, on every line.
Secure means that the NXDOMAIN (no such domain name), nodata (no such data)
or positive data response validated correctly with one of the keys.
Secure means that the NXDOMAIN (no such domain name), nodata (no such
data) or positive data response validated correctly with one of the
keys.
Insecure means that that domain name has no security set up for it.
Bogus (security failure) means that the response failed one or more checks,
it is likely wrong, outdated, tampered with, or broken.
Bogus (security failure) means that the response failed one or more
checks, it is likely wrong, outdated, tampered with, or broken.
.UNINDENT
.INDENT 0.0
.TP
.B \-d
Enable debug output to stderr. One \-d shows what the resolver and validator
are doing and may tell you what is going on. More times, \-d \-d, gives a
lot of output, with every packet sent and received.
Enable debug output to stderr.
One \fI\%\-d\fP shows what the resolver and validator are doing and may
tell you what is going on.
More times, \fI\%\-d\fP \fI\%\-d\fP, gives a lot of output, with every
packet sent and received.
.UNINDENT
.INDENT 0.0
.TP
.B \-c \fIclass
Specify the class to lookup for, the default is IN the internet class.
.B \-c <class>
Specify the class to lookup for, the default is IN the internet
class.
.UNINDENT
.INDENT 0.0
.TP
.B \-t \fItype
Specify the type of data to lookup. The default looks for IPv4, IPv6 and
mail handler data, or domain name pointers for reverse queries.
.B \-t <type>
Specify the type of data to lookup.
The default looks for IPv4, IPv6 and mail handler data, or domain name
pointers for reverse queries.
.UNINDENT
.INDENT 0.0
.TP
.B \-y \fIkey
Specify a public key to use as trust anchor. This is the base for a chain
of trust that is built up from the trust anchor to the response, in order
to validate the response message. Can be given as a DS or DNSKEY record.
For example \-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD".
.B \-y <key>
Specify a public key to use as trust anchor.
This is the base for a chain of trust that is built up from the trust
anchor to the response, in order to validate the response message.
Can be given as a DS or DNSKEY record.
For example:
.INDENT 7.0
.INDENT 3.5
.sp
.nf
.ft C
\-y \(dqexample.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD\(dq
.ft P
.fi
.UNINDENT
.UNINDENT
.UNINDENT
.INDENT 0.0
.TP
.B \-D
Enables DNSSEC validation. Reads the root anchor from the default configured
root anchor at the default location, \fI@UNBOUND_ROOTKEY_FILE@\fR.
Enables DNSSEC validation.
Reads the root anchor from the default configured root anchor at the
default location, \fB@UNBOUND_ROOTKEY_FILE@\fP\&.
.UNINDENT
.INDENT 0.0
.TP
.B \-f \fIkeyfile
Reads keys from a file. Every line has a DS or DNSKEY record, in the format
as for \-y. The zone file format, the same as dig and drill produce.
.B \-f <keyfile>
Reads keys from a file.
Every line has a DS or DNSKEY record, in the format as for \fI\%\-y\fP\&.
The zone file format, the same as \fBdig\fP and \fBdrill\fP produce.
.UNINDENT
.INDENT 0.0
.TP
.B \-F \fInamedkeyfile
Reads keys from a BIND\-style named.conf file. Only the trusted\-key {}; entries
are read.
.B \-F <namedkeyfile>
Reads keys from a BIND\-style \fBnamed.conf\fP file.
Only the \fBtrusted\-key {};\fP entries are read.
.UNINDENT
.INDENT 0.0
.TP
.B \-C \fIconfigfile
Uses the specified unbound.conf to prime
.IR libunbound (3).
.B \-C <configfile>
Uses the specified unbound.conf to prime \fI\%libunbound(3)\fP\&.
Pass it as first argument if you want to override some options from the
config file with further arguments on the commandline.
.UNINDENT
.INDENT 0.0
.TP
.B \-r
Read /etc/resolv.conf, and use the forward DNS servers from there (those could
have been set by DHCP). More info in
.IR resolv.conf (5).
Read \fB/etc/resolv.conf\fP, and use the forward DNS servers from
there (those could have been set by DHCP).
More info in \fIresolv.conf(5)\fP\&.
Breaks validation if those servers do not support DNSSEC.
.UNINDENT
.INDENT 0.0
.TP
.B \-4
Use solely the IPv4 network for sending packets.
.UNINDENT
.INDENT 0.0
.TP
.B \-6
Use solely the IPv6 network for sending packets.
.SH "EXAMPLES"
Some examples of use. The keys shown below are fakes, thus a security failure
is encountered.
.P
.UNINDENT
.SH EXAMPLES
.sp
Some examples of use.
The keys shown below are fakes, thus a security failure is encountered.
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
$ unbound\-host www.example.com
.P
$ unbound\-host \-v \-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD" www.example.com
.P
$ unbound\-host \-v \-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD" 192.0.2.153
.SH "EXIT CODE"
The unbound\-host program exits with status code 1 on error,
0 on no error. The data may not be available on exit code 0, exit code 1
means the lookup encountered a fatal error.
.SH "SEE ALSO"
\fIunbound.conf\fR(5),
\fIunbound\fR(8).
$ unbound\-host \-v \-y \(dqexample.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD\(dq www.example.com
$ unbound\-host \-v \-y \(dqexample.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD\(dq 192.0.2.153
.ft P
.fi
.UNINDENT
.UNINDENT
.SH EXIT CODE
.sp
The \fBunbound\-host\fP program exits with status code 1 on error, 0 on no error.
The data may not be available on exit code 0, exit code 1 means the lookup
encountered a fatal error.
.SH SEE ALSO
.sp
\fI\%unbound.conf(5)\fP,
\fI\%unbound(8)\fP\&.
.SH AUTHOR
Unbound developers are mentioned in the CREDITS file in the distribution.
.SH COPYRIGHT
1999-2025, NLnet Labs
.\" Generated by docutils manpage writer.
.

176
doc/unbound-host.rst Normal file
View file

@ -0,0 +1,176 @@
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
.. program:: unbound-host
unbound-host(1)
===============
Synopsis
--------
**unbound-host** [``-C configfile``] [``-vdhr46D``] [``-c class``]
[``-t type``] [``-y key``] [``-f keyfile``] [``-F namedkeyfile``] hostname
Description
-----------
``unbound-host`` uses the Unbound validating resolver to query for the hostname
and display results.
With the :option:`-v` option it displays validation status: secure, insecure,
bogus (security failure).
By default it reads no configuration file whatsoever.
It attempts to reach the internet root servers.
With :option:`-C` an unbound config file and with :option:`-r` ``resolv.conf``
can be read.
The available options are:
.. option:: hostname
This name is resolved (looked up in the DNS).
If a IPv4 or IPv6 address is given, a reverse lookup is performed.
.. option:: -h
Show the version and commandline option help.
.. option:: -v
Enable verbose output and it shows validation results, on every line.
Secure means that the NXDOMAIN (no such domain name), nodata (no such
data) or positive data response validated correctly with one of the
keys.
Insecure means that that domain name has no security set up for it.
Bogus (security failure) means that the response failed one or more
checks, it is likely wrong, outdated, tampered with, or broken.
.. option:: -d
Enable debug output to stderr.
One :option:`-d` shows what the resolver and validator are doing and may
tell you what is going on.
More times, :option:`-d` :option:`-d`, gives a lot of output, with every
packet sent and received.
.. option:: -c <class>
Specify the class to lookup for, the default is IN the internet
class.
.. option:: -t <type>
Specify the type of data to lookup.
The default looks for IPv4, IPv6 and mail handler data, or domain name
pointers for reverse queries.
.. option:: -y <key>
Specify a public key to use as trust anchor.
This is the base for a chain of trust that is built up from the trust
anchor to the response, in order to validate the response message.
Can be given as a DS or DNSKEY record.
For example:
.. code-block:: text
-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD"
.. option:: -D
Enables DNSSEC validation.
Reads the root anchor from the default configured root anchor at the
default location, :file:`@UNBOUND_ROOTKEY_FILE@`.
.. option:: -f <keyfile>
Reads keys from a file.
Every line has a DS or DNSKEY record, in the format as for :option:`-y`.
The zone file format, the same as ``dig`` and ``drill`` produce.
.. option:: -F <namedkeyfile>
Reads keys from a BIND-style :file:`named.conf` file.
Only the ``trusted-key {};`` entries are read.
.. option:: -C <configfile>
Uses the specified unbound.conf to prime :doc:`libunbound(3)</manpages/libunbound>`.
Pass it as first argument if you want to override some options from the
config file with further arguments on the commandline.
.. option:: -r
Read :file:`/etc/resolv.conf`, and use the forward DNS servers from
there (those could have been set by DHCP).
More info in *resolv.conf(5)*.
Breaks validation if those servers do not support DNSSEC.
.. option:: -4
Use solely the IPv4 network for sending packets.
.. option:: -6
Use solely the IPv6 network for sending packets.
Examples
--------
Some examples of use.
The keys shown below are fakes, thus a security failure is encountered.
.. code-block:: text
$ unbound-host www.example.com
$ unbound-host -v -y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD" www.example.com
$ unbound-host -v -y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD" 192.0.2.153
Exit Code
---------
The ``unbound-host`` program exits with status code 1 on error, 0 on no error.
The data may not be available on exit code 0, exit code 1 means the lookup
encountered a fatal error.
See Also
--------
:doc:`unbound.conf(5)</manpages/unbound.conf>`,
:doc:`unbound(8)</manpages/unbound>`.

View file

@ -1,88 +1,123 @@
.TH "unbound" "8" "@date@" "NLnet Labs" "unbound @version@"
.\"
.\" unbound.8 -- unbound manual
.\"
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
.\"
.\" See LICENSE for the license.
.\"
.\"
.SH "NAME"
.B unbound
\- Unbound DNS validating resolver @version@.
.SH "SYNOPSIS"
.B unbound
.RB [ \-h ]
.RB [ \-d ]
.RB [ \-p ]
.RB [ \-v ]
.RB [ \-c
.IR cfgfile ]
.SH "DESCRIPTION"
.B Unbound
is a caching DNS resolver.
.P
It uses a built in list of authoritative nameservers for the root zone (.),
.\" Man page generated from reStructuredText.
.
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "UNBOUND" "8" "@date@" "@version@" "Unbound"
.SH NAME
unbound \- Unbound DNS validating resolver @version@.
.SH SYNOPSIS
.sp
\fBunbound\fP [\fB\-hdpv\fP] [\fB\-c <cfgfile>\fP]
.SH DESCRIPTION
.sp
\fBunbound\fP is a caching DNS resolver.
.sp
It uses a built in list of authoritative nameservers for the root zone (\fB\&.\fP),
the so called root hints.
On receiving a DNS query it will ask the root nameservers for
an answer and will in almost all cases receive a delegation to a top level
domain (TLD) authoritative nameserver.
On receiving a DNS query it will ask the root nameservers for an answer and
will in almost all cases receive a delegation to a top level domain (TLD)
authoritative nameserver.
It will then ask that nameserver for an answer.
It will recursively continue until an answer is found or no answer is
available (NXDOMAIN).
For performance and efficiency reasons that answer is cached for a
certain time (the answer's time\-to\-live or TTL).
It will recursively continue until an answer is found or no answer is available
(NXDOMAIN).
For performance and efficiency reasons that answer is cached for a certain time
(the answer\(aqs time\-to\-live or TTL).
A second query for the same name will then be answered from the cache.
Unbound can also do DNSSEC validation.
.P
To use a locally running
.B Unbound
for resolving put
.sp
.RS 6n
To use a locally running Unbound for resolving put:
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
nameserver 127.0.0.1
.RE
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
into \fIresolv.conf(5)\fP\&.
.sp
If authoritative DNS is needed as well using \fI\%nsd(8)\fP,
careful setup is required because authoritative nameservers and resolvers are
using the same port number (53).
.sp
into
.IR resolv.conf (5).
.P
If authoritative DNS is needed as well using
.IR nsd (8),
careful setup is required because authoritative nameservers and
resolvers are using the same port number (53).
.P
The available options are:
.INDENT 0.0
.TP
.B \-h
Show the version number and commandline option help, and exit.
.UNINDENT
.INDENT 0.0
.TP
.B \-c\fI cfgfile
Set the config file with settings for Unbound to read instead of reading the
file at the default location, @ub_conf_file@. The syntax is
described in \fIunbound.conf\fR(5).
.B \-c <cfgfile>
Set the config file with settings for unbound to read instead of reading the
file at the default location, \fB@ub_conf_file@\fP\&.
The syntax is described in \fI\%unbound.conf(5)\fP\&.
.UNINDENT
.INDENT 0.0
.TP
.B \-d
Debug flag: do not fork into the background, but stay attached to
the console. This flag will also delay writing to the log file until
the thread\-spawn time, so that most config and setup errors appear on
stderr. If given twice or more, logging does not switch to the log file
or to syslog, but the log messages are printed to stderr all the time.
Debug flag: do not fork into the background, but stay attached to the
console.
This flag will also delay writing to the log file until the thread\-spawn
time, so that most config and setup errors appear on stderr.
If given twice or more, logging does not switch to the log file or to
syslog, but the log messages are printed to stderr all the time.
.UNINDENT
.INDENT 0.0
.TP
.B \-p
Don't use a pidfile. This argument should only be used by supervision
systems which can ensure that only one instance of Unbound will run
concurrently.
Don\(aqt use a pidfile.
This argument should only be used by supervision systems which can ensure
that only one instance of Unbound will run concurrently.
.UNINDENT
.INDENT 0.0
.TP
.B \-v
Increase verbosity. If given multiple times, more information is logged.
This is added to the verbosity (if any) from the config file.
Increase verbosity.
If given multiple times, more information is logged.
This is in addition to the verbosity (if any) from the config file.
.UNINDENT
.INDENT 0.0
.TP
.B \-V
Show the version number and build options, and exit.
.SH "SEE ALSO"
\fIunbound.conf\fR(5),
\fIunbound\-checkconf\fR(8),
\fInsd\fR(8).
.SH "AUTHORS"
.B Unbound
developers are mentioned in the CREDITS file in the distribution.
.UNINDENT
.SH SEE ALSO
.sp
\fI\%unbound.conf(5)\fP,
\fI\%unbound\-checkconf(8)\fP,
\fI\%nsd(8)\fP\&.
.SH AUTHOR
Unbound developers are mentioned in the CREDITS file in the distribution.
.SH COPYRIGHT
1999-2025, NLnet Labs
.\" Generated by docutils manpage writer.
.

File diff suppressed because it is too large Load diff

4997
doc/unbound.conf.rst Normal file

File diff suppressed because it is too large Load diff

119
doc/unbound.rst Normal file
View file

@ -0,0 +1,119 @@
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
..
WHEN EDITING MAKE SURE EACH SENTENCE STARTS ON A NEW LINE
..
IT HELPS RENDERERS TO DO THE RIGHT THING WRT SPACE
..
IT HELPS PEOPLE DIFFING THE CHANGES
.. program:: unbound
unbound(8)
==========
Synopsis
--------
**unbound** [``-hdpv``] [``-c <cfgfile>``]
Description
-----------
``unbound`` is a caching DNS resolver.
It uses a built in list of authoritative nameservers for the root zone (``.``),
the so called root hints.
On receiving a DNS query it will ask the root nameservers for an answer and
will in almost all cases receive a delegation to a top level domain (TLD)
authoritative nameserver.
It will then ask that nameserver for an answer.
It will recursively continue until an answer is found or no answer is available
(NXDOMAIN).
For performance and efficiency reasons that answer is cached for a certain time
(the answer's time-to-live or TTL).
A second query for the same name will then be answered from the cache.
Unbound can also do DNSSEC validation.
To use a locally running Unbound for resolving put:
.. code-block:: text
nameserver 127.0.0.1
into *resolv.conf(5)*.
If authoritative DNS is needed as well using :external+nsd:doc:`manpages/nsd`,
careful setup is required because authoritative nameservers and resolvers are
using the same port number (53).
The available options are:
.. option:: -h
Show the version number and commandline option help, and exit.
.. option:: -c <cfgfile>
Set the config file with settings for unbound to read instead of reading the
file at the default location, :file:`@ub_conf_file@`.
The syntax is described in :doc:`unbound.conf(5)</manpages/unbound.conf>`.
.. option:: -d
Debug flag: do not fork into the background, but stay attached to the
console.
This flag will also delay writing to the log file until the thread-spawn
time, so that most config and setup errors appear on stderr.
If given twice or more, logging does not switch to the log file or to
syslog, but the log messages are printed to stderr all the time.
.. option:: -p
Don't use a pidfile.
This argument should only be used by supervision systems which can ensure
that only one instance of Unbound will run concurrently.
.. option:: -v
Increase verbosity.
If given multiple times, more information is logged.
This is in addition to the verbosity (if any) from the config file.
.. option:: -V
Show the version number and build options, and exit.
See Also
--------
:doc:`unbound.conf(5)</manpages/unbound.conf>`,
:doc:`unbound-checkconf(8)</manpages/unbound-checkconf>`,
:external+nsd:doc:`manpages/nsd`.

View file

@ -116,7 +116,7 @@ struct addredge {
addrlen_t len;
/** child node this edge is connected to */
struct addrnode *node;
/** Parent node this ege is connected to */
/** Parent node this edge is connected to */
struct addrnode *parent_node;
/** Index of this edge in parent_node */
int parent_index;

View file

@ -51,6 +51,7 @@
#include "services/cache/dns.h"
#include "util/module.h"
#include "util/regional.h"
#include "util/fptr_wlist.h"
#include "util/storage/slabhash.h"
#include "util/config_file.h"
#include "util/data/msgreply.h"
@ -153,9 +154,25 @@ int ecs_whitelist_check(struct query_info* qinfo,
return 1;
sn_env = (struct subnet_env*)qstate->env->modinfo[id];
if(sq->is_subquery_nonsubnet) {
if(sq->is_subquery_scopezero) {
/* Check if the result can be stored in the global cache,
* this is okay if the address and name are not configured
* as subnet address and subnet zone. */
if(!ecs_is_whitelisted(sn_env->whitelist,
addr, addrlen, qinfo->qname, qinfo->qname_len,
qinfo->qclass)) {
verbose(VERB_ALGO, "subnet store subquery global, name and addr have no subnet treatment.");
qstate->no_cache_store = 0;
}
}
return 1;
}
/* Cache by default, might be disabled after parsing EDNS option
* received from nameserver. */
if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)) {
if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)
&& sq->ecs_client_in.subnet_validdata) {
qstate->no_cache_store = 0;
}
@ -232,13 +249,13 @@ subnetmod_init(struct module_env *env, int id)
HASH_DEFAULT_STARTARRAY, env->cfg->msg_cache_size,
msg_cache_sizefunc, query_info_compare, query_entry_delete,
subnet_data_delete, NULL);
slabhash_setmarkdel(sn_env->subnet_msg_cache, &subnet_markdel);
if(!sn_env->subnet_msg_cache) {
log_err("subnetcache: could not create cache");
free(sn_env);
env->modinfo[id] = NULL;
return 0;
}
slabhash_setmarkdel(sn_env->subnet_msg_cache, &subnet_markdel);
/* whitelist for edns subnet capable servers */
sn_env->whitelist = ecs_whitelist_create();
if(!sn_env->whitelist ||
@ -522,6 +539,83 @@ common_prefix(uint8_t *a, uint8_t *b, uint8_t net)
return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]);
}
/**
* Create sub request that looks up the query.
* @param qstate: query state
* @param id: module id.
* @param sq: subnet qstate
* @return false on failure.
*/
static int
generate_sub_request(struct module_qstate *qstate, int id, struct subnet_qstate* sq)
{
struct module_qstate* subq = NULL;
uint16_t qflags = 0; /* OPCODE QUERY, no flags */
int prime = 0;
int valrec = 0;
struct query_info qinf;
qinf.qname = qstate->qinfo.qname;
qinf.qname_len = qstate->qinfo.qname_len;
qinf.qtype = qstate->qinfo.qtype;
qinf.qclass = qstate->qinfo.qclass;
qinf.local_alias = NULL;
qflags |= BIT_RD;
if((qstate->query_flags & BIT_CD)!=0) {
qflags |= BIT_CD;
valrec = 1;
}
fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec,
&subq)) {
return 0;
}
if(subq) {
/* It is possible to access the subquery module state. */
struct subnet_qstate* subsq;
if(!subnet_new_qstate(subq, id)) {
verbose(VERB_ALGO, "Could not allocate new subnet qstate");
return 0;
}
subsq = (struct subnet_qstate*)subq->minfo[id];
subsq->is_subquery_nonsubnet = 1;
/* When the client asks 0.0.0.0/0 and the name is not treated
* as subnet, it is to be stored in the global cache.
* Store that the client asked for that, if so. */
if(sq->ecs_client_in.subnet_source_mask == 0 &&
edns_opt_list_find(qstate->edns_opts_front_in,
qstate->env->cfg->client_subnet_opcode)) {
subq->no_cache_store = 1;
subsq->is_subquery_scopezero = 1;
}
}
return 1;
}
/**
* Perform the query without subnet
* @param qstate: query state
* @param id: module id.
* @param sq: subnet qstate
* @return module state
*/
static enum module_ext_state
generate_lookup_without_subnet(struct module_qstate *qstate, int id,
struct subnet_qstate* sq)
{
verbose(VERB_ALGO, "subnetcache: make subquery to look up without subnet");
if(!generate_sub_request(qstate, id, sq)) {
verbose(VERB_ALGO, "Could not generate sub query");
qstate->return_rcode = LDNS_RCODE_SERVFAIL;
qstate->return_msg = NULL;
return module_finished;
}
sq->wait_subquery = 1;
return module_wait_subquery;
}
static enum module_ext_state
eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
{
@ -557,14 +651,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
* is still useful to put it in the edns subnet cache for
* when a client explicitly asks for subnet specific answer. */
verbose(VERB_QUERY, "subnetcache: Authority indicates no support");
if(!sq->started_no_cache_store) {
lock_rw_wrlock(&sne->biglock);
update_cache(qstate, id);
lock_rw_unlock(&sne->biglock);
}
if (sq->subnet_downstream)
cp_edns_bad_response(c_out, c_in);
return module_finished;
return generate_lookup_without_subnet(qstate, id, sq);
}
/* Purposefully there was no sent subnet, and there is consequently
@ -589,14 +676,14 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
!common_prefix(s_out->subnet_addr, s_in->subnet_addr,
s_out->subnet_source_mask))
{
/* we can not accept, restart query without option */
/* we can not accept, perform query without option */
verbose(VERB_QUERY, "subnetcache: forged data");
s_out->subnet_validdata = 0;
(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;
return generate_lookup_without_subnet(qstate, id, sq);
}
lock_rw_wrlock(&sne->biglock);
@ -795,6 +882,9 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id,
} else if(sq->subnet_sent_no_subnet) {
/* The answer can be stored as scope 0, not in global cache. */
qstate->no_cache_store = 1;
} else if(sq->subnet_sent) {
/* Need another query to be able to store in global cache. */
qstate->no_cache_store = 1;
}
return 1;
@ -812,6 +902,32 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
strmodulevent(event));
log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo);
if(sq && sq->wait_subquery_done) {
/* The subquery lookup returned. */
if(sq->ecs_client_in.subnet_source_mask == 0 &&
edns_opt_list_find(qstate->edns_opts_front_in,
qstate->env->cfg->client_subnet_opcode)) {
if(!sq->started_no_cache_store &&
qstate->return_msg) {
lock_rw_wrlock(&sne->biglock);
update_cache(qstate, id);
lock_rw_unlock(&sne->biglock);
}
if (sq->subnet_downstream)
cp_edns_bad_response(&sq->ecs_client_out,
&sq->ecs_client_in);
/* It is a scope zero lookup, append edns subnet
* option to the querier. */
subnet_ecs_opt_list_append(&sq->ecs_client_out,
&qstate->edns_opts_front_out, qstate,
qstate->region);
}
sq->wait_subquery_done = 0;
qstate->ext_state[id] = module_finished;
qstate->no_cache_store = sq->started_no_cache_store;
qstate->no_cache_lookup = sq->started_no_cache_lookup;
return;
}
if((event == module_event_new || event == module_event_pass) &&
sq == NULL) {
struct edns_option* ecs_opt;
@ -822,6 +938,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
}
sq = (struct subnet_qstate*)qstate->minfo[id];
if(sq->wait_subquery)
return; /* Wait for that subquery to return */
if((ecs_opt = edns_opt_list_find(
qstate->edns_opts_front_in,
@ -851,6 +969,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
/* No clients are interested in result or we could not
* parse it, we don't do client subnet */
sq->ecs_server_out.subnet_validdata = 0;
if(edns_opt_list_find(qstate->edns_opts_front_in,
qstate->env->cfg->client_subnet_opcode)) {
/* aggregated this deaggregated state */
qstate->ext_state[id] =
generate_lookup_without_subnet(
qstate, id, sq);
return;
}
verbose(VERB_ALGO, "subnetcache: pass to next module");
qstate->ext_state[id] = module_wait_module;
return;
@ -891,6 +1017,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
}
lock_rw_unlock(&sne->biglock);
}
if(sq->ecs_client_in.subnet_source_mask == 0 &&
edns_opt_list_find(qstate->edns_opts_front_in,
qstate->env->cfg->client_subnet_opcode)) {
/* client asked for resolution without edns subnet */
qstate->ext_state[id] = generate_lookup_without_subnet(
qstate, id, sq);
return;
}
sq->ecs_server_out.subnet_addr_fam =
sq->ecs_client_in.subnet_addr_fam;
@ -927,6 +1061,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
qstate->ext_state[id] = module_wait_module;
return;
}
if(sq && sq->wait_subquery)
return; /* Wait for that subquery to return */
/* Query handed back by next module, we have a 'final' answer */
if(sq && event == module_event_moddone) {
qstate->ext_state[id] = eval_response(qstate, id, sq);
@ -975,10 +1111,27 @@ subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate),
}
void
subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate),
int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super))
subnetmod_inform_super(struct module_qstate *qstate, int id,
struct module_qstate *super)
{
/* Not used */
struct subnet_qstate* super_sq =
(struct subnet_qstate*)super->minfo[id];
log_query_info(VERB_ALGO, "subnetcache inform_super: query",
&super->qinfo);
super_sq->wait_subquery = 0;
super_sq->wait_subquery_done = 1;
if(qstate->return_rcode != LDNS_RCODE_NOERROR ||
!qstate->return_msg) {
super->return_msg = NULL;
super->return_rcode = LDNS_RCODE_SERVFAIL;
return;
}
super->return_rcode = LDNS_RCODE_NOERROR;
super->return_msg = dns_copy_msg(qstate->return_msg, super->region);
if(!super->return_msg) {
log_err("subnetcache: copy response, out of memory");
super->return_rcode = LDNS_RCODE_SERVFAIL;
}
}
size_t

View file

@ -102,6 +102,14 @@ struct subnet_qstate {
int started_no_cache_store;
/** has the subnet module been started with no_cache_lookup? */
int started_no_cache_lookup;
/** Wait for subquery that has been started for nonsubnet lookup. */
int wait_subquery;
/** The subquery waited for is done. */
int wait_subquery_done;
/** The subnet state is a subquery state for nonsubnet lookup. */
int is_subquery_nonsubnet;
/** This is a subquery, and it is made due to a scope zero request. */
int is_subquery_scopezero;
};
void subnet_data_delete(void* d, void* ATTR_UNUSED(arg));

View file

@ -278,7 +278,7 @@ delegpt_count_addr(struct delegpt* dp, size_t* numaddr, size_t* numres,
void delegpt_log(enum verbosity_value v, struct delegpt* dp)
{
char buf[LDNS_MAX_DOMAINLEN+1];
char buf[LDNS_MAX_DOMAINLEN];
struct delegpt_ns* ns;
struct delegpt_addr* a;
size_t missing=0, numns=0, numaddr=0, numres=0, numavail=0;

View file

@ -79,6 +79,16 @@ struct delegpt {
* Also true if the delegationpoint was created from a delegation
* message and thus contains the parent-side-info already. */
uint8_t has_parent_side_NS;
/** if true, the delegation point has reached last resort processing
* and the parent side information has been possibly added to the
* delegation point.
* For now this signals that further target lookups will ignore
* the configured target-fetch-policy and only resolve on
* demand to try and avoid triggering limits at this stage (.i.e, it
* is very likely that the A/AAAA queries for the newly added name
* servers will not yield new IP addresses and trigger NXNS
* countermeasures. */
uint8_t fallback_to_parent_side_NS;
/** for assertions on type of delegpt */
uint8_t dp_type_mlc;
/** use SSL for upstream query */

View file

@ -129,7 +129,7 @@ forwards_insert_data(struct iter_forwards* fwd, uint16_t c, uint8_t* nm,
node->namelabs = nmlabs;
node->dp = dp;
if(!rbtree_insert(fwd->tree, &node->node)) {
char buf[257];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(nm, buf);
log_err("duplicate forward zone %s ignored.", buf);
delegpt_free_mlc(dp);
@ -139,6 +139,17 @@ forwards_insert_data(struct iter_forwards* fwd, uint16_t c, uint8_t* nm,
return 1;
}
static struct iter_forward_zone*
fwd_zone_find(struct iter_forwards* fwd, uint16_t c, uint8_t* nm)
{
struct iter_forward_zone key;
key.node.key = &key;
key.dclass = c;
key.name = nm;
key.namelabs = dname_count_size_labels(nm, &key.namelen);
return (struct iter_forward_zone*)rbtree_search(fwd->tree, &key);
}
/** insert new info into forward structure given dp */
static int
forwards_insert(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp)
@ -321,6 +332,40 @@ make_stub_holes(struct iter_forwards* fwd, struct config_file* cfg)
log_err("cannot parse stub name '%s'", s->name);
return 0;
}
if(fwd_zone_find(fwd, LDNS_RR_CLASS_IN, dname) != NULL) {
/* Already a forward zone there. */
free(dname);
continue;
}
if(!fwd_add_stub_hole(fwd, LDNS_RR_CLASS_IN, dname)) {
free(dname);
log_err("out of memory");
return 0;
}
free(dname);
}
return 1;
}
/** make NULL entries for auths */
static int
make_auth_holes(struct iter_forwards* fwd, struct config_file* cfg)
{
struct config_auth* a;
uint8_t* dname;
size_t dname_len;
for(a = cfg->auths; a; a = a->next) {
if(!a->name) continue;
dname = sldns_str2wire_dname(a->name, &dname_len);
if(!dname) {
log_err("cannot parse auth name '%s'", a->name);
return 0;
}
if(fwd_zone_find(fwd, LDNS_RR_CLASS_IN, dname) != NULL) {
/* Already a forward zone there. */
free(dname);
continue;
}
if(!fwd_add_stub_hole(fwd, LDNS_RR_CLASS_IN, dname)) {
free(dname);
log_err("out of memory");
@ -353,6 +398,16 @@ forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg)
lock_rw_unlock(&fwd->lock);
return 0;
}
/* TODO: Now we punch holes for auth zones as well so that in
* iterator:forward_request() we see the configured
* delegation point, but code flow/naming is hard to follow.
* Consider having a single tree with configured
* delegation points for all categories
* (stubs, forwards, auths). */
if(!make_auth_holes(fwd, cfg)) {
lock_rw_unlock(&fwd->lock);
return 0;
}
fwd_init_parents(fwd);
lock_rw_unlock(&fwd->lock);
return 1;
@ -503,17 +558,6 @@ forwards_get_mem(struct iter_forwards* fwd)
return s;
}
static struct iter_forward_zone*
fwd_zone_find(struct iter_forwards* fwd, uint16_t c, uint8_t* nm)
{
struct iter_forward_zone key;
key.node.key = &key;
key.dclass = c;
key.name = nm;
key.namelabs = dname_count_size_labels(nm, &key.namelen);
return (struct iter_forward_zone*)rbtree_search(fwd->tree, &key);
}
int
forwards_add_zone(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp,
int nolock)
@ -590,3 +634,19 @@ forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c,
fwd_init_parents(fwd);
if(!nolock) { lock_rw_unlock(&fwd->lock); }
}
void
forwards_swap_tree(struct iter_forwards* fwd, struct iter_forwards* data)
{
rbtree_type* oldtree = fwd->tree;
if(oldtree) {
lock_unprotect(&fwd->lock, oldtree);
}
if(data->tree) {
lock_unprotect(&data->lock, data->tree);
}
fwd->tree = data->tree;
data->tree = oldtree;
lock_protect(&fwd->lock, fwd->tree, sizeof(*fwd->tree));
lock_protect(&data->lock, data->tree, sizeof(*data->tree));
}

View file

@ -234,4 +234,13 @@ int forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c,
void forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c,
uint8_t* nm, int nolock);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param fwd: the forward data structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void forwards_swap_tree(struct iter_forwards* fwd, struct iter_forwards* data);
#endif /* ITERATOR_ITER_FWD_H */

View file

@ -181,7 +181,7 @@ hints_insert(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
node->noprime = (uint8_t)noprime;
if(!name_tree_insert(&hints->tree, &node->node, dp->name, dp->namelen,
dp->namelabs, c)) {
char buf[257];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(dp->name, buf);
log_err("second hints for zone %s ignored.", buf);
delegpt_free_mlc(dp);
@ -611,3 +611,14 @@ hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm,
name_tree_init_parents(&hints->tree);
if(!nolock) { lock_rw_unlock(&hints->lock); }
}
void
hints_swap_tree(struct iter_hints* hints, struct iter_hints* data)
{
rbnode_type* oldroot = hints->tree.root;
size_t oldcount = hints->tree.count;
hints->tree.root = data->tree.root;
hints->tree.count = data->tree.count;
data->tree.root = oldroot;
data->tree.count = oldcount;
}

View file

@ -198,4 +198,13 @@ int hints_add_stub(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
void hints_delete_stub(struct iter_hints* hints, uint16_t c,
uint8_t* nm, int nolock);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param hints: the hints data structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void hints_swap_tree(struct iter_hints* hints, struct iter_hints* data);
#endif /* ITERATOR_ITER_HINTS_H */

View file

@ -77,41 +77,73 @@
static const char DEFAULT_NAT64_PREFIX[] = "64:ff9b::/96";
/** fillup fetch policy array */
static void
fetch_fill(struct iter_env* ie, const char* str)
static int
fetch_fill(int* target_fetch_policy, int max_dependency_depth, const char* str)
{
char* s = (char*)str, *e;
int i;
for(i=0; i<ie->max_dependency_depth+1; i++) {
ie->target_fetch_policy[i] = strtol(s, &e, 10);
if(s == e)
fatal_exit("cannot parse fetch policy number %s", s);
for(i=0; i<max_dependency_depth+1; i++) {
target_fetch_policy[i] = strtol(s, &e, 10);
if(s == e) {
log_err("cannot parse fetch policy number %s", s);
return 0;
}
s = e;
}
return 1;
}
/** Read config string that represents the target fetch policy */
static int
read_fetch_policy(struct iter_env* ie, const char* str)
int
read_fetch_policy(int** target_fetch_policy, int* max_dependency_depth,
const char* str)
{
int count = cfg_count_numbers(str);
if(count < 1) {
log_err("Cannot parse target fetch policy: \"%s\"", str);
return 0;
}
ie->max_dependency_depth = count - 1;
ie->target_fetch_policy = (int*)calloc(
(size_t)ie->max_dependency_depth+1, sizeof(int));
if(!ie->target_fetch_policy) {
*max_dependency_depth = count - 1;
*target_fetch_policy = (int*)calloc(
(size_t)(*max_dependency_depth)+1, sizeof(int));
if(!*target_fetch_policy) {
log_err("alloc fetch policy: out of memory");
return 0;
}
fetch_fill(ie, str);
if(!fetch_fill(*target_fetch_policy, *max_dependency_depth, str))
return 0;
return 1;
}
/** apply config caps whitelist items to name tree */
static int
struct rbtree_type*
caps_white_create(void)
{
struct rbtree_type* caps_white = rbtree_create(name_tree_compare);
if(!caps_white)
log_err("out of memory");
return caps_white;
}
/** delete caps_whitelist element */
static void
caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d))
{
if(n) {
free(((struct name_tree_node*)n)->name);
free(n);
}
}
void
caps_white_delete(struct rbtree_type* caps_white)
{
if(!caps_white)
return;
traverse_postorder(caps_white, caps_free, NULL);
free(caps_white);
}
int
caps_white_apply_cfg(rbtree_type* ntree, struct config_file* cfg)
{
struct config_strlist* p;
@ -145,12 +177,41 @@ caps_white_apply_cfg(rbtree_type* ntree, struct config_file* cfg)
}
int
iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
nat64_apply_cfg(struct iter_nat64* nat64, struct config_file* cfg)
{
const char *nat64_prefix;
nat64_prefix = cfg->nat64_prefix;
if(!nat64_prefix)
nat64_prefix = cfg->dns64_prefix;
if(!nat64_prefix)
nat64_prefix = DEFAULT_NAT64_PREFIX;
if(!netblockstrtoaddr(nat64_prefix, 0, &nat64->nat64_prefix_addr,
&nat64->nat64_prefix_addrlen, &nat64->nat64_prefix_net)) {
log_err("cannot parse nat64-prefix netblock: %s", nat64_prefix);
return 0;
}
if(!addr_is_ip6(&nat64->nat64_prefix_addr,
nat64->nat64_prefix_addrlen)) {
log_err("nat64-prefix is not IPv6: %s", cfg->nat64_prefix);
return 0;
}
if(!prefixnet_is_nat64(nat64->nat64_prefix_net)) {
log_err("nat64-prefix length it not 32, 40, 48, 56, 64 or 96: %s",
nat64_prefix);
return 0;
}
nat64->use_nat64 = cfg->do_nat64;
return 1;
}
int
iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
{
int i;
/* target fetch policy */
if(!read_fetch_policy(iter_env, cfg->target_fetch_policy))
if(!read_fetch_policy(&iter_env->target_fetch_policy,
&iter_env->max_dependency_depth, cfg->target_fetch_policy))
return 0;
for(i=0; i<iter_env->max_dependency_depth+1; i++)
verbose(VERB_QUERY, "target fetch policy for level %d is %d",
@ -170,7 +231,7 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
}
if(cfg->caps_whitelist) {
if(!iter_env->caps_white)
iter_env->caps_white = rbtree_create(name_tree_compare);
iter_env->caps_white = caps_white_create();
if(!iter_env->caps_white || !caps_white_apply_cfg(
iter_env->caps_white, cfg)) {
log_err("Could not set capsforid whitelist");
@ -179,31 +240,13 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
}
nat64_prefix = cfg->nat64_prefix;
if(!nat64_prefix)
nat64_prefix = cfg->dns64_prefix;
if(!nat64_prefix)
nat64_prefix = DEFAULT_NAT64_PREFIX;
if(!netblockstrtoaddr(nat64_prefix, 0, &iter_env->nat64_prefix_addr,
&iter_env->nat64_prefix_addrlen,
&iter_env->nat64_prefix_net)) {
log_err("cannot parse nat64-prefix netblock: %s", nat64_prefix);
return 0;
}
if(!addr_is_ip6(&iter_env->nat64_prefix_addr,
iter_env->nat64_prefix_addrlen)) {
log_err("nat64-prefix is not IPv6: %s", cfg->nat64_prefix);
return 0;
}
if(!prefixnet_is_nat64(iter_env->nat64_prefix_net)) {
log_err("nat64-prefix length it not 32, 40, 48, 56, 64 or 96: %s",
nat64_prefix);
if(!nat64_apply_cfg(&iter_env->nat64, cfg)) {
log_err("Could not setup nat64");
return 0;
}
iter_env->supports_ipv6 = cfg->do_ip6;
iter_env->supports_ipv4 = cfg->do_ip4;
iter_env->use_nat64 = cfg->do_nat64;
iter_env->outbound_msg_retry = cfg->outbound_msg_retry;
iter_env->max_sent_count = cfg->max_sent_count;
iter_env->max_query_restarts = cfg->max_query_restarts;
@ -270,7 +313,7 @@ iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env,
if(!iter_env->supports_ipv6 && addr_is_ip6(&a->addr, a->addrlen)) {
return -1; /* there is no ip6 available */
}
if(!iter_env->supports_ipv4 && !iter_env->use_nat64 &&
if(!iter_env->supports_ipv4 && !iter_env->nat64.use_nat64 &&
!addr_is_ip6(&a->addr, a->addrlen)) {
return -1; /* there is no ip4 available */
}
@ -1489,14 +1532,15 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf,
/* check stub */
if (stub != NULL && stub->dp != NULL) {
enum verbosity_value level = VERB_ALGO;
int stub_no_cache = stub->dp->no_cache;
lock_rw_unlock(&qstate->env->fwds->lock);
if(stub_no_cache) {
char qname[255+1];
char dpname[255+1];
if(verbosity >= level && stub_no_cache) {
char qname[LDNS_MAX_DOMAINLEN];
char dpname[LDNS_MAX_DOMAINLEN];
dname_str(qinf->qname, qname);
dname_str(stub->dp->name, dpname);
verbose(VERB_ALGO, "stub for %s %s has no_cache", qname, dpname);
verbose(level, "stub for %s %s has no_cache", qname, dpname);
}
if(retdpname) {
if(stub->dp->namelen > dpname_storage_len) {
@ -1517,14 +1561,15 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf,
/* Check for forward. */
if (dp) {
enum verbosity_value level = VERB_ALGO;
int dp_no_cache = dp->no_cache;
lock_rw_unlock(&qstate->env->hints->lock);
if(dp_no_cache) {
char qname[255+1];
char dpname[255+1];
if(verbosity >= level && dp_no_cache) {
char qname[LDNS_MAX_DOMAINLEN];
char dpname[LDNS_MAX_DOMAINLEN];
dname_str(qinf->qname, qname);
dname_str(dp->name, dpname);
verbose(VERB_ALGO, "forward for %s %s has no_cache", qname, dpname);
verbose(level, "forward for %s %s has no_cache", qname, dpname);
}
if(retdpname) {
if(dp->namelen > dpname_storage_len) {

View file

@ -61,6 +61,7 @@ struct sock_list;
struct ub_packed_rrset_key;
struct module_stack;
struct outside_network;
struct iter_nat64;
/* max number of lookups in the cache for target nameserver names.
* This stops, for large delegations, N*N lookups in the cache. */
@ -430,6 +431,43 @@ int iter_stub_fwd_no_cache(struct module_qstate *qstate,
void iterator_set_ip46_support(struct module_stack* mods,
struct module_env* env, struct outside_network* outnet);
/**
* Read config string that represents the target fetch policy.
* @param target_fetch_policy: alloced on return.
* @param max_dependency_depth: set on return.
* @param str: the config string
* @return false on failure.
*/
int read_fetch_policy(int** target_fetch_policy, int* max_dependency_depth,
const char* str);
/**
* Create caps exempt data structure.
* @return NULL on failure.
*/
struct rbtree_type* caps_white_create(void);
/**
* Delete caps exempt data structure.
* @param caps_white: caps exempt tree.
*/
void caps_white_delete(struct rbtree_type* caps_white);
/**
* Apply config caps whitelist items to name tree
* @param ntree: caps exempt tree.
* @param cfg: config with options.
*/
int caps_white_apply_cfg(struct rbtree_type* ntree, struct config_file* cfg);
/**
* Apply config for nat64
* @param nat64: the nat64 state.
* @param cfg: config with options.
* @return false on failure.
*/
int nat64_apply_cfg(struct iter_nat64* nat64, struct config_file* cfg);
/**
* Limit NSEC and NSEC3 TTL in response, RFC9077
* @param msg: dns message, the SOA record ttl is used to restrict ttls

View file

@ -78,6 +78,8 @@ int UNKNOWN_SERVER_NICENESS = 376;
int USEFUL_SERVER_TOP_TIMEOUT = 120000;
/* Equals USEFUL_SERVER_TOP_TIMEOUT*4 */
int BLACKLIST_PENALTY = (120000*4);
/** Timeout when only a single probe query per IP is allowed. */
int PROBE_MAXRTO = PROBE_MAXRTO_DEFAULT; /* in msec */
static void target_count_increase_nx(struct iter_qstate* iq, int num);
@ -105,16 +107,6 @@ iter_init(struct module_env* env, int id)
return 1;
}
/** delete caps_whitelist element */
static void
caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d))
{
if(n) {
free(((struct name_tree_node*)n)->name);
free(n);
}
}
void
iter_deinit(struct module_env* env, int id)
{
@ -126,10 +118,7 @@ iter_deinit(struct module_env* env, int id)
free(iter_env->target_fetch_policy);
priv_delete(iter_env->priv);
donotq_delete(iter_env->donotq);
if(iter_env->caps_white) {
traverse_postorder(iter_env->caps_white, caps_free, NULL);
free(iter_env->caps_white);
}
caps_white_delete(iter_env->caps_white);
free(iter_env);
env->modinfo[id] = NULL;
}
@ -258,7 +247,7 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
log_err("out of memory adding missing");
}
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) &&
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->nat64.use_nat64)) &&
(dpns->got6 == 2 || !ie->supports_ipv6)) {
dpns->resolved = 1; /* mark as failed */
target_count_increase_nx(super_iq, 1);
@ -1092,7 +1081,7 @@ auth_zone_delegpt(struct module_qstate* qstate, struct iter_qstate* iq,
/* cache is blacklisted and fallback, and we
* already have an auth_zone dp */
if(verbosity>=VERB_ALGO) {
char buf[255+1];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(z->name, buf);
verbose(VERB_ALGO, "auth_zone %s "
"fallback because cache blacklisted",
@ -1109,7 +1098,7 @@ auth_zone_delegpt(struct module_qstate* qstate, struct iter_qstate* iq,
* validation failure, and the zone allows
* fallback to the internet, query there. */
if(verbosity>=VERB_ALGO) {
char buf[255+1];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(z->name, buf);
verbose(VERB_ALGO, "auth_zone %s "
"fallback because cache blacklisted",
@ -1723,7 +1712,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
*/
if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags,
iq->dp, ie->supports_ipv4, ie->supports_ipv6,
ie->use_nat64)) {
ie->nat64.use_nat64)) {
int have_dp = 0;
if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &have_dp, &iq->dp, qstate->region)) {
if(have_dp) {
@ -2033,7 +2022,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
return 1;
if(iq->depth > 0 && iq->target_count &&
iq->target_count[TARGET_COUNT_QUERIES] > MAX_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
char s[LDNS_MAX_DOMAINLEN];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"number of glue fetches %d", s,
@ -2041,7 +2030,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
return 2;
}
if(iq->dp_target_count > MAX_DP_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
char s[LDNS_MAX_DOMAINLEN];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"number of glue fetches %d to a single delegation point",
@ -2087,7 +2076,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
if(mesh_jostle_exceeded(qstate->env->mesh)) {
/* If no ip4 query is possible, that makes
* this ns resolved. */
if(!((ie->supports_ipv4 || ie->use_nat64) &&
if(!((ie->supports_ipv4 || ie->nat64.use_nat64) &&
((ns->lame && !ns->done_pside4) ||
(!ns->lame && !ns->got4)))) {
ns->resolved = 1;
@ -2096,7 +2085,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
}
}
/* Send the A request. */
if((ie->supports_ipv4 || ie->use_nat64) &&
if((ie->supports_ipv4 || ie->nat64.use_nat64) &&
((ns->lame && !ns->done_pside4) ||
(!ns->lame && !ns->got4))) {
if(!generate_target_query(qstate, iq, id,
@ -2163,6 +2152,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
verbose(VERB_QUERY, "configured stub or forward servers failed -- returning SERVFAIL");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->dp->fallback_to_parent_side_NS = 1;
if(qstate->env->cfg->harden_unverified_glue) {
if(!cache_fill_missing(qstate->env, iq->qchase.qclass,
qstate->region, iq->dp, PACKED_RRSET_UNVERIFIED_GLUE))
@ -2191,6 +2181,10 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
a->lame, a->tls_auth_name, -1, NULL);
}
lock_rw_unlock(&qstate->env->hints->lock);
/* copy over some configuration since we update the
* delegation point in place */
iq->dp->tcp_upstream = dp->tcp_upstream;
iq->dp->ssl_upstream = dp->ssl_upstream;
}
iq->dp->has_parent_side_NS = 1;
} else if(!iq->dp->has_parent_side_NS) {
@ -2252,7 +2246,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
}
if(iq->depth > 0 && iq->target_count &&
iq->target_count[TARGET_COUNT_QUERIES] > MAX_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
char s[LDNS_MAX_DOMAINLEN];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"number of glue fetches %d", s,
@ -2268,14 +2262,14 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
/* if this nameserver is at a delegation point, but that
* delegation point is a stub and we cannot go higher, skip*/
if( ((ie->supports_ipv6 && !ns->done_pside6) ||
((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4)) &&
((ie->supports_ipv4 || ie->nat64.use_nat64) && !ns->done_pside4)) &&
!can_have_last_resort(qstate->env, ns->name, ns->namelen,
iq->qchase.qclass, NULL, NULL, NULL)) {
log_nametypeclass(VERB_ALGO, "cannot pside lookup ns "
"because it is also a stub/forward,",
ns->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);
if(ie->supports_ipv6) ns->done_pside6 = 1;
if(ie->supports_ipv4 || ie->use_nat64) ns->done_pside4 = 1;
if(ie->supports_ipv4 || ie->nat64.use_nat64) ns->done_pside4 = 1;
continue;
}
/* query for parent-side A and AAAA for nameservers */
@ -2300,7 +2294,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
return 0;
}
}
if((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4) {
if((ie->supports_ipv4 || ie->nat64.use_nat64) && !ns->done_pside4) {
/* Send the A request. */
if(!generate_parentside_target_query(qstate, iq, id,
ns->name, ns->namelen,
@ -2569,7 +2563,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
}
if(!ie->supports_ipv6)
delegpt_no_ipv6(iq->dp);
if(!ie->supports_ipv4 && !ie->use_nat64)
if(!ie->supports_ipv4 && !ie->nat64.use_nat64)
delegpt_no_ipv4(iq->dp);
delegpt_log(VERB_ALGO, iq->dp);
@ -2741,9 +2735,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
if((iq->chase_flags&BIT_RD) && !(iq->response->rep->flags&BIT_AA)) {
verbose(VERB_ALGO, "forwarder, ignoring referral from auth zone");
} else {
lock_rw_wrlock(&qstate->env->auth_zones->lock);
qstate->env->auth_zones->num_query_up++;
lock_rw_unlock(&qstate->env->auth_zones->lock);
qstate->env->mesh->num_query_authzone_up++;
iq->num_current_queries++;
iq->chase_to_rd = 0;
iq->dnssec_lame_query = 0;
@ -2781,7 +2773,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
}
/* if the mesh query list is full, then do not waste cpu and sockets to
* fetch promiscuous targets. They can be looked up when needed. */
if(can_do_promisc && !mesh_jostle_exceeded(qstate->env->mesh)) {
if(!iq->dp->fallback_to_parent_side_NS && can_do_promisc
&& !mesh_jostle_exceeded(qstate->env->mesh)) {
tf_policy = ie->target_fetch_policy[iq->depth];
}
@ -3046,7 +3039,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
target_count_increase_global_quota(iq, 1);
if(iq->target_count && iq->target_count[TARGET_COUNT_GLOBAL_QUOTA]
> MAX_GLOBAL_QUOTA) {
char s[LDNS_MAX_DOMAINLEN+1];
char s[LDNS_MAX_DOMAINLEN];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"global quota on number of upstream queries %d", s,
@ -3070,9 +3063,9 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
real_addr = target->addr;
real_addrlen = target->addrlen;
if(ie->use_nat64 && target->addr.ss_family == AF_INET) {
addr_to_nat64(&target->addr, &ie->nat64_prefix_addr,
ie->nat64_prefix_addrlen, ie->nat64_prefix_net,
if(ie->nat64.use_nat64 && target->addr.ss_family == AF_INET) {
addr_to_nat64(&target->addr, &ie->nat64.nat64_prefix_addr,
ie->nat64.nat64_prefix_addrlen, ie->nat64.nat64_prefix_net,
&real_addr, &real_addrlen);
log_name_addr(VERB_QUERY, "applied NAT64:",
iq->dp->name, &real_addr, real_addrlen);
@ -3260,13 +3253,19 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
}
}
if(type == RESPONSE_TYPE_CNAME &&
iq->qchase.qtype == LDNS_RR_TYPE_CNAME &&
(iq->qchase.qtype == LDNS_RR_TYPE_CNAME ||
iq->qchase.qtype == LDNS_RR_TYPE_ANY) &&
iq->minimisation_state == MINIMISE_STATE &&
query_dname_compare(iq->qchase.qname, iq->qinfo_out.qname) == 0) {
/* The minimised query for full QTYPE and hidden QTYPE can be
* classified as CNAME response type, even when the original
* QTYPE=CNAME. This should be treated as answer response type.
*/
/* For QTYPE=ANY, it is also considered the response, that
* is what the classifier would say, if it saw qtype ANY,
* and this same response was returned for that. The response
* can already be treated as such an answer, without having
* to send another query with a new qtype. */
type = RESPONSE_TYPE_ANSWER;
}
@ -3523,6 +3522,15 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
iq->num_target_queries = 0;
return processDSNSFind(qstate, iq, id);
}
if(iq->minimisation_state == MINIMISE_STATE &&
query_dname_compare(iq->qchase.qname,
iq->qinfo_out.qname) != 0) {
verbose(VERB_ALGO, "continue query minimisation, "
"downwards, after CNAME response for "
"intermediate label");
/* continue query minimisation, downwards */
return next_state(iq, QUERYTARGETS_STATE);
}
/* Process the CNAME response. */
if(!handle_cname_response(qstate, iq, iq->response,
&sname, &snamelen)) {
@ -3585,10 +3593,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
iq->auth_zone_response = 0;
iq->sent_count = 0;
iq->dp_target_count = 0;
if(iq->minimisation_state != MINIMISE_STATE)
/* Only count as query restart when it is not an extra
* query as result of qname minimisation. */
iq->query_restart_count++;
iq->query_restart_count++;
if(qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
@ -3882,7 +3887,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
} else {
verbose(VERB_ALGO, "iterator TargetResponse failed");
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) &&
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->nat64.use_nat64)) &&
(dpns->got6 == 2 || !ie->supports_ipv6)) {
dpns->resolved = 1; /* fail the target */
/* do not count cached answers */
@ -4160,7 +4165,7 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
/* store message with the finished prepended items,
* but only if we did recursion. The nonrecursion referral
* from cache does not need to be stored in the msg cache. */
if(!qstate->no_cache_store && qstate->query_flags&BIT_RD) {
if(!qstate->no_cache_store && (qstate->query_flags&BIT_RD)) {
iter_dns_store(qstate->env, &qstate->qinfo,
iq->response->rep, 0, qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
@ -4345,6 +4350,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
}
/* Copy the edns options we may got from the back end */
qstate->edns_opts_back_in = NULL;
if(edns.opt_list_in) {
qstate->edns_opts_back_in = edns_opt_copy_region(edns.opt_list_in,
qstate->region);

View file

@ -46,8 +46,6 @@
#include "util/data/msgreply.h"
#include "util/module.h"
struct delegpt;
struct iter_hints;
struct iter_forwards;
struct iter_donotq;
struct iter_prep_list;
struct iter_priv;
@ -108,15 +106,9 @@ extern int BLACKLIST_PENALTY;
#define EMPTY_NODATA_RETRY_COUNT 2
/**
* Global state for the iterator.
* Iterator global state for nat64.
*/
struct iter_env {
/** A flag to indicate whether or not we have an IPv6 route */
int supports_ipv6;
/** A flag to indicate whether or not we have an IPv4 route */
int supports_ipv4;
struct iter_nat64 {
/** A flag to locally apply NAT64 to make IPv4 addrs into IPv6 */
int use_nat64;
@ -128,6 +120,20 @@ struct iter_env {
/** CIDR mask length of NAT64 prefix */
int nat64_prefix_net;
};
/**
* Global state for the iterator.
*/
struct iter_env {
/** A flag to indicate whether or not we have an IPv6 route */
int supports_ipv6;
/** A flag to indicate whether or not we have an IPv4 route */
int supports_ipv4;
/** State for nat64 */
struct iter_nat64 nat64;
/** A set of inetaddrs that should never be queried. */
struct iter_donotq* donotq;

View file

@ -423,7 +423,7 @@ int libworker_bg(struct ub_ctx* ctx)
static int
fill_canon(struct ub_result* res, uint8_t* s)
{
char buf[255+2];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(s, buf);
res->canonname = strdup(buf);
return res->canonname != 0;
@ -630,8 +630,9 @@ int libworker_fg(struct ub_ctx* ctx, struct ctx_query* q)
free(qinfo.qname);
return UB_NOERROR;
}
if(ctx->env->auth_zones && auth_zones_answer(ctx->env->auth_zones,
w->env, &qinfo, &edns, NULL, w->back->udp_buff, w->env->scratch)) {
if(ctx->env->auth_zones && auth_zones_downstream_answer(
ctx->env->auth_zones, w->env, &qinfo, &edns, NULL,
w->back->udp_buff, w->env->scratch)) {
regional_free_all(w->env->scratch);
libworker_fillup_fg(q, LDNS_RCODE_NOERROR,
w->back->udp_buff, sec_status_insecure, NULL, 0);
@ -709,8 +710,9 @@ int libworker_attach_mesh(struct ub_ctx* ctx, struct ctx_query* q,
w->back->udp_buff, sec_status_insecure, NULL, 0);
return UB_NOERROR;
}
if(ctx->env->auth_zones && auth_zones_answer(ctx->env->auth_zones,
w->env, &qinfo, &edns, NULL, w->back->udp_buff, w->env->scratch)) {
if(ctx->env->auth_zones && auth_zones_downstream_answer(
ctx->env->auth_zones, w->env, &qinfo, &edns, NULL,
w->back->udp_buff, w->env->scratch)) {
regional_free_all(w->env->scratch);
free(qinfo.qname);
libworker_event_done_cb(q, LDNS_RCODE_NOERROR,
@ -847,8 +849,9 @@ handle_newq(struct libworker* w, uint8_t* buf, uint32_t len)
free(qinfo.qname);
return;
}
if(w->ctx->env->auth_zones && auth_zones_answer(w->ctx->env->auth_zones,
w->env, &qinfo, &edns, NULL, w->back->udp_buff, w->env->scratch)) {
if(w->ctx->env->auth_zones && auth_zones_downstream_answer(
w->ctx->env->auth_zones, w->env, &qinfo, &edns, NULL,
w->back->udp_buff, w->env->scratch)) {
regional_free_all(w->env->scratch);
q->msg_security = sec_status_insecure;
add_bg_result(w, q, w->back->udp_buff, UB_NOERROR, NULL, 0);
@ -1059,6 +1062,20 @@ void dtio_mainfdcallback(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
}
#endif
void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
#ifdef HAVE_NGTCP2
void doq_client_event_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))

View file

@ -772,6 +772,8 @@ struct ub_server_stats {
long long ans_bogus;
/** rrsets marked bogus by validator */
long long rrset_bogus;
/** number of signature validation operations performed by validator */
long long val_ops;
/** number of queries that have been ratelimited by domain recursion. */
long long queries_ratelimited;
/** unwanted traffic received on server-facing ports */
@ -853,6 +855,8 @@ struct ub_server_stats {
long long num_queries_discard_timeout;
/** number of queries removed due to wait-limit */
long long num_queries_wait_limit;
/** number of dns error reports generated */
long long num_dns_error_reports;
};
/**

View file

@ -59,7 +59,7 @@ Generate a distribution tar file for unbound.
-wxp expat.xx.tar.gz Also build expat from tarball for windows dist.
-wdir directory Build openssl and expat in a persistent directory for
windows dist. If builds are already in that directory
they are used right away. Useful when debuggin windows
they are used right away. Useful when debugging windows
builds.
-w32 32bit windows compile.
-w ... Build windows binary dist. last args passed to configure.
@ -539,6 +539,12 @@ if [ "$DOWIN" = "yes" ]; then
if test -f "$sspdll"; then
cp "$sspdll" libunbound/.
fi
if test "$W64" = "no"; then
# This could be solved with -static -static-libgcc -static-libstdc++.
# The dependency on c++ is probably due to libexpat. But the copy
# of the dll should work too. It may be needed for the libunbound.dll.
cp /usr/i686-w64-mingw32/sys-root/mingw/bin/libgcc_s_dw2-1.dll libunbound/.
fi
# zipfile
zip -r ../$file LICENSE README.txt unbound.exe unbound-anchor.exe unbound-host.exe unbound-control.exe unbound-checkconf.exe unbound-service-install.exe unbound-service-remove.exe unbound-control-setup.cmd example.conf service.conf root.key unbound-website.url create_unbound_ad_servers.cmd warmup.cmd unbound_cache.cmd Changelog libunbound
info "Testing $file"

View file

@ -38,12 +38,14 @@ import os
def dataHex(data, prefix=""):
"""Converts binary string data to display representation form"""
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])
d = map(lambda x:x, data[i*16:i*16+17])
count=0
for ch in d:
res += "%02X " % ch
for i in range(0,17-len(d)):
count+=1
for i in range(0,17-count):
res += " "
res += "| "
for ch in d:
@ -60,31 +62,31 @@ def logDnsMsg(qstate):
r = qstate.return_msg.rep
q = qstate.return_msg.qinfo
print "-"*100
print("-"*100)
print("Query: %s, type: %s (%d), class: %s (%d) " % (
qstate.qinfo.qname_str, qstate.qinfo.qtype_str, qstate.qinfo.qtype,
qstate.qinfo.qclass_str, qstate.qinfo.qclass))
print "-"*100
print "Return reply :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (r.flags, r.qdcount, r.security, r.ttl)
print " qinfo :: qname: %s %s, qtype: %s, qclass: %s" % (str(q.qname_list), q.qname_str, q.qtype_str, q.qclass_str)
print("-"*100)
print("Return reply :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (r.flags, r.qdcount, r.security, r.ttl))
print(" qinfo :: qname: %s %s, qtype: %s, qclass: %s" % (str(q.qname_list), q.qname_str, q.qtype_str, q.qclass_str))
if (r):
print "Reply:"
print("Reply:")
for i in range(0, r.rrset_count):
rr = r.rrsets[i]
rk = rr.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 = rr.entry.data
for j in range(0,d.count+d.rrsig_count):
print " ",j,":","TTL=",d.rr_ttl[j],
if (j >= d.count): print "rrsig",
print
print dataHex(d.rr_data[j]," ")
print(" ",j,":","TTL=",d.rr_ttl[j],)
if (j >= d.count): print("rrsig",)
print()
print(dataHex(d.rr_data[j]," "))
print "-"*100
print("-"*100)
def init(id, cfg):
log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script']))

View file

@ -204,7 +204,7 @@ struct query_info {
%inline %{
PyObject* dnameAsStr(PyObject* dname) {
char buf[LDNS_MAX_DOMAINLEN+1];
char buf[LDNS_MAX_DOMAINLEN];
buf[0] = '\0';
dname_str((uint8_t*)PyBytes_AsString(dname), buf);
return PyString_FromString(buf);

View file

@ -105,6 +105,7 @@ respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage*
socklen_t addrlen, int net, int create, const char* ipstr)
{
struct resp_addr* node;
log_assert(set);
node = (struct resp_addr*)addr_tree_find(&set->ip_tree, addr, addrlen, net);
if(!node && create) {
node = regional_alloc_zero(set->region, sizeof(*node));
@ -128,6 +129,7 @@ void
respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node)
{
struct resp_addr* prev;
log_assert(set);
prev = (struct resp_addr*)rbtree_previous((struct rbnode_type*)node);
lock_rw_destroy(&node->lock);
(void)rbtree_delete(&set->ip_tree, node);
@ -146,6 +148,7 @@ respip_find_or_create(struct respip_set* set, const char* ipstr, int create)
struct sockaddr_storage addr;
int net;
socklen_t addrlen;
log_assert(set);
if(!netblockstrtoaddr(ipstr, 0, &addr, &addrlen, &net)) {
log_err("cannot parse netblock: '%s'", ipstr);
@ -160,6 +163,7 @@ respip_tag_cfg(struct respip_set* set, const char* ipstr,
const uint8_t* taglist, size_t taglen)
{
struct resp_addr* node;
log_assert(set);
if(!(node=respip_find_or_create(set, ipstr, 1)))
return 0;
@ -183,6 +187,7 @@ respip_action_cfg(struct respip_set* set, const char* ipstr,
{
struct resp_addr* node;
enum respip_action action;
log_assert(set);
if(!(node=respip_find_or_create(set, ipstr, 1)))
return 0;
@ -325,6 +330,7 @@ static int
respip_data_cfg(struct respip_set* set, const char* ipstr, const char* rrstr)
{
struct resp_addr* node;
log_assert(set);
node=respip_find_or_create(set, ipstr, 0);
if(!node || node->action == respip_none) {
@ -344,6 +350,7 @@ respip_set_apply_cfg(struct respip_set* set, char* const* tagname, int num_tags,
struct config_strbytelist* p;
struct config_str2list* pa;
struct config_str2list* pd;
log_assert(set);
set->tagname = tagname;
set->num_tags = num_tags;
@ -609,6 +616,7 @@ respip_addr_lookup(const struct reply_info *rep, struct respip_set* rs,
struct resp_addr* ra;
struct sockaddr_storage ss;
socklen_t addrlen;
log_assert(rs);
lock_rw_rdlock(&rs->lock);
for(i=0; i<rep->an_numrrsets; i++) {
@ -867,7 +875,8 @@ respip_rewrite_reply(const struct query_info* qinfo,
const struct respip_client_info* cinfo, const struct reply_info* rep,
struct reply_info** new_repp, struct respip_action_info* actinfo,
struct ub_packed_rrset_key** alias_rrset, int search_only,
struct regional* region, struct auth_zones* az, int* rpz_passthru)
struct regional* region, struct auth_zones* az, int* rpz_passthru,
struct views* views, struct respip_set* ipset)
{
const uint8_t* ctaglist;
size_t ctaglen;
@ -876,7 +885,6 @@ respip_rewrite_reply(const struct query_info* qinfo,
struct config_strlist** tag_datas;
size_t tag_datas_size;
struct view* view = NULL;
struct respip_set* ipset = NULL;
size_t rrset_id = 0, rr_id = 0;
enum respip_action action = respip_none;
int tag = -1;
@ -899,8 +907,20 @@ respip_rewrite_reply(const struct query_info* qinfo,
tag_actions_size = cinfo->tag_actions_size;
tag_datas = cinfo->tag_datas;
tag_datas_size = cinfo->tag_datas_size;
view = cinfo->view;
ipset = cinfo->respip_set;
if(cinfo->view) {
view = cinfo->view;
lock_rw_rdlock(&view->lock);
} else if(cinfo->view_name) {
view = views_find_view(views, cinfo->view_name, 0);
if(!view) {
/* If the view no longer exists, the rewrite can not
* be processed further. */
verbose(VERB_ALGO, "respip: failed because view %s no "
"longer exists", cinfo->view_name);
return 0;
}
/* The view is rdlocked by views_find_view. */
}
log_assert(ipset);
@ -915,7 +935,6 @@ respip_rewrite_reply(const struct query_info* qinfo,
* Note also that we assume 'view' is valid in this function, which
* should be safe (see unbound bug #1191) */
if(view) {
lock_rw_rdlock(&view->lock);
if(view->respip_set) {
if((raddr = respip_addr_lookup(rep,
view->respip_set, &rrset_id, &rr_id))) {
@ -961,7 +980,7 @@ respip_rewrite_reply(const struct query_info* qinfo,
struct sockaddr_storage ss;
socklen_t ss_len = 0;
char nm[256], ip[256];
char qn[255+1];
char qn[LDNS_MAX_DOMAINLEN];
if(!rdata2sockaddr(rep->rrsets[rrset_id]->entry.data, ntohs(rep->rrsets[rrset_id]->rk.type), rr_id, &ss, &ss_len))
snprintf(ip, sizeof(ip), "invalidRRdata");
else
@ -1101,7 +1120,8 @@ respip_operate(struct module_qstate* qstate, enum module_ev event, int id,
qstate->client_info, qstate->return_msg->rep,
&new_rep, &actinfo, &alias_rrset, 0,
qstate->region, qstate->env->auth_zones,
&qstate->rpz_passthru)) {
&qstate->rpz_passthru, qstate->env->views,
qstate->env->respip_set)) {
goto servfail;
}
if(actinfo.action != respip_none) {
@ -1149,7 +1169,8 @@ respip_merge_cname(struct reply_info* base_rep,
const struct query_info* qinfo, const struct reply_info* tgt_rep,
const struct respip_client_info* cinfo, int must_validate,
struct reply_info** new_repp, struct regional* region,
struct auth_zones* az)
struct auth_zones* az, struct views* views,
struct respip_set* respip_set)
{
struct reply_info* new_rep;
struct reply_info* tmp_rep = NULL; /* just a placeholder */
@ -1176,7 +1197,7 @@ respip_merge_cname(struct reply_info* base_rep,
/* see if the target reply would be subject to a response-ip action. */
if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo,
&alias_rrset, 1, region, az, NULL))
&alias_rrset, 1, region, az, NULL, views, respip_set))
return 0;
if(actinfo.action != respip_none) {
log_info("CNAME target of redirect response-ip action would "
@ -1229,7 +1250,8 @@ respip_inform_super(struct module_qstate* qstate, int id,
if(!respip_merge_cname(super->return_msg->rep, &qstate->qinfo,
qstate->return_msg->rep, super->client_info,
super->env->need_to_validate, &new_rep, super->region,
qstate->env->auth_zones))
qstate->env->auth_zones, qstate->env->views,
qstate->env->respip_set))
goto fail;
super->return_msg->rep = new_rep;
return;
@ -1326,3 +1348,35 @@ respip_inform_print(struct respip_action_info* respip_actinfo, uint8_t* qname,
(actionstr) ? actionstr : "inform", srcip, port);
log_nametypeclass(NO_VERBOSE, txt, qname, qtype, qclass);
}
size_t respip_set_get_mem(struct respip_set* set)
{
size_t m;
if(!set) return 0;
m = sizeof(*set);
lock_rw_rdlock(&set->lock);
m += regional_get_mem(set->region);
lock_rw_unlock(&set->lock);
return m;
}
void
respip_set_swap_tree(struct respip_set* respip_set,
struct respip_set* data)
{
rbnode_type* oldroot = respip_set->ip_tree.root;
size_t oldcount = respip_set->ip_tree.count;
struct regional* oldregion = respip_set->region;
char* const* oldtagname = respip_set->tagname;
int oldnum_tags = respip_set->num_tags;
respip_set->ip_tree.root = data->ip_tree.root;
respip_set->ip_tree.count = data->ip_tree.count;
respip_set->region = data->region;
respip_set->tagname = data->tagname;
respip_set->num_tags = data->num_tags;
data->ip_tree.root = oldroot;
data->ip_tree.count = oldcount;
data->region = oldregion;
data->tagname = oldtagname;
data->num_tags = oldnum_tags;
}

View file

@ -23,7 +23,8 @@
struct respip_set {
struct regional* region;
struct rbtree_type ip_tree;
lock_rw_type lock; /* lock on the respip tree */
lock_rw_type lock; /* lock on the respip tree. It is ordered
after views and before hints, stubs and local zones. */
char* const* tagname; /* shallow copy of tag names, for logging */
int num_tags; /* number of tagname entries */
};
@ -59,7 +60,6 @@ struct respip_addr_info;
* This is essentially a subset of acl_addr (except for respip_set) but
* defined as a separate structure to avoid dependency on the daemon-specific
* structure.
* respip_set is supposed to refer to the response-ip set for the global view.
*/
struct respip_client_info {
uint8_t* taglist;
@ -68,8 +68,12 @@ struct respip_client_info {
size_t tag_actions_size;
struct config_strlist** tag_datas;
size_t tag_datas_size;
/** The view for the action, during cache callback that is by
* pointer. */
struct view* view;
struct respip_set* respip_set;
/** If from module query state, the view pointer is NULL, but the
* name is stored in reference to the view. */
char* view_name;
};
/**
@ -149,13 +153,16 @@ int respip_views_apply_cfg(struct views* vs, struct config_file* cfg,
* on error.
* @param region: allocator to build *new_repp.
* @param az: auth zones containing RPZ information.
* @param views: views tree to lookup view used.
* @param respip_set: the respip set for the global view.
* @return 1 on success, 0 on error.
*/
int respip_merge_cname(struct reply_info* base_rep,
const struct query_info* qinfo, const struct reply_info* tgt_rep,
const struct respip_client_info* cinfo, int must_validate,
struct reply_info** new_repp, struct regional* region,
struct auth_zones* az);
struct auth_zones* az, struct views* views,
struct respip_set* respip_set);
/**
* See if any IP-based action should apply to any IP address of AAAA/A answer
@ -178,6 +185,8 @@ int respip_merge_cname(struct reply_info* base_rep,
* @param region: allocator to build *new_repp.
* @param rpz_passthru: keeps track of query state can have passthru that
* stops further rpz processing. Or NULL for cached answer processing.
* @param views: views tree to lookup view used.
* @param ipset: the respip set for the global view.
* @return 1 on success, 0 on error.
*/
int respip_rewrite_reply(const struct query_info* qinfo,
@ -186,7 +195,7 @@ int respip_rewrite_reply(const struct query_info* qinfo,
struct respip_action_info* actinfo,
struct ub_packed_rrset_key** alias_rrset,
int search_only, struct regional* region, struct auth_zones* az,
int* rpz_passthru);
int* rpz_passthru, struct views* views, struct respip_set* ipset);
/**
* Get the response-ip function block.
@ -267,7 +276,7 @@ void respip_inform_print(struct respip_action_info* respip_actinfo,
* @param addrlen: length of addr.
* @param net: netblock to lookup.
* @param create: create node if it does not exist when 1.
* @param ipstr: human redable ip string, for logging.
* @param ipstr: human readable ip string, for logging.
* @return newly created of found node, not holding lock.
*/
struct resp_addr*
@ -302,4 +311,18 @@ respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node);
struct ub_packed_rrset_key*
respip_copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region);
/** Get memory usage of respip set tree. The routine locks and unlocks the
* set for reading. */
size_t respip_set_get_mem(struct respip_set* set);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param respip_set: response ip tree
* @param data: preallocated information.
*/
void respip_set_swap_tree(struct respip_set* respip_set,
struct respip_set* data);
#endif /* RESPIP_RESPIP_H */

View file

@ -1578,7 +1578,7 @@ auth_zone_read_zonefile(struct auth_zone* z, struct config_file* cfg)
cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
zfilename += strlen(cfg->chrootdir);
if(verbosity >= VERB_ALGO) {
char nm[255+1];
char nm[LDNS_MAX_DOMAINLEN];
dname_str(z->name, nm);
verbose(VERB_ALGO, "read zonefile %s for %s", zfilename, nm);
}
@ -1942,7 +1942,7 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z,
unsupported_reason = *reason;
/* continue to check for valid ZONEMD */
if(verbosity >= VERB_ALGO) {
char zstr[255+1];
char zstr[LDNS_MAX_DOMAINLEN];
dname_str(z->name, zstr);
verbose(VERB_ALGO, "auth-zone %s ZONEMD %d %d is unsupported: %s", zstr, (int)scheme, (int)hashalgo, *reason);
}
@ -1950,7 +1950,7 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z,
continue;
}
if(verbosity >= VERB_ALGO) {
char zstr[255+1];
char zstr[LDNS_MAX_DOMAINLEN];
dname_str(z->name, zstr);
if(!*reason)
verbose(VERB_ALGO, "auth-zone %s ZONEMD hash is correct", zstr);
@ -1973,7 +1973,7 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z,
if(!*reason)
*reason = "no ZONEMD records found";
if(verbosity >= VERB_ALGO) {
char zstr[255+1];
char zstr[LDNS_MAX_DOMAINLEN];
dname_str(z->name, zstr);
verbose(VERB_ALGO, "auth-zone %s ZONEMD failed: %s", zstr, *reason);
}
@ -2317,9 +2317,6 @@ auth_free_masters(struct auth_master* list)
}
}
/** delete auth xfer structure
* @param xfr: delete this xfer and its tasks.
*/
void
auth_xfer_delete(struct auth_xfer* xfr)
{
@ -2416,14 +2413,12 @@ az_find_wildcard(struct auth_zone* z, struct query_info* qinfo,
if(!dname_subdomain_c(nm, z->name))
return NULL; /* out of zone */
while((node=az_find_wildcard_domain(z, nm, nmlen))==NULL) {
/* see if we can go up to find the wildcard */
if(nmlen == z->namelen)
return NULL; /* top of zone reached */
if(ce && nmlen == ce->namelen)
return NULL; /* ce reached */
if(dname_is_root(nm))
return NULL; /* cannot go up */
dname_remove_label(&nm, &nmlen);
if(!dname_remove_label_limit_len(&nm, &nmlen, z->namelen))
return NULL; /* can't go up */
}
return node;
}
@ -2445,9 +2440,8 @@ az_find_candidate_ce(struct auth_zone* z, struct query_info* qinfo,
n = az_find_name(z, nm, nmlen);
/* delete labels and go up on name */
while(!n) {
if(dname_is_root(nm))
return NULL; /* cannot go up */
dname_remove_label(&nm, &nmlen);
if(!dname_remove_label_limit_len(&nm, &nmlen, z->namelen))
return NULL; /* can't go up */
n = az_find_name(z, nm, nmlen);
}
return n;
@ -2459,8 +2453,7 @@ az_domain_go_up(struct auth_zone* z, struct auth_data* n)
{
uint8_t* nm = n->name;
size_t nmlen = n->namelen;
while(!dname_is_root(nm)) {
dname_remove_label(&nm, &nmlen);
while(dname_remove_label_limit_len(&nm, &nmlen, z->namelen)) {
if((n=az_find_name(z, nm, nmlen)) != NULL)
return n;
}
@ -2774,26 +2767,23 @@ az_change_dnames(struct dns_msg* msg, uint8_t* oldname, uint8_t* newname,
}
}
/** find NSEC record covering the query */
/** find NSEC record covering the query, with the given node in the zone */
static struct auth_rrset*
az_find_nsec_cover(struct auth_zone* z, struct auth_data** node)
{
uint8_t* nm = (*node)->name;
size_t nmlen = (*node)->namelen;
uint8_t* nm;
size_t nmlen;
struct auth_rrset* rrset;
log_assert(*node); /* we already have a node when calling this */
nm = (*node)->name;
nmlen = (*node)->namelen;
/* find the NSEC for the smallest-or-equal node */
/* if node == NULL, we did not find a smaller name. But the zone
* name is the smallest name and should have an NSEC. So there is
* no NSEC to return (for a properly signed zone) */
/* for empty nonterminals, the auth-data node should not exist,
* and thus we don't need to go rbtree_previous here to find
* a domain with an NSEC record */
/* but there could be glue, and if this is node, then it has no NSEC.
/* But there could be glue, and then it has no NSEC.
* Go up to find nonglue (previous) NSEC-holding nodes */
while((rrset=az_domain_rrset(*node, LDNS_RR_TYPE_NSEC)) == NULL) {
if(dname_is_root(nm)) return NULL;
if(nmlen == z->namelen) return NULL;
dname_remove_label(&nm, &nmlen);
if(!dname_remove_label_limit_len(&nm, &nmlen, z->namelen))
return NULL; /* can't go up */
/* adjust *node for the nsec rrset to find in */
*node = az_find_name(z, nm, nmlen);
}
@ -3021,12 +3011,9 @@ az_nsec3_find_ce(struct auth_zone* z, uint8_t** cenm, size_t* cenmlen,
struct auth_data* node;
while((node = az_nsec3_find_exact(z, *cenm, *cenmlen,
algo, iter, salt, saltlen)) == NULL) {
if(*cenmlen == z->namelen) {
/* next step up would take us out of the zone. fail */
return NULL;
}
if(!dname_remove_label_limit_len(cenm, cenmlen, z->namelen))
return NULL; /* can't go up */
*no_exact_ce = 1;
dname_remove_label(cenm, cenmlen);
}
return node;
}
@ -3343,7 +3330,8 @@ az_generate_wildcard_answer(struct auth_zone* z, struct query_info* qinfo,
} else if(ce) {
uint8_t* wildup = wildcard->name;
size_t wilduplen= wildcard->namelen;
dname_remove_label(&wildup, &wilduplen);
if(!dname_remove_label_limit_len(&wildup, &wilduplen, z->namelen))
return 0; /* can't go up */
if(!az_add_nsec3_proof(z, region, msg, wildup,
wilduplen, msg->qinfo.qname,
msg->qinfo.qname_len, 0, insert_ce, 1, 0))
@ -3402,7 +3390,7 @@ az_generate_answer_with_node(struct auth_zone* z, struct query_info* qinfo,
}
/** Generate answer without an existing-node that we can use.
* So it'll be a referral, DNAME or nxdomain */
* So it'll be a referral, DNAME, notype, wildcard or nxdomain */
static int
az_generate_answer_nonexistnode(struct auth_zone* z, struct query_info* qinfo,
struct regional* region, struct dns_msg* msg, struct auth_data* ce,
@ -3568,14 +3556,17 @@ auth_error_encode(struct query_info* qinfo, struct module_env* env,
sldns_buffer_read_u16_at(buf, 2), edns);
}
int auth_zones_answer(struct auth_zones* az, struct module_env* env,
int auth_zones_downstream_answer(struct auth_zones* az, struct module_env* env,
struct query_info* qinfo, struct edns_data* edns,
struct comm_reply* repinfo, struct sldns_buffer* buf, struct regional* temp)
struct comm_reply* repinfo, struct sldns_buffer* buf,
struct regional* temp)
{
struct dns_msg* msg = NULL;
struct auth_zone* z;
int r;
int fallback = 0;
/* Copy the qinfo in case of cname aliasing from local-zone */
struct query_info zqinfo = *qinfo;
lock_rw_rdlock(&az->lock);
if(!az->have_downstream) {
@ -3583,6 +3574,7 @@ int auth_zones_answer(struct auth_zones* az, struct module_env* env,
lock_rw_unlock(&az->lock);
return 0;
}
if(qinfo->qtype == LDNS_RR_TYPE_DS) {
uint8_t* delname = qinfo->qname;
size_t delnamelen = qinfo->qname_len;
@ -3590,8 +3582,14 @@ int auth_zones_answer(struct auth_zones* az, struct module_env* env,
z = auth_zones_find_zone(az, delname, delnamelen,
qinfo->qclass);
} else {
z = auth_zones_find_zone(az, qinfo->qname, qinfo->qname_len,
qinfo->qclass);
if(zqinfo.local_alias && !local_alias_shallow_copy_qname(
zqinfo.local_alias, &zqinfo.qname,
&zqinfo.qname_len)) {
lock_rw_unlock(&az->lock);
return 0;
}
z = auth_zones_find_zone(az, zqinfo.qname, zqinfo.qname_len,
zqinfo.qclass);
}
if(!z) {
/* no zone above it */
@ -3610,24 +3608,20 @@ int auth_zones_answer(struct auth_zones* az, struct module_env* env,
return 0;
}
lock_rw_unlock(&z->lock);
lock_rw_wrlock(&az->lock);
az->num_query_down++;
lock_rw_unlock(&az->lock);
env->mesh->num_query_authzone_down++;
auth_error_encode(qinfo, env, edns, repinfo, buf, temp,
LDNS_RCODE_SERVFAIL);
return 1;
}
/* answer it from zone z */
r = auth_zone_generate_answer(z, qinfo, temp, &msg, &fallback);
r = auth_zone_generate_answer(z, &zqinfo, temp, &msg, &fallback);
lock_rw_unlock(&z->lock);
if(!r && fallback) {
/* fallback to regular answering (recursive) */
return 0;
}
lock_rw_wrlock(&az->lock);
az->num_query_down++;
lock_rw_unlock(&az->lock);
env->mesh->num_query_authzone_down++;
/* encode answer */
if(!r)
@ -4803,8 +4797,8 @@ log_rrlist_position(const char* label, struct auth_chunk* rr_chunk,
{
sldns_buffer pkt;
size_t dlen;
uint8_t buf[256];
char str[256];
uint8_t buf[LDNS_MAX_DOMAINLEN];
char str[LDNS_MAX_DOMAINLEN];
char typestr[32];
sldns_buffer_init_frm_data(&pkt, rr_chunk->data, rr_chunk->len);
sldns_buffer_set_position(&pkt, (size_t)(rr_dname -
@ -5030,6 +5024,7 @@ apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
xfr->have_zone = 0;
xfr->serial = 0;
xfr->soa_zone_acquired = 0;
/* insert all RRs in to the zone */
/* insert the SOA only once, skip the last one */
@ -5131,6 +5126,7 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,
xfr->have_zone = 0;
xfr->serial = 0;
xfr->soa_zone_acquired = 0;
chunk = xfr->task_transfer->chunks_first;
chunk_pos = 0;
@ -5231,7 +5227,7 @@ xfr_write_after_update(struct auth_xfer* xfr, struct module_env* env)
cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
zfilename += strlen(cfg->chrootdir);
if(verbosity >= VERB_ALGO) {
char nm[255+1];
char nm[LDNS_MAX_DOMAINLEN];
dname_str(z->name, nm);
verbose(VERB_ALGO, "write zonefile %s for %s", zfilename, nm);
}
@ -5341,6 +5337,8 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env,
" (or malformed RR)", xfr->task_transfer->master->host);
return 0;
}
z->soa_zone_acquired = *env->now;
xfr->soa_zone_acquired = *env->now;
/* release xfr lock while verifying zonemd because it may have
* to spawn lookups in the state machines */
@ -5348,7 +5346,7 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env,
/* holding z lock */
auth_zone_verify_zonemd(z, env, &env->mesh->mods, NULL, 0, 0);
if(z->zone_expired) {
char zname[256];
char zname[LDNS_MAX_DOMAINLEN];
/* ZONEMD must have failed */
/* reacquire locks, so we hold xfr lock on exit of routine,
* and both xfr and z again after releasing xfr for potential
@ -5380,7 +5378,7 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env,
lock_rw_unlock(&z->lock);
if(verbosity >= VERB_QUERY && xfr->have_zone) {
char zname[256];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_QUERY, "auth zone %s updated to serial %u", zname,
(unsigned)xfr->serial);
@ -5442,7 +5440,7 @@ xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env)
qinfo.local_alias = NULL;
if(verbosity >= VERB_ALGO) {
char buf1[512];
char buf2[LDNS_MAX_DOMAINLEN+1];
char buf2[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, buf2);
snprintf(buf1, sizeof(buf1), "auth zone %s: master lookup"
" for task_transfer", buf2);
@ -5498,7 +5496,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
/* the ones that are not in addr format are supposed
* to be looked up. The lookup has failed however,
* so skip them */
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
log_err("%s: failed lookup, cannot transfer from master %s",
zname, master->host);
@ -5537,7 +5535,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
&addr, addrlen, -1, master->ssl, master->host,
master->file, env->cfg);
if(!xfr->task_transfer->cp) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "cannot create http cp "
@ -5546,7 +5544,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
}
comm_timer_set(xfr->task_transfer->timer, &t);
if(verbosity >= VERB_ALGO) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "auth zone %s transfer next HTTP fetch from %s started", zname, as);
@ -5569,7 +5567,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
env->scratch_buffer, -1,
auth_name != NULL, auth_name);
if(!xfr->task_transfer->cp) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "cannot create tcp cp connection for "
@ -5578,7 +5576,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
}
comm_timer_set(xfr->task_transfer->timer, &t);
if(verbosity >= VERB_ALGO) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "auth zone %s transfer next %s fetch from %s started", zname,
@ -5602,7 +5600,7 @@ xfr_transfer_nexttarget_or_end(struct auth_xfer* xfr, struct module_env* env)
* and that calls the callback just like a full
* lookup and lookup failures also call callback */
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s transfer next target lookup", zname);
}
@ -5625,7 +5623,7 @@ xfr_transfer_nexttarget_or_end(struct auth_xfer* xfr, struct module_env* env)
xfr_transfer_nextmaster(xfr);
}
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s transfer failed, wait", zname);
}
@ -5728,14 +5726,14 @@ void auth_xfer_transfer_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
lookup_target, answer, wanted_qtype);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s transfer lookup has nodata", zname, xfr->task_transfer->lookup_target->host, (xfr->task_transfer->lookup_aaaa?"AAAA":"A"));
}
}
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s transfer lookup has no answer", zname, xfr->task_transfer->lookup_target->host, (xfr->task_transfer->lookup_aaaa?"AAAA":"A"));
}
@ -5743,7 +5741,7 @@ void auth_xfer_transfer_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
regional_free_all(temp);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s transfer lookup failed", zname, xfr->task_transfer->lookup_target->host, (xfr->task_transfer->lookup_aaaa?"AAAA":"A"));
}
@ -6385,7 +6383,7 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env,
/* the ones that are not in addr format are supposed
* to be looked up. The lookup has failed however,
* so skip them */
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
log_err("%s: failed lookup, cannot probe to master %s",
zname, master->host);
@ -6427,7 +6425,7 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env,
xfr->task_probe->cp = outnet_comm_point_for_udp(env->outnet,
auth_xfer_probe_udp_callback, xfr, &addr, addrlen);
if(!xfr->task_probe->cp) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "cannot create udp cp for "
@ -6447,7 +6445,7 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env,
/* send udp packet */
if(!comm_point_send_udp_msg(xfr->task_probe->cp, env->scratch_buffer,
(struct sockaddr*)&addr, addrlen, 0)) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "failed to send soa probe for %s to %s",
@ -6455,7 +6453,7 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env,
return 0;
}
if(verbosity >= VERB_ALGO) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "auth zone %s soa probe sent to %s", zname,
@ -6486,7 +6484,7 @@ auth_xfer_probe_timer_callback(void* arg)
}
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s soa probe timeout", zname);
}
@ -6534,7 +6532,7 @@ auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err,
&serial)) {
/* successful lookup */
if(verbosity >= VERB_ALGO) {
char buf[256];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, buf);
verbose(VERB_ALGO, "auth zone %s: soa probe "
"serial is %u", buf, (unsigned)serial);
@ -6573,14 +6571,14 @@ auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err,
}
} else {
if(verbosity >= VERB_ALGO) {
char buf[256];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, buf);
verbose(VERB_ALGO, "auth zone %s: bad reply to soa probe", buf);
}
}
} else {
if(verbosity >= VERB_ALGO) {
char buf[256];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, buf);
verbose(VERB_ALGO, "auth zone %s: soa probe failed", buf);
}
@ -6637,7 +6635,7 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env)
qinfo.local_alias = NULL;
if(verbosity >= VERB_ALGO) {
char buf1[512];
char buf2[LDNS_MAX_DOMAINLEN+1];
char buf2[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, buf2);
snprintf(buf1, sizeof(buf1), "auth zone %s: master lookup"
" for task_probe", buf2);
@ -6683,7 +6681,7 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)
* and that calls the callback just like a full
* lookup and lookup failures also call callback */
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s probe next target lookup", zname);
}
@ -6696,7 +6694,7 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)
* allow_notify addrs */
probe_copy_masters_for_allow_notify(xfr);
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s probe: notify addrs updated", zname);
}
@ -6704,7 +6702,7 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)
/* only wanted lookups for copy, stop probe and start wait */
xfr->task_probe->only_lookup = 0;
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s probe: finished only_lookup", zname);
}
@ -6730,7 +6728,7 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)
if(xfr->task_probe->have_new_lease) {
/* if zone not updated, start the wait timer again */
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth_zone %s unchanged, new lease, wait", zname);
}
@ -6741,7 +6739,7 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)
xfr_set_timeout(xfr, env, 0, 0);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s soa probe failed, wait to retry", zname);
}
@ -6791,14 +6789,14 @@ void auth_xfer_probe_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
lookup_target, answer, wanted_qtype);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s probe lookup has nodata", zname, xfr->task_probe->lookup_target->host, (xfr->task_probe->lookup_aaaa?"AAAA":"A"));
}
}
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s probe lookup has no address", zname, xfr->task_probe->lookup_target->host, (xfr->task_probe->lookup_aaaa?"AAAA":"A"));
}
@ -6806,7 +6804,7 @@ void auth_xfer_probe_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
regional_free_all(temp);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s probe lookup failed", zname, xfr->task_probe->lookup_target->host, (xfr->task_probe->lookup_aaaa?"AAAA":"A"));
}
@ -6980,7 +6978,7 @@ xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
if(!xfr->task_nextprobe->timer) {
/* failed to malloc memory. likely zone transfer
* also fails for that. skip the timeout */
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
log_err("cannot allocate timer, no refresh for %s",
zname);
@ -7001,7 +6999,7 @@ xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
xfr->task_probe->only_lookup = 1;
}
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s timeout in %d seconds",
zname, (int)tv.tv_sec);
@ -7010,22 +7008,43 @@ xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
comm_timer_set(xfr->task_nextprobe->timer, &tv);
}
void auth_zone_pickup_initial_zone(struct auth_zone* z, struct module_env* env)
{
/* Set the time, because we now have timestamp in env,
* (not earlier during startup and apply_cfg), and this
* notes the start time when the data was acquired. */
z->soa_zone_acquired = *env->now;
}
void auth_xfer_pickup_initial_zone(struct auth_xfer* x, struct module_env* env)
{
/* set lease_time, because we now have timestamp in env,
* (not earlier during startup and apply_cfg), and this
* notes the start time when the data was acquired */
if(x->have_zone) {
x->lease_time = *env->now;
x->soa_zone_acquired = *env->now;
}
if(x->task_nextprobe && x->task_nextprobe->worker == NULL) {
xfr_set_timeout(x, env, 0, 1);
}
}
/** initial pick up of worker timeouts, ties events to worker event loop */
void
auth_xfer_pickup_initial(struct auth_zones* az, struct module_env* env)
{
struct auth_xfer* x;
struct auth_zone* z;
lock_rw_wrlock(&az->lock);
RBTREE_FOR(z, struct auth_zone*, &az->ztree) {
lock_rw_wrlock(&z->lock);
auth_zone_pickup_initial_zone(z, env);
lock_rw_unlock(&z->lock);
}
RBTREE_FOR(x, struct auth_xfer*, &az->xtree) {
lock_basic_lock(&x->lock);
/* set lease_time, because we now have timestamp in env,
* (not earlier during startup and apply_cfg), and this
* notes the start time when the data was acquired */
if(x->have_zone)
x->lease_time = *env->now;
if(x->task_nextprobe && x->task_nextprobe->worker == NULL) {
xfr_set_timeout(x, env, 0, 1);
}
auth_xfer_pickup_initial_zone(x, env);
lock_basic_unlock(&x->lock);
}
lock_rw_unlock(&az->lock);
@ -7107,6 +7126,7 @@ auth_xfer_new(struct auth_zone* z)
lock_protect(&xfr->lock, &xfr->notify_serial, sizeof(xfr->notify_serial));
lock_protect(&xfr->lock, &xfr->zone_expired, sizeof(xfr->zone_expired));
lock_protect(&xfr->lock, &xfr->have_zone, sizeof(xfr->have_zone));
lock_protect(&xfr->lock, &xfr->soa_zone_acquired, sizeof(xfr->soa_zone_acquired));
lock_protect(&xfr->lock, &xfr->serial, sizeof(xfr->serial));
lock_protect(&xfr->lock, &xfr->retry, sizeof(xfr->retry));
lock_protect(&xfr->lock, &xfr->refresh, sizeof(xfr->refresh));
@ -7788,7 +7808,7 @@ static void auth_zone_log(uint8_t* name, enum verbosity_value level,
va_list args;
va_start(args, format);
if(verbosity >= level) {
char str[255+1];
char str[LDNS_MAX_DOMAINLEN];
char msg[MAXSYSLOGMSGLEN];
dname_str(name, str);
vsnprintf(msg, sizeof(msg), format, args);
@ -7990,7 +8010,7 @@ static int zonemd_check_dnssec_soazonemd(struct auth_zone* z,
static void auth_zone_zonemd_fail(struct auth_zone* z, struct module_env* env,
char* reason, char* why_bogus, char** result)
{
char zstr[255+1];
char zstr[LDNS_MAX_DOMAINLEN];
/* if fail: log reason, and depending on config also take action
* and drop the zone, eg. it is gone from memory, set zone_expired */
dname_str(z->name, zstr);
@ -8436,7 +8456,7 @@ zonemd_lookup_dnskey(struct auth_zone* z, struct module_env* env)
qinfo.local_alias = NULL;
if(verbosity >= VERB_ALGO) {
char buf1[512];
char buf2[LDNS_MAX_DOMAINLEN+1];
char buf2[LDNS_MAX_DOMAINLEN];
dname_str(z->name, buf2);
snprintf(buf1, sizeof(buf1), "auth zone %s: lookup %s "
"for zonemd verification", buf2,
@ -8584,3 +8604,161 @@ void auth_zones_pickup_zonemd_verify(struct auth_zones* az,
}
lock_rw_unlock(&az->lock);
}
/** Get memory usage of auth rrset */
static size_t
auth_rrset_get_mem(struct auth_rrset* rrset)
{
size_t m = sizeof(*rrset) + packed_rrset_sizeof(rrset->data);
return m;
}
/** Get memory usage of auth data */
static size_t
auth_data_get_mem(struct auth_data* node)
{
size_t m = sizeof(*node) + node->namelen;
struct auth_rrset* rrset;
for(rrset = node->rrsets; rrset; rrset = rrset->next) {
m += auth_rrset_get_mem(rrset);
}
return m;
}
/** Get memory usage of auth zone */
static size_t
auth_zone_get_mem(struct auth_zone* z)
{
size_t m = sizeof(*z) + z->namelen;
struct auth_data* node;
if(z->zonefile)
m += strlen(z->zonefile)+1;
RBTREE_FOR(node, struct auth_data*, &z->data) {
m += auth_data_get_mem(node);
}
if(z->rpz)
m += rpz_get_mem(z->rpz);
return m;
}
/** Get memory usage of list of auth addr */
static size_t
auth_addrs_get_mem(struct auth_addr* list)
{
size_t m = 0;
struct auth_addr* a;
for(a = list; a; a = a->next) {
m += sizeof(*a);
}
return m;
}
/** Get memory usage of list of primaries for auth xfer */
static size_t
auth_primaries_get_mem(struct auth_master* list)
{
size_t m = 0;
struct auth_master* n;
for(n = list; n; n = n->next) {
m += sizeof(*n);
m += auth_addrs_get_mem(n->list);
if(n->host)
m += strlen(n->host)+1;
if(n->file)
m += strlen(n->file)+1;
}
return m;
}
/** Get memory usage or list of auth chunks */
static size_t
auth_chunks_get_mem(struct auth_chunk* list)
{
size_t m = 0;
struct auth_chunk* chunk;
for(chunk = list; chunk; chunk = chunk->next) {
m += sizeof(*chunk) + chunk->len;
}
return m;
}
/** Get memory usage of auth xfer */
static size_t
auth_xfer_get_mem(struct auth_xfer* xfr)
{
size_t m = sizeof(*xfr) + xfr->namelen;
/* auth_nextprobe */
m += comm_timer_get_mem(xfr->task_nextprobe->timer);
/* auth_probe */
m += auth_primaries_get_mem(xfr->task_probe->masters);
m += comm_point_get_mem(xfr->task_probe->cp);
m += comm_timer_get_mem(xfr->task_probe->timer);
/* auth_transfer */
m += auth_chunks_get_mem(xfr->task_transfer->chunks_first);
m += auth_primaries_get_mem(xfr->task_transfer->masters);
m += comm_point_get_mem(xfr->task_transfer->cp);
m += comm_timer_get_mem(xfr->task_transfer->timer);
/* allow_notify_list */
m += auth_primaries_get_mem(xfr->allow_notify_list);
return m;
}
/** Get memory usage of auth zones ztree */
static size_t
az_ztree_get_mem(struct auth_zones* az)
{
size_t m = 0;
struct auth_zone* z;
RBTREE_FOR(z, struct auth_zone*, &az->ztree) {
lock_rw_rdlock(&z->lock);
m += auth_zone_get_mem(z);
lock_rw_unlock(&z->lock);
}
return m;
}
/** Get memory usage of auth zones xtree */
static size_t
az_xtree_get_mem(struct auth_zones* az)
{
size_t m = 0;
struct auth_xfer* xfr;
RBTREE_FOR(xfr, struct auth_xfer*, &az->xtree) {
lock_basic_lock(&xfr->lock);
m += auth_xfer_get_mem(xfr);
lock_basic_unlock(&xfr->lock);
}
return m;
}
size_t auth_zones_get_mem(struct auth_zones* zones)
{
size_t m;
if(!zones) return 0;
m = sizeof(*zones);
lock_rw_rdlock(&zones->rpz_lock);
lock_rw_rdlock(&zones->lock);
m += az_ztree_get_mem(zones);
m += az_xtree_get_mem(zones);
lock_rw_unlock(&zones->lock);
lock_rw_unlock(&zones->rpz_lock);
return m;
}
void xfr_disown_tasks(struct auth_xfer* xfr, struct worker* worker)
{
if(xfr->task_nextprobe->worker == worker) {
xfr_nextprobe_disown(xfr);
}
if(xfr->task_probe->worker == worker) {
xfr_probe_disown(xfr);
}
if(xfr->task_transfer->worker == worker) {
xfr_transfer_disown(xfr);
}
}

View file

@ -70,7 +70,8 @@ struct auth_chunk;
* Authoritative zones, shared.
*/
struct auth_zones {
/** lock on the authzone trees */
/** lock on the authzone trees. It is locked after views, respip,
* local_zones and before fwds and stubs. */
lock_rw_type lock;
/** rbtree of struct auth_zone */
rbtree_type ztree;
@ -78,10 +79,6 @@ struct auth_zones {
rbtree_type xtree;
/** do we have downstream enabled */
int have_downstream;
/** number of queries upstream */
size_t num_query_up;
/** number of queries downstream */
size_t num_query_down;
/** first auth zone containing rpz item in linked list */
struct auth_zone* rpz_first;
/** rw lock for rpz linked list, needed when iterating or editing linked
@ -121,6 +118,8 @@ struct auth_zone {
char* zonefile;
/** fallback to the internet on failure or ttl-expiry of auth zone */
int fallback_enabled;
/** the time when zone was transferred from upstream */
time_t soa_zone_acquired;
/** the zone has expired (enabled by the xfer worker), fallback
* happens if that option is enabled. */
int zone_expired;
@ -211,7 +210,9 @@ struct auth_xfer {
* one of the tasks.
* Once it has the task assigned to it, the worker can access the
* other elements of the task structure without a lock, because that
* is necessary for the eventloop and callbacks from that. */
* is necessary for the eventloop and callbacks from that.
* The auth_zone->lock is locked before this lock.
*/
lock_basic_type lock;
/** zone name, in uncompressed wireformat */
@ -262,6 +263,8 @@ struct auth_xfer {
int zone_expired;
/** do we have a zone (if 0, no zone data at all) */
int have_zone;
/** the time when zone was transferred from upstream */
time_t soa_zone_acquired;
/** current serial (from SOA), if we have no zone, 0 */
uint32_t serial;
@ -551,9 +554,10 @@ int auth_zones_lookup(struct auth_zones* az, struct query_info* qinfo,
* @param temp: temporary storage region.
* @return false if not answered
*/
int auth_zones_answer(struct auth_zones* az, struct module_env* env,
int auth_zones_downstream_answer(struct auth_zones* az, struct module_env* env,
struct query_info* qinfo, struct edns_data* edns,
struct comm_reply* repinfo, struct sldns_buffer* buf, struct regional* temp);
struct comm_reply* repinfo, struct sldns_buffer* buf,
struct regional* temp);
/**
* Find the auth zone that is above the given qname.
@ -787,4 +791,41 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode,
void auth_zones_pickup_zonemd_verify(struct auth_zones* az,
struct module_env* env);
/** Get memory usage for auth zones. The routine locks and unlocks
* for reading. */
size_t auth_zones_get_mem(struct auth_zones* zones);
/**
* Initial pick up of the auth zone nextprobe timeout and that turns
* into further zone transfer work, if any. Also sets the lease time.
* @param x: xfer structure, locked by caller.
* @param env: environment of the worker that picks up the task.
*/
void auth_xfer_pickup_initial_zone(struct auth_xfer* x,
struct module_env* env);
/**
* Initial pick up of the auth zone, it sets the acquired time.
* @param z: the zone, write locked by caller.
* @param env: environment of the worker, with current time.
*/
void auth_zone_pickup_initial_zone(struct auth_zone* z,
struct module_env* env);
/**
* Delete auth xfer structure
* @param xfr: delete this xfer and its tasks.
*/
void auth_xfer_delete(struct auth_xfer* xfr);
/**
* Disown tasks from the xfr that belong to this worker.
* Only tasks for the worker in question, the comm point and timer
* delete functions need to run in the thread of that worker to be
* able to delete the callback from the event base.
* @param xfr: xfr structure
* @param worker: the worker for which to stop tasks.
*/
void xfr_disown_tasks(struct auth_xfer* xfr, struct worker* worker);
#endif /* SERVICES_AUTHZONE_H */

41
services/cache/dns.c vendored
View file

@ -1056,7 +1056,7 @@ dns_cache_lookup(struct module_env* env,
int
dns_cache_store(struct module_env* env, struct query_info* msgqinf,
struct reply_info* msgrep, int is_referral, time_t leeway, int pside,
struct reply_info* msgrep, int is_referral, time_t leeway, int pside,
struct regional* region, uint32_t flags, time_t qstarttime,
int is_valrec)
{
@ -1066,7 +1066,7 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
* useful expired record exists. */
struct msgreply_entry* e = msg_cache_lookup(env,
msgqinf->qname, msgqinf->qname_len, msgqinf->qtype,
msgqinf->qclass, flags, 0, 0);
msgqinf->qclass, flags, 0, 1);
if(e) {
struct reply_info* cached = e->entry.data;
if(cached->ttl < *env->now
@ -1081,7 +1081,42 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
&& cached->security != sec_status_bogus
&& (env->need_to_validate &&
msgrep->security == sec_status_unchecked)
&& !is_valrec) {
/* Exceptions to that rule are:
* o recursions that don't need validation but
* need to update the cache for coherence
* (delegation information while iterating,
* DNSKEY and DS lookups from validator)
* o explicit RRSIG queries that are not
* validated. */
&& !is_valrec
&& msgqinf->qtype != LDNS_RR_TYPE_RRSIG) {
if((int)FLAGS_GET_RCODE(msgrep->flags) !=
LDNS_RCODE_NOERROR &&
(int)FLAGS_GET_RCODE(msgrep->flags) !=
LDNS_RCODE_NXDOMAIN) {
/* The current response has an
* erroneous rcode. Adjust norec time
* so that additional lookups are not
* performed for some time. */
verbose(VERB_ALGO, "set "
"serve-expired-norec-ttl for "
"response in cache");
cached->serve_expired_norec_ttl =
NORR_TTL + *env->now;
if(env->cfg->serve_expired_ttl_reset &&
cached->serve_expired_ttl
< *env->now +
env->cfg->serve_expired_ttl) {
/* Reset serve-expired-ttl for
* valid response in cache. */
verbose(VERB_ALGO, "reset "
"serve-expired-ttl "
"for response in cache");
cached->serve_expired_ttl =
*env->now +
env->cfg->serve_expired_ttl;
}
}
verbose(VERB_ALGO, "a validated expired entry "
"could be overwritten, skip caching "
"the new message at this stage");

170
services/cache/infra.c vendored
View file

@ -52,24 +52,6 @@
#include "util/config_file.h"
#include "iterator/iterator.h"
/** Timeout when only a single probe query per IP is allowed. */
#define PROBE_MAXRTO 12000 /* in msec */
/** number of timeouts for a type when the domain can be blocked ;
* even if another type has completely rtt maxed it, the different type
* can do this number of packets (until those all timeout too) */
#define TIMEOUT_COUNT_MAX 3
/** Minus 1000 because that is outside of the RTTBAND, so
* blacklisted servers stay blacklisted if this is chosen.
* If USEFUL_SERVER_TOP_TIMEOUT is below 1000 (configured via RTT_MAX_TIMEOUT,
* infra-cache-max-rtt) change it to just above the RTT_BAND. */
#define STILL_USEFUL_TIMEOUT ( \
USEFUL_SERVER_TOP_TIMEOUT < 1000 || \
USEFUL_SERVER_TOP_TIMEOUT - 1000 <= RTT_BAND \
?RTT_BAND + 1 \
:USEFUL_SERVER_TOP_TIMEOUT - 1000)
/** ratelimit value for delegation point */
int infra_dp_ratelimit = 0;
@ -82,6 +64,20 @@ int infra_ip_ratelimit = 0;
* For clients with a valid DNS Cookie. */
int infra_ip_ratelimit_cookie = 0;
/** Minus 1000 because that is outside of the RTTBAND, so
* blacklisted servers stay blacklisted if this is chosen.
* If USEFUL_SERVER_TOP_TIMEOUT is below 1000 (configured via RTT_MAX_TIMEOUT,
* infra-cache-max-rtt) change it to just above the RTT_BAND. */
int
still_useful_timeout()
{
return
USEFUL_SERVER_TOP_TIMEOUT < 1000 ||
USEFUL_SERVER_TOP_TIMEOUT - 1000 <= RTT_BAND
?RTT_BAND + 1
:USEFUL_SERVER_TOP_TIMEOUT - 1000;
}
size_t
infra_sizefunc(void* k, void* ATTR_UNUSED(d))
{
@ -165,7 +161,7 @@ rate_deldatafunc(void* d, void* ATTR_UNUSED(arg))
/** find or create element in domainlimit tree */
static struct domain_limit_data* domain_limit_findcreate(
struct infra_cache* infra, char* name)
struct rbtree_type* domain_limits, char* name)
{
uint8_t* nm;
int labs;
@ -181,8 +177,8 @@ static struct domain_limit_data* domain_limit_findcreate(
labs = dname_count_labels(nm);
/* can we find it? */
d = (struct domain_limit_data*)name_tree_find(&infra->domain_limits,
nm, nmlen, labs, LDNS_RR_CLASS_IN);
d = (struct domain_limit_data*)name_tree_find(domain_limits, nm,
nmlen, labs, LDNS_RR_CLASS_IN);
if(d) {
free(nm);
return d;
@ -201,8 +197,8 @@ static struct domain_limit_data* domain_limit_findcreate(
d->node.dclass = LDNS_RR_CLASS_IN;
d->lim = -1;
d->below = -1;
if(!name_tree_insert(&infra->domain_limits, &d->node, nm, nmlen,
labs, LDNS_RR_CLASS_IN)) {
if(!name_tree_insert(domain_limits, &d->node, nm, nmlen, labs,
LDNS_RR_CLASS_IN)) {
log_err("duplicate element in domainlimit tree");
free(nm);
free(d);
@ -212,19 +208,19 @@ static struct domain_limit_data* domain_limit_findcreate(
}
/** insert rate limit configuration into lookup tree */
static int infra_ratelimit_cfg_insert(struct infra_cache* infra,
static int infra_ratelimit_cfg_insert(struct rbtree_type* domain_limits,
struct config_file* cfg)
{
struct config_str2list* p;
struct domain_limit_data* d;
for(p = cfg->ratelimit_for_domain; p; p = p->next) {
d = domain_limit_findcreate(infra, p->str);
d = domain_limit_findcreate(domain_limits, p->str);
if(!d)
return 0;
d->lim = atoi(p->str2);
}
for(p = cfg->ratelimit_below_domain; p; p = p->next) {
d = domain_limit_findcreate(infra, p->str);
d = domain_limit_findcreate(domain_limits, p->str);
if(!d)
return 0;
d->below = atoi(p->str2);
@ -232,24 +228,21 @@ static int infra_ratelimit_cfg_insert(struct infra_cache* infra,
return 1;
}
/** setup domain limits tree (0 on failure) */
static int
setup_domain_limits(struct infra_cache* infra, struct config_file* cfg)
int
setup_domain_limits(struct rbtree_type* domain_limits, struct config_file* cfg)
{
name_tree_init(&infra->domain_limits);
if(!infra_ratelimit_cfg_insert(infra, cfg)) {
name_tree_init(domain_limits);
if(!infra_ratelimit_cfg_insert(domain_limits, cfg)) {
return 0;
}
name_tree_init_parents(&infra->domain_limits);
name_tree_init_parents(domain_limits);
return 1;
}
/** find or create element in wait limit netblock tree */
static struct wait_limit_netblock_info*
wait_limit_netblock_findcreate(struct infra_cache* infra, char* str,
int cookie)
wait_limit_netblock_findcreate(struct rbtree_type* tree, char* str)
{
rbtree_type* tree;
struct sockaddr_storage addr;
int net;
socklen_t addrlen;
@ -261,10 +254,6 @@ wait_limit_netblock_findcreate(struct infra_cache* infra, char* str,
}
/* can we find it? */
if(cookie)
tree = &infra->wait_limits_cookie_netblock;
else
tree = &infra->wait_limits_netblock;
d = (struct wait_limit_netblock_info*)addr_tree_find(tree, &addr,
addrlen, net);
if(d)
@ -286,19 +275,21 @@ wait_limit_netblock_findcreate(struct infra_cache* infra, char* str,
/** insert wait limit information into lookup tree */
static int
infra_wait_limit_netblock_insert(struct infra_cache* infra,
struct config_file* cfg)
infra_wait_limit_netblock_insert(rbtree_type* wait_limits_netblock,
rbtree_type* wait_limits_cookie_netblock, struct config_file* cfg)
{
struct config_str2list* p;
struct wait_limit_netblock_info* d;
for(p = cfg->wait_limit_netblock; p; p = p->next) {
d = wait_limit_netblock_findcreate(infra, p->str, 0);
d = wait_limit_netblock_findcreate(wait_limits_netblock,
p->str);
if(!d)
return 0;
d->limit = atoi(p->str2);
}
for(p = cfg->wait_limit_cookie_netblock; p; p = p->next) {
d = wait_limit_netblock_findcreate(infra, p->str, 1);
d = wait_limit_netblock_findcreate(wait_limits_cookie_netblock,
p->str);
if(!d)
return 0;
d->limit = atoi(p->str2);
@ -306,16 +297,48 @@ infra_wait_limit_netblock_insert(struct infra_cache* infra,
return 1;
}
/** setup wait limits tree (0 on failure) */
/** Add a default wait limit netblock */
static int
setup_wait_limits(struct infra_cache* infra, struct config_file* cfg)
wait_limit_netblock_default(struct rbtree_type* tree, char* str, int limit)
{
addr_tree_init(&infra->wait_limits_netblock);
addr_tree_init(&infra->wait_limits_cookie_netblock);
if(!infra_wait_limit_netblock_insert(infra, cfg))
struct wait_limit_netblock_info* d;
d = wait_limit_netblock_findcreate(tree, str);
if(!d)
return 0;
addr_tree_init_parents(&infra->wait_limits_netblock);
addr_tree_init_parents(&infra->wait_limits_cookie_netblock);
d->limit = limit;
return 1;
}
int
setup_wait_limits(rbtree_type* wait_limits_netblock,
rbtree_type* wait_limits_cookie_netblock, struct config_file* cfg)
{
addr_tree_init(wait_limits_netblock);
addr_tree_init(wait_limits_cookie_netblock);
/* Insert defaults */
/* The loopback address is separated from the rest of the network. */
/* wait-limit-netblock: 127.0.0.0/8 -1 */
if(!wait_limit_netblock_default(wait_limits_netblock, "127.0.0.0/8",
-1))
return 0;
/* wait-limit-netblock: ::1/128 -1 */
if(!wait_limit_netblock_default(wait_limits_netblock, "::1/128", -1))
return 0;
/* wait-limit-cookie-netblock: 127.0.0.0/8 -1 */
if(!wait_limit_netblock_default(wait_limits_cookie_netblock,
"127.0.0.0/8", -1))
return 0;
/* wait-limit-cookie-netblock: ::1/128 -1 */
if(!wait_limit_netblock_default(wait_limits_cookie_netblock,
"::1/128", -1))
return 0;
if(!infra_wait_limit_netblock_insert(wait_limits_netblock,
wait_limits_cookie_netblock, cfg))
return 0;
addr_tree_init_parents(wait_limits_netblock);
addr_tree_init_parents(wait_limits_cookie_netblock);
return 1;
}
@ -348,11 +371,12 @@ infra_create(struct config_file* cfg)
return NULL;
}
/* insert config data into ratelimits */
if(!setup_domain_limits(infra, cfg)) {
if(!setup_domain_limits(&infra->domain_limits, cfg)) {
infra_delete(infra);
return NULL;
}
if(!setup_wait_limits(infra, cfg)) {
if(!setup_wait_limits(&infra->wait_limits_netblock,
&infra->wait_limits_cookie_netblock, cfg)) {
infra_delete(infra);
return NULL;
}
@ -377,12 +401,29 @@ static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg))
}
}
void
domain_limits_free(struct rbtree_type* domain_limits)
{
if(!domain_limits)
return;
traverse_postorder(domain_limits, domain_limit_free, NULL);
}
/** delete wait_limit_netblock_info entries */
static void wait_limit_netblock_del(rbnode_type* n, void* ATTR_UNUSED(arg))
{
free(n);
}
void
wait_limits_free(struct rbtree_type* wait_limits_tree)
{
if(!wait_limits_tree)
return;
traverse_postorder(wait_limits_tree, wait_limit_netblock_del,
NULL);
}
void
infra_delete(struct infra_cache* infra)
{
@ -390,12 +431,10 @@ infra_delete(struct infra_cache* infra)
return;
slabhash_delete(infra->hosts);
slabhash_delete(infra->domain_rates);
traverse_postorder(&infra->domain_limits, domain_limit_free, NULL);
domain_limits_free(&infra->domain_limits);
slabhash_delete(infra->client_ip_rates);
traverse_postorder(&infra->wait_limits_netblock,
wait_limit_netblock_del, NULL);
traverse_postorder(&infra->wait_limits_cookie_netblock,
wait_limit_netblock_del, NULL);
wait_limits_free(&infra->wait_limits_netblock);
wait_limits_free(&infra->wait_limits_cookie_netblock);
free(infra);
}
@ -426,7 +465,7 @@ infra_adjust(struct infra_cache* infra, struct config_file* cfg)
/* reapply domain limits */
traverse_postorder(&infra->domain_limits, domain_limit_free,
NULL);
if(!setup_domain_limits(infra, cfg)) {
if(!setup_domain_limits(&infra->domain_limits, cfg)) {
infra_delete(infra);
return NULL;
}
@ -668,7 +707,7 @@ infra_update_tcp_works(struct infra_cache* infra,
if(data->rtt.rto >= RTT_MAX_TIMEOUT)
/* do not disqualify this server altogether, it is better
* than nothing */
data->rtt.rto = STILL_USEFUL_TIMEOUT;
data->rtt.rto = still_useful_timeout();
lock_rw_unlock(&e->lock);
}
@ -808,7 +847,7 @@ infra_get_lame_rtt(struct infra_cache* infra,
&& infra->infra_keep_probing) {
/* single probe, keep probing */
if(*rtt >= USEFUL_SERVER_TOP_TIMEOUT)
*rtt = STILL_USEFUL_TIMEOUT;
*rtt = still_useful_timeout();
} else if(host->rtt.rto >= PROBE_MAXRTO && timenow < host->probedelay
&& rtt_notimeout(&host->rtt)*4 <= host->rtt.rto) {
/* single probe for this domain, and we are not probing */
@ -816,15 +855,15 @@ infra_get_lame_rtt(struct infra_cache* infra,
if(qtype == LDNS_RR_TYPE_A) {
if(host->timeout_A >= TIMEOUT_COUNT_MAX)
*rtt = USEFUL_SERVER_TOP_TIMEOUT;
else *rtt = STILL_USEFUL_TIMEOUT;
else *rtt = still_useful_timeout();
} else if(qtype == LDNS_RR_TYPE_AAAA) {
if(host->timeout_AAAA >= TIMEOUT_COUNT_MAX)
*rtt = USEFUL_SERVER_TOP_TIMEOUT;
else *rtt = STILL_USEFUL_TIMEOUT;
else *rtt = still_useful_timeout();
} else {
if(host->timeout_other >= TIMEOUT_COUNT_MAX)
*rtt = USEFUL_SERVER_TOP_TIMEOUT;
else *rtt = STILL_USEFUL_TIMEOUT;
else *rtt = still_useful_timeout();
}
}
/* expired entry */
@ -832,7 +871,7 @@ infra_get_lame_rtt(struct infra_cache* infra,
/* see if this can be a re-probe of an unresponsive server */
if(host->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT) {
lock_rw_unlock(&e->lock);
*rtt = STILL_USEFUL_TIMEOUT;
*rtt = still_useful_timeout();
*lame = 0;
*dnsseclame = 0;
*reclame = 0;
@ -1081,7 +1120,8 @@ int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name,
lock_rw_unlock(&entry->lock);
if(premax <= lim && max > lim) {
char buf[257], qnm[257], ts[12], cs[12], ip[128];
char buf[LDNS_MAX_DOMAINLEN], qnm[LDNS_MAX_DOMAINLEN];
char ts[12], cs[12], ip[128];
dname_str(name, buf);
dname_str(qinfo->qname, qnm);
sldns_wire2str_type_buf(qinfo->qtype, ts, sizeof(ts));

View file

@ -52,6 +52,19 @@
struct slabhash;
struct config_file;
/** number of timeouts for a type when the domain can be blocked ;
* even if another type has completely rtt maxed it, the different type
* can do this number of packets (until those all timeout too) */
#define TIMEOUT_COUNT_MAX 3
/** Timeout when only a single probe query per IP is allowed.
* Any RTO above this number is considered a probe.
* It is synchronized (caped) with USEFUL_SERVER_TOP_TIMEOUT so that probing
* keeps working even if that configurable number drops below the default
* 12000 ms of probing. */
extern int PROBE_MAXRTO;
/**
* Host information kept for every server, per zone.
*/
@ -502,4 +515,22 @@ void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep,
void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep,
struct config_file* cfg);
/** setup wait limits tree (0 on failure) */
int setup_wait_limits(struct rbtree_type* wait_limits_netblock,
struct rbtree_type* wait_limits_cookie_netblock,
struct config_file* cfg);
/** Free the wait limits and wait cookie limits tree. */
void wait_limits_free(struct rbtree_type* wait_limits_tree);
/** setup domain limits tree (0 on failure) */
int setup_domain_limits(struct rbtree_type* domain_limits,
struct config_file* cfg);
/** Free the domain limits tree. */
void domain_limits_free(struct rbtree_type* domain_limits);
/** exported for unit test */
int still_useful_timeout();
#endif /* SERVICES_CACHE_INFRA_H */

View file

@ -68,6 +68,8 @@ struct rrset_cache* rrset_cache_create(struct config_file* cfg,
struct rrset_cache *r = (struct rrset_cache*)slabhash_create(slabs,
startarray, maxmem, ub_rrset_sizefunc, ub_rrset_compare,
ub_rrset_key_delete, rrset_data_delete, alloc);
if(!r)
return NULL;
slabhash_setmarkdel(&r->table, &rrset_markdel);
return r;
}

View file

@ -90,10 +90,13 @@
#ifdef HAVE_NGTCP2
#include <ngtcp2/ngtcp2.h>
#include <ngtcp2/ngtcp2_crypto.h>
#ifdef HAVE_NGTCP2_NGTCP2_CRYPTO_QUICTLS_H
#ifdef HAVE_NGTCP2_NGTCP2_CRYPTO_OSSL_H
#include <ngtcp2/ngtcp2_crypto_ossl.h>
#elif defined(HAVE_NGTCP2_NGTCP2_CRYPTO_QUICTLS_H)
#include <ngtcp2/ngtcp2_crypto_quictls.h>
#else
#elif defined(HAVE_NGTCP2_NGTCP2_CRYPTO_OPENSSL_H)
#include <ngtcp2/ngtcp2_crypto_openssl.h>
#define MAKE_QUIC_METHOD 1
#endif
#endif
@ -473,7 +476,8 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
"Got %u. To fix: start with "
"root permissions(linux) or sysctl "
"bigger net.core.wmem_max(linux) or "
"kern.ipc.maxsockbuf(bsd) values.",
"kern.ipc.maxsockbuf(bsd) values. or "
"set so-sndbuf: 0 (use system value).",
(unsigned)snd, (unsigned)got);
}
# ifdef SO_SNDBUFFORCE
@ -703,7 +707,10 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
{
int s = -1;
char* err;
#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND) || defined(SO_BINDANY)
#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) \
|| defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT) \
|| defined(IP_BINDANY) || defined(IP_FREEBIND) \
|| defined(SO_BINDANY) || defined(TCP_NODELAY)
int on = 1;
#endif
#ifdef HAVE_SYSTEMD
@ -899,7 +906,7 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
against IP spoofing attacks as suggested in RFC7413 */
#ifdef __APPLE__
/* OS X implementation only supports qlen of 1 via this call. Actual
value is configured by the net.inet.tcp.fastopen_backlog kernel parm. */
value is configured by the net.inet.tcp.fastopen_backlog kernel param. */
qlen = 1;
#else
/* 5 is recommended on linux */
@ -1031,7 +1038,7 @@ err:
* Create socket from getaddrinfo results
*/
static int
make_sock(int stype, const char* ifname, const char* port,
make_sock(int stype, const char* ifname, int port,
struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd,
int* reuseport, int transparent, int tcp_mss, int nodelay, int freebind,
int use_systemd, int dscp, struct unbound_socket* ub_sock,
@ -1039,9 +1046,11 @@ make_sock(int stype, const char* ifname, const char* port,
{
struct addrinfo *res = NULL;
int r, s, inuse, noproto;
char portbuf[32];
snprintf(portbuf, sizeof(portbuf), "%d", port);
hints->ai_socktype = stype;
*noip6 = 0;
if((r=getaddrinfo(ifname, port, hints, &res)) != 0 || !res) {
if((r=getaddrinfo(ifname, portbuf, hints, &res)) != 0 || !res) {
#ifdef USE_WINSOCK
if(r == EAI_NONAME && hints->ai_family == AF_INET6){
*noip6 = 1; /* 'Host not found' for IP6 on winXP */
@ -1049,7 +1058,7 @@ make_sock(int stype, const char* ifname, const char* port,
}
#endif
log_err("node %s:%s getaddrinfo: %s %s",
ifname?ifname:"default", port, gai_strerror(r),
ifname?ifname:"default", portbuf, gai_strerror(r),
#ifdef EAI_SYSTEM
(r==EAI_SYSTEM?(char*)strerror(errno):"")
#else
@ -1103,7 +1112,7 @@ make_sock(int stype, const char* ifname, const char* port,
/** make socket and first see if ifname contains port override info */
static int
make_sock_port(int stype, const char* ifname, const char* port,
make_sock_port(int stype, const char* ifname, int port,
struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd,
int* reuseport, int transparent, int tcp_mss, int nodelay, int freebind,
int use_systemd, int dscp, struct unbound_socket* ub_sock,
@ -1112,23 +1121,22 @@ make_sock_port(int stype, const char* ifname, const char* port,
char* s = strchr(ifname, '@');
if(s) {
/* override port with ifspec@port */
char p[16];
int port;
char newif[128];
if((size_t)(s-ifname) >= sizeof(newif)) {
log_err("ifname too long: %s", ifname);
*noip6 = 0;
return -1;
}
if(strlen(s+1) >= sizeof(p)) {
log_err("portnumber too long: %s", ifname);
port = atoi(s+1);
if(port < 0 || 0 == port || port > 65535) {
log_err("invalid portnumber in interface: %s", ifname);
*noip6 = 0;
return -1;
}
(void)strlcpy(newif, ifname, sizeof(newif));
newif[s-ifname] = 0;
(void)strlcpy(p, s+1, sizeof(p));
p[strlen(s+1)]=0;
return make_sock(stype, newif, p, hints, v6only, noip6, rcv,
return make_sock(stype, newif, port, hints, v6only, noip6, rcv,
snd, reuseport, transparent, tcp_mss, nodelay, freebind,
use_systemd, dscp, ub_sock, additional);
}
@ -1175,6 +1183,15 @@ set_recvtimestamp(int s)
return 0;
}
return 1;
#elif defined(SO_TIMESTAMP) && defined(SCM_TIMESTAMP)
int on = 1;
/* FreeBSD and also Linux. */
if (setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, (void*)&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., SO_TIMESTAMP, ...) failed: %s",
strerror(errno));
return 0;
}
return 1;
#else
log_err("packets timestamping is not supported on this platform");
(void)s;
@ -1237,26 +1254,6 @@ set_recvpktinfo(int s, int family)
return 1;
}
/** see if interface is ssl, its port number == the ssl port number */
static int
if_is_ssl(const char* ifname, const char* port, int ssl_port,
struct config_strlist* tls_additional_port)
{
struct config_strlist* s;
char* p = strchr(ifname, '@');
if(!p && atoi(port) == ssl_port)
return 1;
if(p && atoi(p+1) == ssl_port)
return 1;
for(s = tls_additional_port; s; s = s->next) {
if(p && atoi(p+1) == atoi(s->str))
return 1;
if(!p && atoi(port) == atoi(s->str))
return 1;
}
return 0;
}
/**
* Helper for ports_open. Creates one interface (or NULL for default).
* @param ifname: The interface ip address.
@ -1265,7 +1262,7 @@ if_is_ssl(const char* ifname, const char* port, int ssl_port,
* @param do_udp: if udp should be used.
* @param do_tcp: if tcp should be used.
* @param hints: for getaddrinfo. family and flags have to be set by caller.
* @param port: Port number to use (as string).
* @param port: Port number to use.
* @param list: list of open ports, appended to, changed to point to list head.
* @param rcv: receive buffer size for UDP
* @param snd: send buffer size for UDP
@ -1291,7 +1288,7 @@ if_is_ssl(const char* ifname, const char* port, int ssl_port,
*/
static int
ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
struct addrinfo *hints, const char* port, struct listen_port** list,
struct addrinfo *hints, int port, struct listen_port** list,
size_t rcv, size_t snd, int ssl_port,
struct config_strlist* tls_additional_port, int https_port,
struct config_strlist* proxy_protocol_port,
@ -1300,12 +1297,18 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
int quic_port, int http_notls_downstream, int sock_queue_timeout)
{
int s, noip6=0;
int is_ssl = if_is_ssl(ifname, port, ssl_port, tls_additional_port);
int is_https = if_is_https(ifname, port, https_port);
int is_dnscrypt = if_is_dnscrypt(ifname, port, dnscrypt_port);
int is_pp2 = if_is_pp2(ifname, port, proxy_protocol_port);
int nodelay = is_https && http2_nodelay;
struct unbound_socket* ub_sock;
int is_doq = if_is_quic(ifname, port, quic_port);
/* Always set TCP_NODELAY on TLS connection as it speeds up the TLS
* handshake. DoH had already such option so we respect it.
* Otherwise the server waits before sending more handshake data for
* the client ACK (Nagle's algorithm), which is delayed because the
* client waits for more data before ACKing (delayed ACK). */
int nodelay = is_https?http2_nodelay:is_ssl;
struct unbound_socket* ub_sock;
const char* add = NULL;
if(!do_udp && !do_tcp)
@ -1324,6 +1327,12 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
}
}
/* Check if both UDP and TCP ports should be open.
* In the case of encrypted channels, probably an unencrypted channel
* at the same port is not desired. */
if((is_ssl || is_https) && !is_doq) do_udp = do_auto = 0;
if((is_doq) && !(is_https || is_ssl)) do_tcp = 0;
if(do_auto) {
ub_sock = calloc(1, sizeof(struct unbound_socket));
if(!ub_sock)
@ -1369,13 +1378,11 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
} else if(is_doq) {
udp_port_type = listen_type_doq;
add = "doq";
if(((strchr(ifname, '@') &&
atoi(strchr(ifname, '@')+1) == 53) ||
(!strchr(ifname, '@') && atoi(port) == 53))) {
log_err("DNS over QUIC is not allowed on "
"port 53. Port 53 is for DNS "
"datagrams. Error for "
"interface '%s'.", ifname);
if(if_listens_on(ifname, port, 53, NULL)) {
log_err("DNS over QUIC is strictly not "
"allowed on port 53 as per RFC 9250. "
"Port 53 is for DNS datagrams. Error "
"for interface '%s'.", ifname);
free(ub_sock->addr);
free(ub_sock);
return 0;
@ -1423,8 +1430,6 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
}
}
if(do_tcp) {
int is_ssl = if_is_ssl(ifname, port, ssl_port,
tls_additional_port);
enum listen_type port_type;
ub_sock = calloc(1, sizeof(struct unbound_socket));
if(!ub_sock)
@ -1523,9 +1528,10 @@ listen_create(struct comm_base* base, struct listen_port* ports,
size_t bufsize, int tcp_accept_count, int tcp_idle_timeout,
int harden_large_queries, uint32_t http_max_streams,
char* http_endpoint, int http_notls, struct tcl_list* tcp_conn_limit,
void* sslctx, struct dt_env* dtenv, struct doq_table* doq_table,
struct ub_randstate* rnd, const char* ssl_service_key,
const char* ssl_service_pem, struct config_file* cfg,
void* dot_sslctx, void* doh_sslctx, void* quic_sslctx,
struct dt_env* dtenv,
struct doq_table* doq_table,
struct ub_randstate* rnd,struct config_file* cfg,
comm_point_callback_type* cb, void *cb_arg)
{
struct listen_dnsport* front = (struct listen_dnsport*)
@ -1558,8 +1564,7 @@ listen_create(struct comm_base* base, struct listen_port* ports,
#endif
cp = comm_point_create_doq(base, ports->fd,
front->udp_buff, cb, cb_arg, ports->socket,
doq_table, rnd, ssl_service_key,
ssl_service_pem, cfg);
doq_table, rnd, quic_sslctx, cfg);
} else if(ports->ftype == listen_type_tcp ||
ports->ftype == listen_type_tcp_dnscrypt) {
cp = comm_point_create_tcp(base, ports->fd,
@ -1578,7 +1583,7 @@ listen_create(struct comm_base* base, struct listen_port* ports,
ports->ftype, ports->pp2_enabled, cb, cb_arg,
ports->socket);
if(ports->ftype == listen_type_http) {
if(!sslctx && !http_notls) {
if(!doh_sslctx && !http_notls) {
log_warn("HTTPS port configured, but "
"no TLS tls-service-key or "
"tls-service-pem set");
@ -1606,7 +1611,7 @@ listen_create(struct comm_base* base, struct listen_port* ports,
front->udp_buff, ports->pp2_enabled, cb,
cb_arg, ports->socket);
#else
log_warn("This system does not support UDP ancilliary data.");
log_warn("This system does not support UDP ancillary data.");
#endif
}
if(!cp) {
@ -1620,10 +1625,15 @@ listen_create(struct comm_base* base, struct listen_port* ports,
(ports->ftype == listen_type_udpancil) ||
(ports->ftype == listen_type_tcp_dnscrypt) ||
(ports->ftype == listen_type_udp_dnscrypt) ||
(ports->ftype == listen_type_udpancil_dnscrypt))
(ports->ftype == listen_type_udpancil_dnscrypt)) {
cp->ssl = NULL;
else
cp->ssl = sslctx;
} else if(ports->ftype == listen_type_doq) {
cp->ssl = quic_sslctx;
} else if(ports->ftype == listen_type_http) {
cp->ssl = doh_sslctx;
} else {
cp->ssl = dot_sslctx;
}
cp->dtenv = dtenv;
cp->do_not_close = 1;
#ifdef USE_DNSCRYPT
@ -1887,8 +1897,6 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
struct addrinfo hints;
int i, do_ip4, do_ip6;
int do_tcp, do_auto;
char portbuf[32];
snprintf(portbuf, sizeof(portbuf), "%d", cfg->port);
do_ip4 = cfg->do_ip4;
do_ip6 = cfg->do_ip6;
do_tcp = cfg->do_tcp;
@ -1934,12 +1942,11 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
return NULL;
}
now = after;
snprintf(portbuf, sizeof(portbuf), "%d", extraport);
if(do_ip6) {
hints.ai_family = AF_INET6;
if(!ports_create_if("::0",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
&hints, extraport, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port,
@ -1958,7 +1965,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
hints.ai_family = AF_INET;
if(!ports_create_if("0.0.0.0",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
&hints, extraport, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port,
@ -1980,7 +1987,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
hints.ai_family = AF_INET6;
if(!ports_create_if(do_auto?"::0":"::1",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
&hints, cfg->port, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port, cfg->proxy_protocol_port,
@ -1998,7 +2005,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
hints.ai_family = AF_INET;
if(!ports_create_if(do_auto?"0.0.0.0":"127.0.0.1",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
&hints, cfg->port, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port, cfg->proxy_protocol_port,
@ -2018,7 +2025,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
continue;
hints.ai_family = AF_INET6;
if(!ports_create_if(ifs[i], 0, cfg->do_udp,
do_tcp, &hints, portbuf, &list,
do_tcp, &hints, cfg->port, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port, cfg->proxy_protocol_port,
@ -2036,7 +2043,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
continue;
hints.ai_family = AF_INET;
if(!ports_create_if(ifs[i], 0, cfg->do_udp,
do_tcp, &hints, portbuf, &list,
do_tcp, &hints, cfg->port, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port, cfg->proxy_protocol_port,
@ -3105,7 +3112,7 @@ static int http2_req_header_cb(nghttp2_session* session,
return 0;
}
/* Content type is a SHOULD (rfc7231#section-3.1.1.5) when using POST,
* and not needed when using GET. Don't enfore.
* and not needed when using GET. Don't enforce.
* If set only allow lowercase "application/dns-message".
*
* Clients SHOULD (rfc8484#section-4.1) set an accept header, but MUST
@ -3167,7 +3174,7 @@ static int http2_req_data_chunk_recv_cb(nghttp2_session* ATTR_UNUSED(session),
qlen = h2_stream->content_length;
} else if(len <= h2_session->c->http2_stream_max_qbuffer_size) {
/* setting this to msg-buffer-size can result in a lot
* of memory consuption. Most queries should fit in a
* of memory consumption. Most queries should fit in a
* single DATA frame, and most POST queries will
* contain content-length which does not impose this
* limit. */
@ -3193,7 +3200,7 @@ static int http2_req_data_chunk_recv_cb(nghttp2_session* ATTR_UNUSED(session),
if(!h2_stream->qbuffer ||
sldns_buffer_remaining(h2_stream->qbuffer) < len) {
verbose(VERB_ALGO, "http2 data_chunck_recv failed. Not enough "
verbose(VERB_ALGO, "http2 data_chunk_recv failed. Not enough "
"buffer space for POST query. Can happen on multi "
"frame requests without content-length header");
h2_stream->query_too_large = 1;
@ -3263,6 +3270,21 @@ doq_table_create(struct config_file* cfg, struct ub_randstate* rnd)
struct doq_table* table = calloc(1, sizeof(*table));
if(!table)
return NULL;
#ifdef USE_NGTCP2_CRYPTO_OSSL
/* Initialize the ossl crypto, it is harmless to call twice,
* and this is before use of doq connections. */
if(ngtcp2_crypto_ossl_init() != 0) {
log_err("ngtcp2_crypto_oss_init failed");
free(table);
return NULL;
}
#elif defined(HAVE_NGTCP2_CRYPTO_QUICTLS_INIT)
if(ngtcp2_crypto_quictls_init() != 0) {
log_err("ngtcp2_crypto_quictls_init failed");
free(table);
return NULL;
}
#endif
table->idle_timeout = ((uint64_t)cfg->tcp_idle_timeout)*
NGTCP2_MILLISECONDS;
table->sv_scidlen = 16;
@ -3602,12 +3624,18 @@ doq_conn_delete(struct doq_conn* conn, struct doq_table* table)
lock_rw_wrlock(&conn->table->conid_lock);
doq_conn_clear_conids(conn);
lock_rw_unlock(&conn->table->conid_lock);
ngtcp2_conn_del(conn->conn);
/* Remove the app data from ngtcp2 before SSL_free of conn->ssl,
* because the ngtcp2 conn is deleted. */
SSL_set_app_data(conn->ssl, NULL);
if(conn->stream_tree.count != 0) {
traverse_postorder(&conn->stream_tree, stream_tree_del, table);
}
free(conn->key.dcid);
SSL_free(conn->ssl);
#ifdef USE_NGTCP2_CRYPTO_OSSL
ngtcp2_crypto_ossl_ctx_del(conn->ossl_ctx);
#endif
ngtcp2_conn_del(conn->conn);
free(conn->close_pkt);
free(conn);
}
@ -4465,7 +4493,7 @@ doq_log_printf_cb(void* ATTR_UNUSED(user_data), const char* fmt, ...)
va_end(ap);
}
#ifndef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT
#ifdef MAKE_QUIC_METHOD
/** the doq application tx key callback, false on failure */
static int
doq_application_tx_key_cb(struct doq_conn* conn)
@ -4499,7 +4527,9 @@ doq_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
ngtcp2_crypto_level
#endif
level =
#ifdef HAVE_NGTCP2_CRYPTO_QUICTLS_FROM_OSSL_ENCRYPTION_LEVEL
#ifdef USE_NGTCP2_CRYPTO_OSSL
ngtcp2_crypto_ossl_from_ossl_encryption_level(ossl_level);
#elif defined(HAVE_NGTCP2_CRYPTO_QUICTLS_FROM_OSSL_ENCRYPTION_LEVEL)
ngtcp2_crypto_quictls_from_ossl_encryption_level(ossl_level);
#else
ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
@ -4545,7 +4575,9 @@ doq_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
ngtcp2_crypto_level
#endif
level =
#ifdef HAVE_NGTCP2_CRYPTO_QUICTLS_FROM_OSSL_ENCRYPTION_LEVEL
#ifdef USE_NGTCP2_CRYPTO_OSSL
ngtcp2_crypto_ossl_from_ossl_encryption_level(ossl_level);
#elif defined(HAVE_NGTCP2_CRYPTO_QUICTLS_FROM_OSSL_ENCRYPTION_LEVEL)
ngtcp2_crypto_quictls_from_ossl_encryption_level(ossl_level);
#else
ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
@ -4580,7 +4612,7 @@ doq_send_alert(SSL *ssl, enum ssl_encryption_level_t ATTR_UNUSED(level),
doq_conn->tls_alert = alert;
return 1;
}
#endif /* HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT */
#endif /* MAKE_QUIC_METHOD */
/** ALPN select callback for the doq SSL context */
static int
@ -4598,12 +4630,11 @@ doq_alpn_select_cb(SSL* ATTR_UNUSED(ssl), const unsigned char** out,
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
/** create new tls session for server doq connection */
static SSL_CTX*
doq_ctx_server_setup(struct doq_server_socket* doq_socket)
void* quic_sslctx_create(char* key, char* pem, char* verifypem)
{
#ifdef HAVE_NGTCP2
char* sid_ctx = "unbound server";
#ifndef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT
#ifdef MAKE_QUIC_METHOD
SSL_QUIC_METHOD* quic_method;
#endif
SSL_CTX* ctx = SSL_CTX_new(TLS_server_method());
@ -4611,6 +4642,16 @@ doq_ctx_server_setup(struct doq_server_socket* doq_socket)
log_crypto_err("Could not SSL_CTX_new");
return NULL;
}
if(!key || key[0] == 0) {
log_err("doq: error, no tls-service-key file specified");
SSL_CTX_free(ctx);
return NULL;
}
if(!pem || pem[0] == 0) {
log_err("doq: error, no tls-service-pem file specified");
SSL_CTX_free(ctx);
return NULL;
}
SSL_CTX_set_options(ctx,
(SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
SSL_OP_SINGLE_ECDH_USE |
@ -4623,43 +4664,37 @@ doq_ctx_server_setup(struct doq_server_socket* doq_socket)
SSL_CTX_set_alpn_select_cb(ctx, doq_alpn_select_cb, NULL);
#endif
SSL_CTX_set_default_verify_paths(ctx);
if(!SSL_CTX_use_certificate_chain_file(ctx,
doq_socket->ssl_service_pem)) {
log_err("doq: error for cert file: %s",
doq_socket->ssl_service_pem);
if(!SSL_CTX_use_certificate_chain_file(ctx, pem)) {
log_err("doq: error for cert file: %s", pem);
log_crypto_err("doq: error in "
"SSL_CTX_use_certificate_chain_file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_use_PrivateKey_file(ctx, doq_socket->ssl_service_key,
SSL_FILETYPE_PEM)) {
log_err("doq: error for private key file: %s",
doq_socket->ssl_service_key);
if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) {
log_err("doq: error for private key file: %s", key);
log_crypto_err("doq: error in SSL_CTX_use_PrivateKey_file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_check_private_key(ctx)) {
log_err("doq: error for key file: %s",
doq_socket->ssl_service_key);
log_err("doq: error for key file: %s", key);
log_crypto_err("doq: error in SSL_CTX_check_private_key");
SSL_CTX_free(ctx);
return NULL;
}
SSL_CTX_set_session_id_context(ctx, (void*)sid_ctx, strlen(sid_ctx));
if(doq_socket->ssl_verify_pem && doq_socket->ssl_verify_pem[0]) {
if(!SSL_CTX_load_verify_locations(ctx,
doq_socket->ssl_verify_pem, NULL)) {
if(verifypem && verifypem[0]) {
if(!SSL_CTX_load_verify_locations(ctx, verifypem, NULL)) {
log_err("doq: error for verify pem file: %s",
doq_socket->ssl_verify_pem);
verifypem);
log_crypto_err("doq: error in "
"SSL_CTX_load_verify_locations");
SSL_CTX_free(ctx);
return NULL;
}
SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(
doq_socket->ssl_verify_pem));
verifypem));
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|
SSL_VERIFY_CLIENT_ONCE|
SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
@ -4672,7 +4707,7 @@ doq_ctx_server_setup(struct doq_server_socket* doq_socket)
SSL_CTX_free(ctx);
return NULL;
}
#else
#elif defined(MAKE_QUIC_METHOD)
/* The quic_method needs to remain valid during the SSL_CTX
* lifetime, so we allocate it. It is freed with the
* doq_server_socket. */
@ -4690,6 +4725,10 @@ doq_ctx_server_setup(struct doq_server_socket* doq_socket)
SSL_CTX_set_quic_method(ctx, doq_socket->quic_method);
#endif
return ctx;
#else /* HAVE_NGTCP2 */
(void)key; (void)pem; (void)verifypem;
return NULL;
#endif /* HAVE_NGTCP2 */
}
/** Get the ngtcp2_conn from ssl userdata of type ngtcp2_conn_ref */
@ -4703,12 +4742,29 @@ static ngtcp2_conn* doq_conn_ref_get_conn(ngtcp2_crypto_conn_ref* conn_ref)
static SSL*
doq_ssl_server_setup(SSL_CTX* ctx, struct doq_conn* conn)
{
#ifdef USE_NGTCP2_CRYPTO_OSSL
int ret;
#endif
SSL* ssl = SSL_new(ctx);
if(!ssl) {
log_crypto_err("doq: SSL_new failed");
return NULL;
}
#ifdef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT
#ifdef USE_NGTCP2_CRYPTO_OSSL
if((ret=ngtcp2_crypto_ossl_ctx_new(&conn->ossl_ctx, NULL)) != 0) {
log_err("doq: ngtcp2_crypto_ossl_ctx_new failed: %s",
ngtcp2_strerror(ret));
SSL_free(ssl);
return NULL;
}
ngtcp2_crypto_ossl_ctx_set_ssl(conn->ossl_ctx, ssl);
if(ngtcp2_crypto_ossl_configure_server_session(ssl) != 0) {
log_err("doq: ngtcp2_crypto_ossl_configure_server_session failed");
SSL_free(ssl);
return NULL;
}
#endif
#if defined(USE_NGTCP2_CRYPTO_OSSL) || defined(HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT)
conn->conn_ref.get_conn = &doq_conn_ref_get_conn;
conn->conn_ref.user_data = conn;
SSL_set_app_data(ssl, &conn->conn_ref);
@ -4716,20 +4772,14 @@ doq_ssl_server_setup(SSL_CTX* ctx, struct doq_conn* conn)
SSL_set_app_data(ssl, conn);
#endif
SSL_set_accept_state(ssl);
#ifdef USE_NGTCP2_CRYPTO_OSSL
SSL_set_quic_tls_early_data_enabled(ssl, 1);
#else
SSL_set_quic_early_data_enabled(ssl, 1);
#endif
return ssl;
}
/** setup the doq_socket server tls context */
int
doq_socket_setup_ctx(struct doq_server_socket* doq_socket)
{
doq_socket->ctx = doq_ctx_server_setup(doq_socket);
if(!doq_socket->ctx)
return 0;
return 1;
}
int
doq_conn_setup(struct doq_conn* conn, uint8_t* scid, size_t scidlen,
uint8_t* ocid, size_t ocidlen, const uint8_t* token, size_t tokenlen)
@ -4847,7 +4897,11 @@ doq_conn_setup(struct doq_conn* conn, uint8_t* scid, size_t scidlen,
log_err("doq_ssl_server_setup failed");
return 0;
}
#ifdef USE_NGTCP2_CRYPTO_OSSL
ngtcp2_conn_set_tls_native_handle(conn->conn, conn->ossl_ctx);
#else
ngtcp2_conn_set_tls_native_handle(conn->conn, conn->ssl);
#endif
doq_conn_write_enable(conn);
return 1;
}

View file

@ -52,6 +52,9 @@
#ifdef HAVE_NGTCP2
#include <ngtcp2/ngtcp2.h>
#include <ngtcp2/ngtcp2_crypto.h>
#ifdef USE_NGTCP2_CRYPTO_OSSL
struct ngtcp2_crypto_ossl_ctx;
#endif
#endif
struct listen_list;
struct config_file;
@ -194,12 +197,12 @@ int resolve_interface_names(char** ifs, int num_ifs,
* @param http_endpoint: HTTP endpoint to service queries on
* @param http_notls: no TLS for http downstream
* @param tcp_conn_limit: TCP connection limit info.
* @param sslctx: nonNULL if ssl context.
* @param dot_sslctx: nonNULL if dot ssl context.
* @param doh_sslctx: nonNULL if doh ssl context.
* @param quic_sslctx: nonNULL if quic ssl context.
* @param dtenv: nonNULL if dnstap enabled.
* @param doq_table: the doq connection table, with shared information.
* @param rnd: random state.
* @param ssl_service_key: the SSL service key file.
* @param ssl_service_pem: the SSL service pem file.
* @param cfg: config file struct.
* @param cb: callback function when a request arrives. It is passed
* the packet and user argument. Return true to send a reply.
@ -211,9 +214,10 @@ listen_create(struct comm_base* base, struct listen_port* ports,
size_t bufsize, int tcp_accept_count, int tcp_idle_timeout,
int harden_large_queries, uint32_t http_max_streams,
char* http_endpoint, int http_notls, struct tcl_list* tcp_conn_limit,
void* sslctx, struct dt_env* dtenv, struct doq_table* doq_table,
struct ub_randstate* rnd, const char* ssl_service_key,
const char* ssl_service_pem, struct config_file* cfg,
void* dot_sslctx, void* doh_sslctx, void* quic_sslctx,
struct dt_env* dtenv,
struct doq_table* doq_table,
struct ub_randstate* rnd,struct config_file* cfg,
comm_point_callback_type* cb, void *cb_arg);
/**
@ -512,6 +516,15 @@ struct doq_table {
size_t current_size;
};
/**
* create SSL context for QUIC
* @param key: private key file.
* @param pem: public key cert.
* @param verifypem: if nonNULL, verifylocation file.
* return SSL_CTX* or NULL on failure (logged).
*/
void* quic_sslctx_create(char* key, char* pem, char* verifypem);
/** create doq table */
struct doq_table* doq_table_create(struct config_file* cfg,
struct ub_randstate* rnd);
@ -596,9 +609,13 @@ struct doq_conn {
uint8_t tls_alert;
/** the ssl context, SSL* */
void* ssl;
#ifdef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT
#if defined(USE_NGTCP2_CRYPTO_OSSL) || defined(HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT)
/** the connection reference for ngtcp2_conn and userdata in ssl */
struct ngtcp2_crypto_conn_ref conn_ref;
#endif
#ifdef USE_NGTCP2_CRYPTO_OSSL
/** the per-connection state for ngtcp2_crypto_ossl */
struct ngtcp2_crypto_ossl_ctx* ossl_ctx;
#endif
/** closure packet, if any */
uint8_t* close_pkt;
@ -712,9 +729,6 @@ int doq_timer_cmp(const void* key1, const void* key2);
/** compare function of doq_stream */
int doq_stream_cmp(const void* key1, const void* key2);
/** setup the doq_socket server tls context */
int doq_socket_setup_ctx(struct doq_server_socket* doq_socket);
/** setup the doq connection callbacks, and settings. */
int doq_conn_setup(struct doq_conn* conn, uint8_t* scid, size_t scidlen,
uint8_t* ocid, size_t ocidlen, const uint8_t* token, size_t tokenlen);

View file

@ -223,7 +223,7 @@ lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len,
lock_rw_wrlock(&z->lock);
if(!rbtree_insert(&zones->ztree, &z->node)) {
struct local_zone* oldz;
char str[256];
char str[LDNS_MAX_DOMAINLEN];
dname_str(nm, str);
log_warn("duplicate local-zone %s", str);
lock_rw_unlock(&z->lock);
@ -943,6 +943,16 @@ int local_zone_enter_defaults(struct local_zones* zones, struct config_file* cfg
log_err("out of memory adding default zone");
return 0;
}
/* resolver.arpa. zone (RFC 9462) */
if(!add_empty_default(zones, cfg, "resolver.arpa.")) {
log_err("out of memory adding default zone");
return 0;
}
/* service.arpa. zone (draft-ietf-dnssd-srp-25) */
if(!add_empty_default(zones, cfg, "service.arpa.")) {
log_err("out of memory adding default zone");
return 0;
}
/* onion. zone (RFC 7686) */
if(!add_empty_default(zones, cfg, "onion.")) {
log_err("out of memory adding default zone");
@ -1765,7 +1775,7 @@ lz_inform_print(struct local_zone* z, struct query_info* qinfo,
struct sockaddr_storage* addr, socklen_t addrlen)
{
char ip[128], txt[512];
char zname[LDNS_MAX_DOMAINLEN+1];
char zname[LDNS_MAX_DOMAINLEN];
uint16_t port = ntohs(((struct sockaddr_in*)addr)->sin_port);
dname_str(z->name, zname);
addr_to_str(addr, addrlen, ip, sizeof(ip));
@ -1875,7 +1885,7 @@ local_zones_answer(struct local_zones* zones, struct module_env* env,
return 0;
}
if(z && verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(z->name, zname);
verbose(VERB_ALGO, "using localzone %s %s from view %s",
zname, local_zone_type2str(lzt), view->name);
@ -1897,7 +1907,7 @@ local_zones_answer(struct local_zones* zones, struct module_env* env,
z->override_tree, &tag, tagname, num_tags);
lock_rw_unlock(&zones->lock);
if(z && verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(z->name, zname);
verbose(VERB_ALGO, "using localzone %s %s", zname,
local_zone_type2str(lzt));
@ -2210,3 +2220,35 @@ void local_zones_del_data(struct local_zones* zones,
lock_rw_unlock(&z->lock);
}
/** Get memory usage for local_zone */
static size_t
local_zone_get_mem(struct local_zone* z)
{
size_t m = sizeof(*z);
lock_rw_rdlock(&z->lock);
m += z->namelen + z->taglen + regional_get_mem(z->region);
lock_rw_unlock(&z->lock);
return m;
}
size_t local_zones_get_mem(struct local_zones* zones)
{
struct local_zone* z;
size_t m;
if(!zones) return 0;
m = sizeof(*zones);
lock_rw_rdlock(&zones->lock);
RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
m += local_zone_get_mem(z);
}
lock_rw_unlock(&zones->lock);
return m;
}
void local_zones_swap_tree(struct local_zones* zones, struct local_zones* data)
{
rbtree_type oldtree = zones->ztree;
zones->ztree = data->ztree;
data->ztree = oldtree;
}

View file

@ -642,6 +642,20 @@ local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen,
struct local_data*
local_zone_find_data(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs);
/** Get memory usage for local_zones tree. The routine locks and unlocks
* the tree for reading. */
size_t local_zones_get_mem(struct local_zones* zones);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param zones: the local zones structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void local_zones_swap_tree(struct local_zones* zones,
struct local_zones* data);
/** Enter a new zone; returns with WRlock
* Made public for unit testing
* @param zones: the local zones tree

View file

@ -77,6 +77,20 @@
#include <netdb.h>
#endif
/** Compare two views by name */
static int
view_name_compare(const char* v_a, const char* v_b)
{
if(v_a == NULL && v_b == NULL)
return 0;
/* The NULL name is smaller than if the name is set. */
if(v_a == NULL)
return -1;
if(v_b == NULL)
return 1;
return strcmp(v_a, v_b);
}
/**
* Compare two response-ip client info entries for the purpose of mesh state
* compare. It returns 0 if ci_a and ci_b are considered equal; otherwise
@ -132,12 +146,14 @@ client_info_compare(const struct respip_client_info* ci_a,
}
if(ci_a->tag_datas != ci_b->tag_datas)
return ci_a->tag_datas < ci_b->tag_datas ? -1 : 1;
if(ci_a->view != ci_b->view)
return ci_a->view < ci_b->view ? -1 : 1;
/* For the unbound daemon these should be non-NULL and identical,
* but we check that just in case. */
if(ci_a->respip_set != ci_b->respip_set)
return ci_a->respip_set < ci_b->respip_set ? -1 : 1;
if(ci_a->view || ci_a->view_name || ci_b->view || ci_b->view_name) {
/* Compare the views by name. */
cmp = view_name_compare(
(ci_a->view?ci_a->view->name:ci_a->view_name),
(ci_b->view?ci_b->view->name:ci_b->view_name));
if(cmp != 0)
return cmp;
}
return 0;
}
@ -216,6 +232,7 @@ mesh_create(struct module_stack* stack, struct module_env* env)
mesh->ans_cachedb = 0;
mesh->num_queries_discard_timeout = 0;
mesh->num_queries_wait_limit = 0;
mesh->num_dns_error_reports = 0;
mesh->max_reply_states = env->cfg->num_queries_per_thread;
mesh->max_forever_states = (mesh->max_reply_states+1)/2;
#ifndef S_SPLINT_S
@ -870,6 +887,78 @@ void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
mesh_run(mesh, e->qstate->mesh_info, event, e);
}
/** copy strlist to region */
static struct config_strlist*
cfg_region_strlist_copy(struct regional* region, struct config_strlist* list)
{
struct config_strlist* result = NULL, *last = NULL, *s = list;
while(s) {
struct config_strlist* n = regional_alloc_zero(region,
sizeof(*n));
if(!n)
return NULL;
n->str = regional_strdup(region, s->str);
if(!n->str)
return NULL;
if(last)
last->next = n;
else result = n;
last = n;
s = s->next;
}
return result;
}
/** Copy the client info to the query region. */
static struct respip_client_info*
mesh_copy_client_info(struct regional* region, struct respip_client_info* cinfo)
{
size_t i;
struct respip_client_info* client_info;
client_info = regional_alloc_init(region, cinfo, sizeof(*cinfo));
if(!client_info)
return NULL;
/* Copy the client_info so that if the configuration changes,
* then the data stays valid. */
if(cinfo->taglist) {
client_info->taglist = regional_alloc_init(region, cinfo->taglist,
cinfo->taglen);
if(!client_info->taglist)
return NULL;
}
if(cinfo->tag_actions) {
client_info->tag_actions = regional_alloc_init(region, cinfo->tag_actions,
cinfo->tag_actions_size);
if(!client_info->tag_actions)
return NULL;
}
if(cinfo->tag_datas) {
client_info->tag_datas = regional_alloc_zero(region,
sizeof(struct config_strlist*)*cinfo->tag_datas_size);
if(!client_info->tag_datas)
return NULL;
for(i=0; i<cinfo->tag_datas_size; i++) {
if(cinfo->tag_datas[i]) {
client_info->tag_datas[i] = cfg_region_strlist_copy(
region, cinfo->tag_datas[i]);
if(!client_info->tag_datas[i])
return NULL;
}
}
}
if(cinfo->view) {
/* Do not copy the view pointer but store a name instead.
* The name is looked up later when done, this means that
* the view tree can be changed, by reloads. */
client_info->view = NULL;
client_info->view_name = regional_strdup(region,
cinfo->view->name);
if(!client_info->view_name)
return NULL;
}
return client_info;
}
struct mesh_state*
mesh_state_create(struct module_env* env, struct query_info* qinfo,
struct respip_client_info* cinfo, uint16_t qflags, int prime,
@ -910,8 +999,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
return NULL;
}
if(cinfo) {
mstate->s.client_info = regional_alloc_init(region, cinfo,
sizeof(*cinfo));
mstate->s.client_info = mesh_copy_client_info(region, cinfo);
if(!mstate->s.client_info) {
alloc_reg_release(env->alloc, region);
return NULL;
@ -1491,10 +1579,121 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
&r->query_reply.client_addr,
r->query_reply.client_addrlen, duration, 0, r_buffer,
(m->s.env->cfg->log_destaddr?(void*)r->query_reply.c->socket->addr:NULL),
r->query_reply.c->type);
r->query_reply.c->type, r->query_reply.c->ssl);
}
}
/**
* Generate the DNS Error Report (RFC9567).
* If there is an EDE attached for this reply and there was a Report-Channel
* EDNS0 option from the upstream, fire up a report query.
* @param qstate: module qstate.
* @param rep: prepared reply to be sent.
*/
static void dns_error_reporting(struct module_qstate* qstate,
struct reply_info* rep)
{
struct query_info qinfo;
struct mesh_state* sub;
struct module_qstate* newq;
uint8_t buf[LDNS_MAX_DOMAINLEN];
size_t count = 0;
int written;
size_t expected_length;
struct edns_option* opt;
sldns_ede_code reason_bogus = LDNS_EDE_NONE;
sldns_rr_type qtype = qstate->qinfo.qtype;
uint8_t* qname = qstate->qinfo.qname;
size_t qname_len = qstate->qinfo.qname_len-1; /* skip the trailing \0 */
uint8_t* agent_domain;
size_t agent_domain_len;
/* We need a valid reporting agent;
* this is based on qstate->edns_opts_back_in that will probably have
* the latest reporting agent we found while iterating */
opt = edns_opt_list_find(qstate->edns_opts_back_in,
LDNS_EDNS_REPORT_CHANNEL);
if(!opt) return;
agent_domain_len = opt->opt_len;
agent_domain = opt->opt_data;
if(dname_valid(agent_domain, agent_domain_len) < 3) {
/* The agent domain needs to be a valid dname that is not the
* root; from RFC9567. */
return;
}
/* Get the EDE generated from the mesh state, these are mostly
* validator errors. If other errors are produced in the future (e.g.,
* RPZ) we would not want them to result in error reports. */
reason_bogus = errinf_to_reason_bogus(qstate);
if(rep && ((reason_bogus == LDNS_EDE_DNSSEC_BOGUS &&
rep->reason_bogus != LDNS_EDE_NONE) ||
reason_bogus == LDNS_EDE_NONE)) {
reason_bogus = rep->reason_bogus;
}
if(reason_bogus == LDNS_EDE_NONE ||
/* other, does not make sense without the text that comes
* with it */
reason_bogus == LDNS_EDE_OTHER) return;
/* Synthesize the error report query in the format:
* "_er.$qtype.$qname.$ede._er.$reporting-agent-domain" */
/* First check if the static length parts fit in the buffer.
* That is everything except for qtype and ede that need to be
* converted to decimal and checked further on. */
expected_length = 4/*_er*/+qname_len+4/*_er*/+agent_domain_len;
if(expected_length > LDNS_MAX_DOMAINLEN) goto skip;
memmove(buf+count, "\3_er", 4);
count += 4;
written = snprintf((char*)buf+count, LDNS_MAX_DOMAINLEN-count,
"X%d", qtype);
expected_length += written;
/* Skip on error, truncation or long expected length */
if(written < 0 || (size_t)written >= LDNS_MAX_DOMAINLEN-count ||
expected_length > LDNS_MAX_DOMAINLEN ) goto skip;
/* Put in the label length */
*(buf+count) = (char)(written - 1);
count += written;
memmove(buf+count, qname, qname_len);
count += qname_len;
written = snprintf((char*)buf+count, LDNS_MAX_DOMAINLEN-count,
"X%d", reason_bogus);
expected_length += written;
/* Skip on error, truncation or long expected length */
if(written < 0 || (size_t)written >= LDNS_MAX_DOMAINLEN-count ||
expected_length > LDNS_MAX_DOMAINLEN ) goto skip;
*(buf+count) = (char)(written - 1);
count += written;
memmove(buf+count, "\3_er", 4);
count += 4;
/* Copy the agent domain */
memmove(buf+count, agent_domain, agent_domain_len);
count += agent_domain_len;
qinfo.qname = buf;
qinfo.qname_len = count;
qinfo.qtype = LDNS_RR_TYPE_TXT;
qinfo.qclass = qstate->qinfo.qclass;
qinfo.local_alias = NULL;
log_query_info(VERB_ALGO, "DNS Error Reporting: generating report "
"query for", &qinfo);
if(mesh_add_sub(qstate, &qinfo, BIT_RD, 0, 0, &newq, &sub)) {
qstate->env->mesh->num_dns_error_reports++;
}
return;
skip:
verbose(VERB_ALGO, "DNS Error Reporting: report query qname too long; "
"skip");
return;
}
void mesh_query_done(struct mesh_state* mstate)
{
struct mesh_reply* r;
@ -1512,8 +1711,10 @@ void mesh_query_done(struct mesh_state* mstate)
}
if(mstate->s.return_rcode == LDNS_RCODE_SERVFAIL ||
(rep && FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_SERVFAIL)) {
/* we are SERVFAILing; check for expired answer here */
mesh_serve_expired_callback(mstate);
if(mstate->s.env->cfg->serve_expired) {
/* we are SERVFAILing; check for expired answer here */
mesh_respond_serve_expired(mstate);
}
if((mstate->reply_list || mstate->cb_list)
&& mstate->s.env->cfg->log_servfail
&& !mstate->s.env->cfg->val_log_squelch) {
@ -1521,6 +1722,10 @@ void mesh_query_done(struct mesh_state* mstate)
if(err) { log_err("%s", err); }
}
}
if(mstate->reply_list && mstate->s.env->cfg->dns_error_reporting)
dns_error_reporting(&mstate->s, rep);
for(r = mstate->reply_list; r; r = r->next) {
struct timeval old;
timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time);
@ -1684,6 +1889,25 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh,
return result;
}
/** remove mesh state callback */
int mesh_state_del_cb(struct mesh_state* s, mesh_cb_func_type cb, void* cb_arg)
{
struct mesh_cb* r, *prev = NULL;
r = s->cb_list;
while(r) {
if(r->cb == cb && r->cb_arg == cb_arg) {
/* Delete this entry. */
/* It was allocated in the s.region, so no free. */
if(prev) prev->next = r->next;
else s->cb_list = r->next;
return 1;
}
prev = r;
r = r->next;
}
return 0;
}
int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg,
uint16_t qid, uint16_t qflags)
@ -2031,6 +2255,8 @@ mesh_stats_clear(struct mesh_area* mesh)
{
if(!mesh)
return;
mesh->num_query_authzone_up = 0;
mesh->num_query_authzone_down = 0;
mesh->replies_sent = 0;
mesh->replies_sum_wait.tv_sec = 0;
mesh->replies_sum_wait.tv_usec = 0;
@ -2039,6 +2265,7 @@ mesh_stats_clear(struct mesh_area* mesh)
timehist_clear(mesh->histogram);
mesh->ans_secure = 0;
mesh->ans_bogus = 0;
mesh->val_ops = 0;
mesh->ans_expired = 0;
mesh->ans_cachedb = 0;
memset(&mesh->ans_rcode[0], 0, sizeof(size_t)*UB_STATS_RCODE_NUM);
@ -2046,6 +2273,7 @@ mesh_stats_clear(struct mesh_area* mesh)
mesh->ans_nodata = 0;
mesh->num_queries_discard_timeout = 0;
mesh->num_queries_wait_limit = 0;
mesh->num_dns_error_reports = 0;
}
size_t
@ -2147,7 +2375,8 @@ apply_respip_action(struct module_qstate* qstate,
return 1;
if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, actinfo,
alias_rrset, 0, qstate->region, az, NULL))
alias_rrset, 0, qstate->region, az, NULL, qstate->env->views,
qstate->env->respip_set))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
@ -2181,7 +2410,7 @@ mesh_serve_expired_callback(void* arg)
struct timeval tv = {0, 0};
int must_validate = (!(qstate->query_flags&BIT_CD)
|| qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate;
int i = 0;
int i = 0, for_count;
int is_expired;
if(!qstate->serve_expired_data) return;
verbose(VERB_ALGO, "Serve expired: Trying to reply with expired data");
@ -2194,15 +2423,21 @@ mesh_serve_expired_callback(void* arg)
"Serve expired: Not allowed to look into cache for stale");
return;
}
/* The following while is used instead of the `goto lookup_cache`
* like in the worker. */
while(1) {
/* The following for is used instead of the `goto lookup_cache`
* like in the worker. This loop should get max 2 passes if we need to
* do any aliasing. */
for(for_count = 0; for_count < 2; for_count++) {
fptr_ok(fptr_whitelist_serve_expired_lookup(
qstate->serve_expired_data->get_cached_answer));
msg = (*qstate->serve_expired_data->get_cached_answer)(qstate,
lookup_qinfo, &is_expired);
if(!msg)
if(!msg || (FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR
&& FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NXDOMAIN
&& FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_YXDOMAIN)) {
/* We don't care for cached failure answers at this
* stage. */
return;
}
/* Reset these in case we pass a second time from here. */
encode_rep = msg->rep;
memset(&actinfo, 0, sizeof(actinfo));
@ -2216,7 +2451,8 @@ mesh_serve_expired_callback(void* arg)
} else if(partial_rep &&
!respip_merge_cname(partial_rep, &qstate->qinfo, msg->rep,
qstate->client_info, must_validate, &encode_rep, qstate->region,
qstate->env->auth_zones)) {
qstate->env->auth_zones, qstate->env->views,
qstate->env->respip_set)) {
return;
}
if(!encode_rep || alias_rrset) {
@ -2370,3 +2606,25 @@ int mesh_jostle_exceeded(struct mesh_area* mesh)
return 0;
return 1;
}
void mesh_remove_callback(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, mesh_cb_func_type cb, void* cb_arg)
{
struct mesh_state* s = NULL;
s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
if(!s) return;
if(!mesh_state_del_cb(s, cb, cb_arg)) return;
/* It was in the list and removed. */
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
if(!s->reply_list && !s->cb_list) {
/* was a reply state, not anymore */
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
}
if(!s->reply_list && !s->cb_list &&
s->super_set.count == 0) {
mesh->num_detached_states++;
}
}

View file

@ -90,6 +90,11 @@ struct mesh_area {
/** rbtree of all current queries (mesh_state.node)*/
rbtree_type all;
/** number of queries for unbound's auth_zones, upstream query */
size_t num_query_authzone_up;
/** number of queries for unbound's auth_zones, downstream answers */
size_t num_query_authzone_down;
/** count of the total number of mesh_reply entries */
size_t num_reply_addrs;
/** count of the number of mesh_states that have mesh_replies
@ -126,6 +131,8 @@ struct mesh_area {
size_t ans_secure;
/** (extended stats) bogus replies */
size_t ans_bogus;
/** (extended stats) number of validation operations */
size_t val_ops;
/** (extended stats) rcodes in replies */
size_t ans_rcode[UB_STATS_RCODE_NUM];
/** (extended stats) rcode nodata in replies */
@ -136,6 +143,8 @@ struct mesh_area {
size_t num_queries_discard_timeout;
/** stats, number of queries removed due to wait-limit */
size_t num_queries_wait_limit;
/** stats, number of dns error reports generated */
size_t num_dns_error_reports;
/** backup of query if other operations recurse and need the
* network buffers */
@ -701,4 +710,17 @@ int mesh_jostle_exceeded(struct mesh_area* mesh);
*/
void mesh_respond_serve_expired(struct mesh_state* mstate);
/**
* Remove callback from mesh. Removes the callback from the state.
* The state itself is left to run. Searches for the pointer values.
*
* @param mesh: the mesh.
* @param qinfo: query from client.
* @param qflags: flags from client query.
* @param cb: callback function.
* @param cb_arg: callback user arg.
*/
void mesh_remove_callback(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, mesh_cb_func_type cb, void* cb_arg);
#endif /* SERVICES_MESH_H */

View file

@ -138,8 +138,8 @@ modstack_config(struct module_stack* stack, const char* module_conf)
if(strchr(s, ' ')) *(strchr(s, ' ')) = 0;
if(strchr(s, '\t')) *(strchr(s, '\t')) = 0;
log_err("Unknown value in module-config, module: '%s'."
" This module is not present (not compiled in),"
" See the list of linked modules with unbound -V", s);
" This module is not present (not compiled in);"
" see the list of linked modules with unbound -V", s);
return 0;
}
}

View file

@ -67,7 +67,7 @@ void modstack_init(struct module_stack* stack);
void modstack_free(struct module_stack* stack);
/**
* Initialises modules and assignes ids. Calls module_startup().
* Initialises modules and assigns ids. Calls module_startup().
* @param stack: Expected empty, filled according to module_conf
* @param module_conf: string what modules to initialize
* @param env: module environment which is inited by the modules.

View file

@ -262,12 +262,14 @@ pick_outgoing_tcp(struct pending_tcp* pend, struct waiting_tcp* w, int s)
/** get TCP file descriptor for address, returns -1 on failure,
* tcp_mss is 0 or maxseg size to set for TCP packets. */
int
outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss, int dscp)
outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen,
int tcp_mss, int dscp, int nodelay)
{
int s;
int af;
char* err;
#if defined(SO_REUSEADDR) || defined(IP_BIND_ADDRESS_NO_PORT)
#if defined(SO_REUSEADDR) || defined(IP_BIND_ADDRESS_NO_PORT) \
|| defined(TCP_NODELAY)
int on = 1;
#endif
#ifdef INET6
@ -320,6 +322,18 @@ outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss,
" setsockopt(.. IP_BIND_ADDRESS_NO_PORT ..) failed");
}
#endif /* IP_BIND_ADDRESS_NO_PORT */
if(nodelay) {
#if defined(IPPROTO_TCP) && defined(TCP_NODELAY)
if(setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
verbose(VERB_ALGO, "outgoing tcp:"
" setsockopt(.. TCP_NODELAY ..) failed");
}
#else
verbose(VERB_ALGO, "outgoing tcp:"
" setsockopt(.. TCP_NODELAY ..) unsupported");
#endif /* defined(IPPROTO_TCP) && defined(TCP_NODELAY) */
}
return s;
}
@ -649,7 +663,8 @@ outnet_tcp_take_into_use(struct waiting_tcp* w)
}
/* open socket */
s = outnet_get_tcp_fd(&w->addr, w->addrlen, w->outnet->tcp_mss, w->outnet->ip_dscp);
s = outnet_get_tcp_fd(&w->addr, w->addrlen, w->outnet->tcp_mss,
w->outnet->ip_dscp, w->ssl_upstream);
if(s == -1)
return 0;
@ -1054,7 +1069,7 @@ reuse_move_writewait_away(struct outside_network* outnet,
if(verbosity >= VERB_CLIENT && pend->query->pkt_len > 12+2+2 &&
LDNS_QDCOUNT(pend->query->pkt) > 0 &&
dname_valid(pend->query->pkt+12, pend->query->pkt_len-12)) {
char buf[LDNS_MAX_DOMAINLEN+1];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(pend->query->pkt+12, buf);
verbose(VERB_CLIENT, "reuse_move_writewait_away current %s %d bytes were written",
buf, (int)pend->c->tcp_write_byte_count);
@ -1079,7 +1094,7 @@ reuse_move_writewait_away(struct outside_network* outnet,
if(verbosity >= VERB_CLIENT && w->pkt_len > 12+2+2 &&
LDNS_QDCOUNT(w->pkt) > 0 &&
dname_valid(w->pkt+12, w->pkt_len-12)) {
char buf[LDNS_MAX_DOMAINLEN+1];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(w->pkt+12, buf);
verbose(VERB_CLIENT, "reuse_move_writewait_away item %s", buf);
}
@ -2812,7 +2827,7 @@ serviced_perturb_qname(struct ub_randstate* rnd, uint8_t* qbuf, size_t len)
random = ub_random(rnd);
bits = 30;
}
if(random & 0x1) {
if((random & 0x1)) {
*d = (uint8_t)toupper((unsigned char)*d);
} else {
*d = (uint8_t)tolower((unsigned char)*d);
@ -2825,7 +2840,7 @@ serviced_perturb_qname(struct ub_randstate* rnd, uint8_t* qbuf, size_t len)
lablen = *d++;
}
if(verbosity >= VERB_ALGO) {
char buf[LDNS_MAX_DOMAINLEN+1];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(qbuf+10, buf);
verbose(VERB_ALGO, "qname perturbed to %s", buf);
}
@ -2875,9 +2890,9 @@ serviced_encode(struct serviced_query* sq, sldns_buffer* buff, int with_edns)
edns.opt_list_inplace_cb_out = NULL;
edns.udp_size = serviced_query_udp_size(sq, sq->status);
edns.bits = 0;
if(sq->dnssec & EDNS_DO)
if((sq->dnssec & EDNS_DO))
edns.bits = EDNS_DO;
if(sq->dnssec & BIT_CD)
if((sq->dnssec & BIT_CD))
LDNS_CD_SET(sldns_buffer_begin(buff));
if (sq->ssl_upstream && sq->padding_block_size) {
padding_option.opt_code = LDNS_EDNS_PADDING;
@ -3718,7 +3733,8 @@ outnet_comm_point_for_tcp(struct outside_network* outnet,
sldns_buffer* query, int timeout, int ssl, char* host)
{
struct comm_point* cp;
int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss, outnet->ip_dscp);
int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss,
outnet->ip_dscp, ssl);
if(fd == -1) {
return 0;
}
@ -3793,7 +3809,8 @@ outnet_comm_point_for_http(struct outside_network* outnet,
{
/* cp calls cb with err=NETEVENT_DONE when transfer is done */
struct comm_point* cp;
int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss, outnet->ip_dscp);
int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss,
outnet->ip_dscp, ssl);
if(fd == -1) {
return 0;
}

View file

@ -743,9 +743,11 @@ void reuse_write_wait_remove(struct reuse_tcp* reuse, struct waiting_tcp* w);
void reuse_write_wait_push_back(struct reuse_tcp* reuse, struct waiting_tcp* w);
/** get TCP file descriptor for address, returns -1 on failure,
* tcp_mss is 0 or maxseg size to set for TCP packets. */
* tcp_mss is 0 or maxseg size to set for TCP packets,
* nodelay (TCP_NODELAY) should be set for TLS connections to speed up the TLS
* handshake.*/
int outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen,
int tcp_mss, int dscp);
int tcp_mss, int dscp, int nodelay);
/**
* Create udp commpoint suitable for sending packets to the destination.

View file

@ -666,7 +666,7 @@ rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname,
int newzone = 0;
if(a == RPZ_INVALID_ACTION) {
char str[255+1];
char str[LDNS_MAX_DOMAINLEN];
if(rrtype == LDNS_RR_TYPE_SOA || rrtype == LDNS_RR_TYPE_NS ||
rrtype == LDNS_RR_TYPE_DNAME ||
rrtype == LDNS_RR_TYPE_DNSKEY ||
@ -739,7 +739,7 @@ rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname,
static void
rpz_log_dname(char const* msg, uint8_t* dname, size_t dname_len)
{
char buf[LDNS_MAX_DOMAINLEN+1];
char buf[LDNS_MAX_DOMAINLEN];
(void)dname_len;
dname_str(dname, buf);
verbose(VERB_ALGO, "rpz: %s: <%s>", msg, buf);
@ -1062,7 +1062,7 @@ rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
if(a == RPZ_INVALID_ACTION ||
rpz_action_to_respip_action(a) == respip_invalid) {
char str[255+1];
char str[LDNS_MAX_DOMAINLEN];
dname_str(dname, str);
verbose(VERB_ALGO, "rpz: respip trigger, %s skipping unsupported action: %s",
str, rpz_action_to_string(a));
@ -1633,7 +1633,7 @@ log_rpz_apply(char* trigger, uint8_t* dname, struct addr_tree_node* addrnode,
struct comm_reply* repinfo, struct module_qstate* ms, char* log_name)
{
char ip[128], txt[512], portstr[32];
char dnamestr[LDNS_MAX_DOMAINLEN+1];
char dnamestr[LDNS_MAX_DOMAINLEN];
uint16_t port = 0;
if(dname) {
dname_str(dname, dnamestr);
@ -2427,7 +2427,8 @@ rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones,
match->dname = nameserver->name;
match->dname_len = nameserver->namelen;
if(verbosity >= VERB_ALGO) {
char nm[255+1], zn[255+1];
char nm[LDNS_MAX_DOMAINLEN];
char zn[LDNS_MAX_DOMAINLEN];
dname_str(match->dname, nm);
dname_str(z->name, zn);
if(strcmp(nm, zn) != 0)
@ -2581,7 +2582,7 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms,
}
if(verbosity >= VERB_ALGO) {
char nm[255+1], zn[255+1];
char nm[LDNS_MAX_DOMAINLEN], zn[LDNS_MAX_DOMAINLEN];
dname_str(is->qchase.qname, nm);
dname_str(z->name, zn);
if(strcmp(zn, nm) != 0)
@ -2758,7 +2759,7 @@ rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* env,
}
if(verbosity >= VERB_ALGO) {
char nm[255+1], zn[255+1];
char nm[LDNS_MAX_DOMAINLEN], zn[LDNS_MAX_DOMAINLEN];
dname_str(qinfo->qname, nm);
dname_str(z->name, zn);
if(strcmp(zn, nm) != 0)
@ -2791,3 +2792,31 @@ void rpz_disable(struct rpz* r)
return;
r->disabled = 1;
}
/** Get memory usage for clientip_synthesized_rrset. Ignores memory usage
* of locks. */
static size_t
rpz_clientip_synthesized_set_get_mem(struct clientip_synthesized_rrset* set)
{
size_t m = sizeof(*set);
lock_rw_rdlock(&set->lock);
m += regional_get_mem(set->region);
lock_rw_unlock(&set->lock);
return m;
}
size_t rpz_get_mem(struct rpz* r)
{
size_t m = sizeof(*r);
if(r->taglist)
m += r->taglistlen;
if(r->log_name)
m += strlen(r->log_name) + 1;
m += regional_get_mem(r->region);
m += local_zones_get_mem(r->local_zones);
m += local_zones_get_mem(r->nsdname_zones);
m += respip_set_get_mem(r->respip_set);
m += rpz_clientip_synthesized_set_get_mem(r->client_set);
m += rpz_clientip_synthesized_set_get_mem(r->ns_set);
return m;
}

View file

@ -269,4 +269,11 @@ void rpz_enable(struct rpz* r);
*/
void rpz_disable(struct rpz* r);
/**
* Get memory usage of rpz. Caller must manage locks.
* @param r: RPZ struct.
* @return memory usage.
*/
size_t rpz_get_mem(struct rpz* r);
#endif /* SERVICES_RPZ_H */

View file

@ -43,6 +43,7 @@
#include "services/view.h"
#include "services/localzone.h"
#include "util/config_file.h"
#include "respip/respip.h"
int
view_cmp(const void* v1, const void* v2)
@ -66,11 +67,6 @@ views_create(void)
return v;
}
/* \noop (ignore this comment for doxygen)
* This prototype is defined in in respip.h, but we want to avoid
* unnecessary dependencies */
void respip_set_delete(struct respip_set *set);
void
view_delete(struct view* v)
{
@ -247,3 +243,38 @@ void views_print(struct views* v)
/* TODO implement print */
(void)v;
}
size_t views_get_mem(struct views* vs)
{
struct view* v;
size_t m;
if(!vs) return 0;
m = sizeof(struct views);
lock_rw_rdlock(&vs->lock);
RBTREE_FOR(v, struct view*, &vs->vtree) {
m += view_get_mem(v);
}
lock_rw_unlock(&vs->lock);
return m;
}
size_t view_get_mem(struct view* v)
{
size_t m = sizeof(*v);
lock_rw_rdlock(&v->lock);
m += getmem_str(v->name);
m += local_zones_get_mem(v->local_zones);
m += respip_set_get_mem(v->respip_set);
lock_rw_unlock(&v->lock);
return m;
}
void views_swap_tree(struct views* vs, struct views* data)
{
rbnode_type* oldroot = vs->vtree.root;
size_t oldcount = vs->vtree.count;
vs->vtree.root = data->vtree.root;
vs->vtree.count = data->vtree.count;
data->vtree.root = oldroot;
data->vtree.count = oldcount;
}

View file

@ -54,7 +54,8 @@ struct respip_set;
* Views storage, shared.
*/
struct views {
/** lock on the view tree */
/** lock on the view tree. When locking order, the views lock
* is before the forwards,hints,anchors,localzones lock. */
lock_rw_type lock;
/** rbtree of struct view */
rbtree_type vtree;
@ -135,4 +136,27 @@ void views_print(struct views* v);
*/
struct view* views_find_view(struct views* vs, const char* name, int write);
/**
* Calculate memory usage of views.
* @param vs: the views tree. The routine locks and unlocks the structure
* for reading.
* @return memory in bytes.
*/
size_t views_get_mem(struct views* vs);
/**
* Calculate memory usage of view.
* @param v: the view. The routine locks and unlocks the structure for reading.
* @return memory in bytes.
*/
size_t view_get_mem(struct view* v);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param vs: views tree
* @param data: preallocated information.
*/
void views_swap_tree(struct views* vs, struct views* data);
#endif /* SERVICES_VIEW_H */

View file

@ -124,7 +124,7 @@ uint16_t sldns_calc_keytag_raw(uint8_t* key, size_t keysize)
size_t i;
uint32_t ac32 = 0;
for (i = 0; i < keysize; ++i) {
ac32 += (i & 1) ? key[i] : key[i] << 8;
ac32 += ((i & 1)) ? key[i] : key[i] << 8;
}
ac32 += (ac32 >> 16) & 0xFFFF;
return (uint16_t) (ac32 & 0xFFFF);
@ -195,6 +195,7 @@ void sldns_key_EVP_unload_gost(void)
}
#endif /* USE_GOST */
#ifdef USE_DSA
/* Retrieve params as BIGNUM from raw buffer */
static int
sldns_key_dsa_buf_bignum(unsigned char* key, size_t len, BIGNUM** p,
@ -271,7 +272,7 @@ sldns_key_buf2dsa_raw(unsigned char* key, size_t len)
return NULL;
}
if (!DSA_set0_key(dsa, Y, NULL)) {
/* QPG attached, cleaned up by DSA_fre() */
/* QPG attached, cleaned up by DSA_free() */
DSA_free(dsa);
BN_free(Y);
return NULL;
@ -370,6 +371,7 @@ EVP_PKEY *sldns_key_dsa2pkey_raw(unsigned char* key, size_t len)
return evp_key;
#endif
}
#endif /* USE_DSA */
/* Retrieve params as BIGNUM from raw buffer, n is modulus, e is exponent */
static int

View file

@ -72,7 +72,7 @@ static const sldns_rdf_type type_nsap_wireformat[] = {
LDNS_RDF_TYPE_NSAP
};
static const sldns_rdf_type type_nsap_ptr_wireformat[] = {
LDNS_RDF_TYPE_STR
LDNS_RDF_TYPE_UNQUOTED
};
static const sldns_rdf_type type_sig_wireformat[] = {
LDNS_RDF_TYPE_TYPE, LDNS_RDF_TYPE_ALG, LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_INT32,
@ -86,7 +86,7 @@ static const sldns_rdf_type type_px_wireformat[] = {
LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_DNAME, LDNS_RDF_TYPE_DNAME
};
static const sldns_rdf_type type_gpos_wireformat[] = {
LDNS_RDF_TYPE_STR, LDNS_RDF_TYPE_STR, LDNS_RDF_TYPE_STR
LDNS_RDF_TYPE_UNQUOTED, LDNS_RDF_TYPE_UNQUOTED, LDNS_RDF_TYPE_UNQUOTED
};
static const sldns_rdf_type type_aaaa_wireformat[] = { LDNS_RDF_TYPE_AAAA };
static const sldns_rdf_type type_loc_wireformat[] = { LDNS_RDF_TYPE_LOC };
@ -617,6 +617,12 @@ static sldns_rr_descriptor rdata_field_descriptors[] = {
{(enum sldns_enum_rr_type)0, "TYPE258", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
#endif
{(enum sldns_enum_rr_type)0, "TYPE259", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
{(enum sldns_enum_rr_type)0, "TYPE260", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
/* 261 */
{LDNS_RR_TYPE_RESINFO, "RESINFO", 1, 0, NULL, LDNS_RDF_TYPE_UNQUOTED, LDNS_RR_NO_COMPRESS, 0 },
/* split in array, no longer contiguous */
#ifdef DRAFT_RRTYPES

View file

@ -229,6 +229,8 @@ enum sldns_enum_rr_type
LDNS_RR_TYPE_CAA = 257, /* RFC 6844 */
LDNS_RR_TYPE_AVC = 258,
LDNS_RR_TYPE_RESINFO = 261, /* RFC 9606 */
/** DNSSEC Trust Authorities */
LDNS_RR_TYPE_TA = 32768,
/* RFC 4431, 5074, DNSSEC Lookaside Validation */
@ -341,6 +343,9 @@ enum sldns_enum_rdf_type
/** 8 * 8 bit hex numbers separated by dashes. For EUI64. */
LDNS_RDF_TYPE_EUI64,
/** Character string without quotes. */
LDNS_RDF_TYPE_UNQUOTED,
/** A non-zero sequence of US-ASCII letters and numbers in lower case.
* For CAA.
*/
@ -438,6 +443,7 @@ enum sldns_enum_edns_option
LDNS_EDNS_PADDING = 12, /* RFC7830 */
LDNS_EDNS_EDE = 15, /* RFC8914 */
LDNS_EDNS_CLIENT_TAG = 16, /* draft-bellis-dnsop-edns-tags-01 */
LDNS_EDNS_REPORT_CHANNEL = 18, /* RFC9567 */
LDNS_EDNS_UNBOUND_CACHEDB_TESTFRAME_TEST = 65534
};
typedef enum sldns_enum_edns_option sldns_edns_option;

View file

@ -365,7 +365,8 @@ static int
sldns_rdf_type_maybe_quoted(sldns_rdf_type rdf_type)
{
return rdf_type == LDNS_RDF_TYPE_STR ||
rdf_type == LDNS_RDF_TYPE_LONG_STR;
rdf_type == LDNS_RDF_TYPE_LONG_STR ||
rdf_type == LDNS_RDF_TYPE_UNQUOTED;
}
/** see if rdata is quoted */
@ -856,7 +857,7 @@ rrinternal_parse_rdata(sldns_buffer* strbuf, char* token, size_t token_len,
while (rdata_len && *rdata != 0) {
uint8_t label_len;
if (*rdata & 0xC0)
if ((*rdata & 0xC0))
return LDNS_WIREPARSE_ERR_OK;
label_len = *rdata + 1;
@ -1719,6 +1720,8 @@ int sldns_str2wire_rdf_buf(const char* str, uint8_t* rd, size_t* len,
return sldns_str2wire_eui48_buf(str, rd, len);
case LDNS_RDF_TYPE_EUI64:
return sldns_str2wire_eui64_buf(str, rd, len);
case LDNS_RDF_TYPE_UNQUOTED:
return sldns_str2wire_unquoted_buf(str, rd, len);
case LDNS_RDF_TYPE_TAG:
return sldns_str2wire_tag_buf(str, rd, len);
case LDNS_RDF_TYPE_LONG_STR:
@ -2554,12 +2557,42 @@ int sldns_str2wire_atma_buf(const char* str, uint8_t* rd, size_t* len)
{
const char* s = str;
size_t slen = strlen(str);
size_t dlen = 0; /* number of hexdigits parsed */
size_t dlen = 0; /* number of hexdigits parsed for hex,
digits for E.164 */
/* just a hex string with optional dots? */
/* notimpl e.164 format */
if(slen > LDNS_MAX_RDFLEN*2)
return LDNS_WIREPARSE_ERR_LABEL_OVERFLOW;
if(*len < 1)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
if(*s == 0) {
/* empty string */
rd[0] = 0;
*len = 1;
return LDNS_WIREPARSE_ERR_OK;
}
if(s[0] == '+') {
rd[0] = 1; /* E.164 format */
/* digits '0'..'9', with skipped dots. */
s++;
while(*s) {
if(isspace((unsigned char)*s) || *s == '.') {
s++;
continue;
}
if(*s < '0' || *s > '9')
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX, s-str);
if(*len < dlen + 2)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
s-str);
rd[dlen+1] = *s++;
dlen++;
}
*len = dlen+1;
return LDNS_WIREPARSE_ERR_OK;
}
rd[0] = 0; /* AESA format */
/* hex, with skipped dots. */
while(*s) {
if(isspace((unsigned char)*s) || *s == '.') {
s++;
@ -2567,17 +2600,17 @@ int sldns_str2wire_atma_buf(const char* str, uint8_t* rd, size_t* len)
}
if(!isxdigit((unsigned char)*s))
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str);
if(*len < dlen/2 + 1)
if(*len < dlen/2 + 2)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
s-str);
if((dlen&1)==0)
rd[dlen/2] = (uint8_t)sldns_hexdigit_to_int(*s++) * 16;
else rd[dlen/2] += sldns_hexdigit_to_int(*s++);
rd[dlen/2 + 1] = (uint8_t)sldns_hexdigit_to_int(*s++) * 16;
else rd[dlen/2 + 1] += sldns_hexdigit_to_int(*s++);
dlen++;
}
if((dlen&1)!=0)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str);
*len = dlen/2;
*len = dlen/2 + 1;
return LDNS_WIREPARSE_ERR_OK;
}
@ -2746,6 +2779,11 @@ int sldns_str2wire_eui64_buf(const char* str, uint8_t* rd, size_t* len)
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_unquoted_buf(const char* str, uint8_t* rd, size_t* len)
{
return sldns_str2wire_str_buf(str, rd, len);
}
int sldns_str2wire_tag_buf(const char* str, uint8_t* rd, size_t* len)
{
size_t slen = strlen(str);

View file

@ -551,6 +551,15 @@ int sldns_str2wire_eui48_buf(const char* str, uint8_t* rd, size_t* len);
*/
int sldns_str2wire_eui64_buf(const char* str, uint8_t* rd, size_t* len);
/**
* Convert rdf of type LDNS_RDF_TYPE_UNQUOTED from string to wireformat.
* @param str: the text to convert for this rdata element.
* @param rd: rdata buffer for the wireformat.
* @param len: length of rd buffer on input, used length on output.
* @return 0 on success, error on failure.
*/
int sldns_str2wire_unquoted_buf(const char* str, uint8_t* rd, size_t* len);
/**
* Convert rdf of type LDNS_RDF_TYPE_TAG from string to wireformat.
* @param str: the text to convert for this rdata element.

View file

@ -1344,6 +1344,8 @@ int sldns_wire2str_rdf_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen,
return sldns_wire2str_eui48_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_EUI64:
return sldns_wire2str_eui64_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_UNQUOTED:
return sldns_wire2str_unquoted_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_TAG:
return sldns_wire2str_tag_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_LONG_STR:
@ -1870,7 +1872,33 @@ int sldns_wire2str_nsap_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
int sldns_wire2str_atma_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
return print_remainder_hex("", d, dl, s, sl);
uint8_t format;
int w = 0;
size_t i;
if(*dl < 1) return -1;
format = (*d)[0];
(*d)+=1;
(*dl)-=1;
if(format == 0) {
/* AESA format (ATM End System Address). */
return print_remainder_hex("", d, dl, s, sl);
} else if(format == 1) {
/* E.164 format. */
w += sldns_str_print(s, sl, "+");
for(i=0; i<*dl; i++) {
if((*d)[i] < '0' || (*d)[0] > '9')
return -1;
w += sldns_str_print(s, sl, "%c", (*d)[i]);
}
(*d) += *dl;
(*dl) = 0;
} else {
/* Unknown format. */
return -1;
}
return w;
}
/* internal scan routine that can modify arguments on failure */
@ -2021,6 +2049,26 @@ int sldns_wire2str_eui64_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
return w;
}
int sldns_wire2str_unquoted_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int w = 0;
size_t i, len;
if(*dl < 1) return -1;
len = **d;
if(*dl < 1+len) return -1;
(*d)++;
(*dl)--;
for(i=0; i<len; i++) {
if(isspace((unsigned char)(*d)[i]) || (*d)[i] == '(' ||
(*d)[i] == ')' || (*d)[i] == '\'')
w += sldns_str_print(s, sl, "\\%c", (char)(*d)[i]);
else w += str_char_print(s, sl, (*d)[i]);
}
(*d)+=len;
(*dl)-=len;
return w;
}
int sldns_wire2str_tag_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
size_t i, n;

View file

@ -262,7 +262,7 @@ int sldns_wire2str_rdata_unknown_scan(uint8_t** data, size_t* data_len,
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
* @param comprloop: inout bool, that is set true if compression loop failure
* happens. Pass in 0, if passsed in as true, a lower bound is set
* happens. Pass in 0, if passed in as true, a lower bound is set
* on compression loops to stop arbitrary long packet parse times.
* This is meant so you can set it to 0 at the start of a list of dnames,
* and then scan all of them in sequence, if a loop happens, it becomes
@ -919,6 +919,19 @@ int sldns_wire2str_eui48_scan(uint8_t** data, size_t* data_len, char** str,
int sldns_wire2str_eui64_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat UNQUOTED field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_unquoted_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat TAG field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).

View file

@ -382,7 +382,7 @@ read_cert_file(const char* file)
STACK_OF(X509)* sk;
FILE* in;
int content = 0;
char buf[128];
long flen;
if(file == NULL || strcmp(file, "") == 0) {
return NULL;
}
@ -399,6 +399,11 @@ read_cert_file(const char* file)
#endif
return NULL;
}
if(fseek(in, 0, SEEK_END) < 0)
printf("%s fseek: %s\n", file, strerror(errno));
flen = ftell(in);
if(fseek(in, 0, SEEK_SET) < 0)
printf("%s fseek: %s\n", file, strerror(errno));
while(!feof(in)) {
X509* x = PEM_read_X509(in, NULL, NULL, NULL);
if(x == NULL) {
@ -414,8 +419,9 @@ read_cert_file(const char* file)
exit(0);
}
content = 1;
/* read away newline after --END CERT-- */
if(!fgets(buf, (int)sizeof(buf), in))
/* feof may not be true yet, but if the position is
* at end of file, stop reading more certificates. */
if(ftell(in) == flen)
break;
}
fclose(in);

View file

@ -294,7 +294,8 @@ view_and_respipchecks(struct config_file* cfg)
{
struct views* views = NULL;
struct respip_set* respip = NULL;
int ignored = 0;
int have_view_respip_cfg = 0;
int use_response_ip = 0;
if(!(views = views_create()))
fatal_exit("Could not create views: out of memory");
if(!(respip = respip_set_create()))
@ -303,8 +304,11 @@ view_and_respipchecks(struct config_file* cfg)
fatal_exit("Could not set up views");
if(!respip_global_apply_cfg(respip, cfg))
fatal_exit("Could not setup respip set");
if(!respip_views_apply_cfg(views, cfg, &ignored))
if(!respip_views_apply_cfg(views, cfg, &have_view_respip_cfg))
fatal_exit("Could not setup per-view respip sets");
use_response_ip = !respip_set_is_empty(respip) || have_view_respip_cfg;
if(use_response_ip && !strstr(cfg->module_conf, "respip"))
fatal_exit("response-ip options require respip module");
acl_view_tag_checks(cfg, views);
views_delete(views);
respip_set_delete(respip);
@ -342,8 +346,6 @@ interfacechecks(struct config_file* cfg)
int i, j, i2, j2;
char*** resif = NULL;
int* num_resif = NULL;
char portbuf[32];
snprintf(portbuf, sizeof(portbuf), "%d", cfg->port);
if(cfg->num_ifs != 0) {
resif = (char***)calloc(cfg->num_ifs, sizeof(char**));
@ -366,15 +368,19 @@ interfacechecks(struct config_file* cfg)
cfg->ifs[i]);
}
/* check for port combinations that are not supported */
if(if_is_pp2(resif[i][0], portbuf, cfg->proxy_protocol_port)) {
if(if_is_dnscrypt(resif[i][0], portbuf,
if(if_is_pp2(resif[i][0], cfg->port, cfg->proxy_protocol_port)) {
if(if_is_dnscrypt(resif[i][0], cfg->port,
cfg->dnscrypt_port)) {
fatal_exit("PROXYv2 and DNSCrypt combination not "
"supported!");
} else if(if_is_https(resif[i][0], portbuf,
} else if(if_is_https(resif[i][0], cfg->port,
cfg->https_port)) {
fatal_exit("PROXYv2 and DoH combination not "
"supported!");
} else if(if_is_quic(resif[i][0], cfg->port,
cfg->quic_port)) {
fatal_exit("PROXYv2 and DoQ combination not "
"supported!");
}
}
/* search for duplicates in the returned addresses */
@ -448,6 +454,39 @@ ifautomaticportschecks(char* ifautomaticports)
}
}
/** check control interface strings */
static void
controlinterfacechecks(struct config_file* cfg)
{
struct config_strlist* p;
for(p = cfg->control_ifs.first; p; p = p->next) {
struct sockaddr_storage a;
socklen_t alen;
char** rcif = NULL;
int i, num_rcif = 0;
/* See if it is a local socket, starts with a '/'. */
if(p->str && p->str[0] == '/')
continue;
if(!resolve_interface_names(&p->str, 1, NULL, &rcif,
&num_rcif)) {
fatal_exit("could not resolve interface names, for control-interface: %s",
p->str);
}
for(i=0; i<num_rcif; i++) {
if(!extstrtoaddr(rcif[i], &a, &alen,
cfg->control_port)) {
if(strcmp(p->str, rcif[i])!=0)
fatal_exit("cannot parse control-interface address '%s' from the control-interface specified as '%s'",
rcif[i], p->str);
else
fatal_exit("cannot parse control-interface specified as '%s'",
p->str);
}
}
config_del_strarray(rcif, num_rcif);
}
}
/** check acl ips */
static void
aclchecks(struct config_file* cfg)
@ -634,8 +673,10 @@ check_modules_exist(const char* module_conf)
}
n[j] = s[j];
}
fatal_exit("module_conf lists module '%s' but that "
"module is not available.", n);
fatal_exit("Unknown value in module-config, module: "
"'%s'. This module is not present (not "
"compiled in); see the list of linked modules "
"with unbound -V", n);
}
s += strlen(names[i]);
}
@ -924,6 +965,8 @@ morechecks(struct config_file* cfg)
fatal_exit("control-cert-file: \"%s\" does not exist",
cfg->control_cert_file);
}
if(cfg->remote_control_enable)
controlinterfacechecks(cfg);
donotquerylocalhostcheck(cfg);
localzonechecks(cfg);
@ -964,6 +1007,8 @@ check_auth(struct config_file* cfg)
if(!az || !auth_zones_apply_cfg(az, cfg, 0, &is_rpz, NULL, NULL)) {
fatal_exit("Could not setup authority zones");
}
if(is_rpz && !strstr(cfg->module_conf, "respip"))
fatal_exit("RPZ requires the respip module");
auth_zones_delete(az);
}

View file

@ -204,7 +204,8 @@ fi
# remove unused permissions
chmod o-rw \
"$SVR_BASE.pem" \
"$SVR_BASE.key" \
"$SVR_BASE.key"
chmod g+r,o-rw \
"$CTL_BASE.pem" \
"$CTL_BASE.key"

View file

@ -109,6 +109,16 @@ usage(void)
printf(" That means the caches sizes and\n");
printf(" the number of threads must not\n");
printf(" change between reloads.\n");
printf(" fast_reload [+dpv] reloads the server but only briefly stops\n");
printf(" server processing, keeps cache, and changes\n");
printf(" most options; check unbound-control(8).\n");
printf(" +d drops running queries to keep consistency\n");
printf(" on changed options while reloading.\n");
printf(" +p does not pause threads for even faster\n");
printf(" reload but less options are supported\n");
printf(" ; check unbound-control(8).\n");
printf(" +v verbose output, it will include duration needed.\n");
printf(" +vv more verbose output, it will include memory needed.\n");
printf(" stats print statistics\n");
printf(" stats_noreset peek at statistics\n");
#ifdef HAVE_SHMGET
@ -133,6 +143,8 @@ usage(void)
printf(" load_cache load cache from stdin\n");
printf(" (not supported in remote unbounds in\n");
printf(" multi-process operation)\n");
printf(" cache_lookup [+t] <names> print rrsets and msgs at or under the names\n");
printf(" +t allow tld and root names.\n");
printf(" lookup <name> print nameservers for name\n");
printf(" flush [+c] <name> flushes common types for name from cache\n");
printf(" types: A, AAAA, MX, PTR, NS,\n");
@ -234,12 +246,13 @@ static void pr_stats(const char* nm, struct ub_stats_info* s)
PR_UL_NM("num.expired", s->svr.ans_expired);
PR_UL_NM("num.recursivereplies", s->mesh_replies_sent);
#ifdef USE_DNSCRYPT
PR_UL_NM("num.dnscrypt.crypted", s->svr.num_query_dnscrypt_crypted);
PR_UL_NM("num.dnscrypt.cert", s->svr.num_query_dnscrypt_cert);
PR_UL_NM("num.dnscrypt.cleartext", s->svr.num_query_dnscrypt_cleartext);
PR_UL_NM("num.dnscrypt.malformed",
s->svr.num_query_dnscrypt_crypted_malformed);
PR_UL_NM("num.dnscrypt.crypted", s->svr.num_query_dnscrypt_crypted);
PR_UL_NM("num.dnscrypt.cert", s->svr.num_query_dnscrypt_cert);
PR_UL_NM("num.dnscrypt.cleartext", s->svr.num_query_dnscrypt_cleartext);
PR_UL_NM("num.dnscrypt.malformed",
s->svr.num_query_dnscrypt_crypted_malformed);
#endif /* USE_DNSCRYPT */
PR_UL_NM("num.dns_error_reports", s->svr.num_dns_error_reports);
printf("%s.requestlist.avg"SQ"%g\n", nm,
(s->svr.num_queries_missed_cache+s->svr.num_queries_prefetch)?
(double)s->svr.sum_query_list_size/
@ -398,6 +411,7 @@ static void print_extended(struct ub_stats_info* s, int inhibit_zero)
PR_UL("num.answer.secure", s->svr.ans_secure);
PR_UL("num.answer.bogus", s->svr.ans_bogus);
PR_UL("num.rrset.bogus", s->svr.rrset_bogus);
PR_UL("num.valops", s->svr.val_ops);
PR_UL("num.query.aggressive.NOERROR", s->svr.num_neg_cache_noerror);
PR_UL("num.query.aggressive.NXDOMAIN", s->svr.num_neg_cache_nxdomain);
/* threat detection */

Some files were not shown because too many files have changed in this diff Show more