Fix multiple vulnerabilities in unbound.

Approved by:	so
Security:	FreeBSD-SA-20:19.unbound
Security:	CVE-2020-12662
Security:	CVE-2020-12663
This commit is contained in:
gordon 2020-07-08 20:25:06 +00:00 committed by Franco Fichtner
parent 4a601024e7
commit 2dd985b00c
168 changed files with 12051 additions and 3259 deletions

12
contrib/unbound/.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://nlnetlabs.nl/funding/']

View File

@ -0,0 +1,16 @@
sudo: false
language: c
compiler:
- gcc
addons:
apt:
packages:
- libssl-dev
- libevent-dev
- libexpat-dev
- clang
script:
- ./configure --enable-debug --disable-flto
- make
- make test
- (cd testdata/clang-analysis.tdir; bash clang-analysis.test)

File diff suppressed because it is too large Load Diff

38
contrib/unbound/README.md Normal file
View File

@ -0,0 +1,38 @@
# Unbound
[![Travis Build Status](https://travis-ci.org/NLnetLabs/unbound.svg?branch=master)](https://travis-ci.org/NLnetLabs/unbound)
[![Packaging status](https://repology.org/badge/tiny-repos/unbound.svg)](https://repology.org/project/unbound/versions)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/unbound.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:unbound)
Unbound is a validating, recursive, caching DNS resolver. It is designed to be
fast and lean and incorporates modern features based on open standards. If you
have any feedback, we would love to hear from you. Dont hesitate to
[create an issue on Github](https://github.com/NLnetLabs/unbound/issues/new)
or post a message on the [Unbound mailing list](https://lists.nlnetlabs.nl/mailman/listinfo/unbound-users).
You can lean more about Unbound by reading our
[documentation](https://nlnetlabs.nl/documentation/unbound/).
## Compiling
Make sure you have the C toolchain, OpenSSL and its include files, and libexpat
installed. Unbound can be compiled and installed using:
```
./configure && make && make install
```
You can use libevent if you want. libevent is useful when using many (10000)
outgoing ports. By default max 256 ports are opened at the same time and the
builtin alternative is equally capable and a little faster.
Use the `--with-libevent=dir` configure option to compile Unbound with libevent
support.
## Unbound configuration
All of Unbound's configuration options are described in the man pages, which
will be installed and are available on the Unbound
[documentation page](https://nlnetlabs.nl/documentation/unbound/).
An example configuration file is located in
[doc/example.conf](https://github.com/NLnetLabs/unbound/blob/master/doc/example.conf.in).

View File

@ -1,6 +1,6 @@
# generated automatically by aclocal 1.15.1 -*- Autoconf -*-
# generated automatically by aclocal 1.16.1 -*- Autoconf -*-
# Copyright (C) 1996-2017 Free Software Foundation, Inc.
# Copyright (C) 1996-2018 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@ -9390,7 +9390,7 @@ AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"],
# AM_CONDITIONAL -*- Autoconf -*-
# Copyright (C) 1997-2017 Free Software Foundation, Inc.
# Copyright (C) 1997-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@ -9421,7 +9421,7 @@ AC_CONFIG_COMMANDS_PRE(
Usually this means the macro was only invoked conditionally.]])
fi])])
# Copyright (C) 2006-2017 Free Software Foundation, Inc.
# Copyright (C) 2006-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,

View File

@ -64,13 +64,26 @@
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
#ifdef HAVE_LIBKERN_OSBYTEORDER_H
/* In practice this is specific to MacOS X. We assume it doesn't have
* htobe64/be64toh but has alternatives with a different name. */
# include <libkern/OSByteOrder.h>
# define htobe64(x) OSSwapHostToBigInt64(x)
# define be64toh(x) OSSwapBigToHostInt64(x)
#endif
#ifndef HAVE_HTOBE64
# ifdef HAVE_LIBKERN_OSBYTEORDER_H
/* In practice this is specific to MacOS X. We assume it doesn't have
* htobe64/be64toh but has alternatives with a different name. */
# include <libkern/OSByteOrder.h>
# define htobe64(x) OSSwapHostToBigInt64(x)
# define be64toh(x) OSSwapBigToHostInt64(x)
# else
/* not OSX */
/* Some compilers do not define __BYTE_ORDER__, like IBM XLC on AIX */
# if __BIG_ENDIAN__
# define be64toh(n) (n)
# define htobe64(n) (n)
# else
# define be64toh(n) (((uint64_t)htonl((n) & 0xFFFFFFFF) << 32) | htonl((n) >> 32))
# define htobe64(n) (((uint64_t)htonl((n) & 0xFFFFFFFF) << 32) | htonl((n) >> 32))
# endif /* _ENDIAN */
# endif /* HAVE_LIBKERN_OSBYTEORDER_H */
#endif /* HAVE_BE64TOH */
/** the unit test testframe for cachedb, its module state contains
* a cache for a couple queries (in memory). */
@ -205,10 +218,6 @@ static int
cachedb_apply_cfg(struct cachedb_env* cachedb_env, struct config_file* cfg)
{
const char* backend_str = cfg->cachedb_backend;
/* If unspecified we use the in-memory test DB. */
if(!backend_str)
backend_str = "testframe";
cachedb_env->backend = cachedb_find_backend(backend_str);
if(!cachedb_env->backend) {
log_err("cachedb: cannot find backend name '%s'", backend_str);
@ -231,6 +240,8 @@ cachedb_init(struct module_env* env, int id)
env->modinfo[id] = (void*)cachedb_env;
if(!cachedb_apply_cfg(cachedb_env, env->cfg)) {
log_err("cachedb: could not apply configuration settings.");
free(cachedb_env);
env->modinfo[id] = NULL;
return 0;
}
/* see if a backend is selected */
@ -239,9 +250,20 @@ cachedb_init(struct module_env* env, int id)
if(!(*cachedb_env->backend->init)(env, cachedb_env)) {
log_err("cachedb: could not init %s backend",
cachedb_env->backend->name);
free(cachedb_env);
env->modinfo[id] = NULL;
return 0;
}
cachedb_env->enabled = 1;
if(env->cfg->serve_expired_reply_ttl)
log_warn(
"cachedb: serve-expired-reply-ttl is set but not working for data "
"originating from the external cache; 0 TLL is used for those.");
if(env->cfg->serve_expired_client_timeout)
log_warn(
"cachedb: serve-expired-client-timeout is set but not working for "
"data originating from the external cache; expired data are used "
"in the reply without first trying to refresh the data.");
return 1;
}
@ -312,8 +334,7 @@ calc_hash(struct module_qstate* qstate, char* buf, size_t len)
size_t clen = 0;
uint8_t hash[CACHEDB_HASHSIZE/8];
const char* hex = "0123456789ABCDEF";
const char* secret = qstate->env->cfg->cachedb_secret ?
qstate->env->cfg->cachedb_secret : "default";
const char* secret = qstate->env->cfg->cachedb_secret;
size_t i;
/* copy the hash info into the clear buffer */
@ -336,7 +357,11 @@ calc_hash(struct module_qstate* qstate, char* buf, size_t len)
/* hash the buffer */
secalgo_hash_sha256(clear, clen, hash);
#ifdef HAVE_EXPLICIT_BZERO
explicit_bzero(clear, clen);
#else
memset(clear, 0, clen);
#endif
/* hex encode output for portability (some online dbs need
* no nulls, no control characters, and so on) */
@ -412,8 +437,14 @@ good_expiry_and_qinfo(struct module_qstate* qstate, struct sldns_buffer* buf)
&expiry, sizeof(expiry));
expiry = be64toh(expiry);
/* Check if we are allowed to return expired entries:
* - serve_expired needs to be set
* - if SERVE_EXPIRED_TTL is set make sure that the record is not older
* than that. */
if((time_t)expiry < *qstate->env->now &&
!qstate->env->cfg->serve_expired)
(!qstate->env->cfg->serve_expired ||
(SERVE_EXPIRED_TTL &&
*qstate->env->now - (time_t)expiry > SERVE_EXPIRED_TTL)))
return 0;
return 1;
@ -424,15 +455,15 @@ good_expiry_and_qinfo(struct module_qstate* qstate, struct sldns_buffer* buf)
static void
packed_rrset_ttl_subtract(struct packed_rrset_data* data, time_t subtract)
{
size_t i;
size_t total = data->count + data->rrsig_count;
size_t i;
size_t total = data->count + data->rrsig_count;
if(subtract >= 0 && data->ttl > subtract)
data->ttl -= subtract;
else data->ttl = 0;
for(i=0; i<total; i++) {
for(i=0; i<total; i++) {
if(subtract >= 0 && data->rr_ttl[i] > subtract)
data->rr_ttl[i] -= subtract;
else data->rr_ttl[i] = 0;
data->rr_ttl[i] -= subtract;
else data->rr_ttl[i] = 0;
}
}
@ -444,7 +475,8 @@ adjust_msg_ttl(struct dns_msg* msg, time_t adjust)
size_t i;
if(adjust >= 0 && msg->rep->ttl > adjust)
msg->rep->ttl -= adjust;
else msg->rep->ttl = 0;
else
msg->rep->ttl = 0;
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
@ -652,7 +684,8 @@ cachedb_handle_query(struct module_qstate* qstate,
return;
}
/* lookup inside unbound's internal cache */
/* lookup inside unbound's internal cache.
* This does not look for expired entries. */
if(cachedb_intcache_lookup(qstate)) {
if(verbosity >= VERB_ALGO) {
if(qstate->return_msg->rep)
@ -660,8 +693,9 @@ cachedb_handle_query(struct module_qstate* qstate,
&qstate->return_msg->qinfo,
qstate->return_msg->rep);
else log_info("cachedb internal cache lookup: rcode %s",
sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)?
sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)->name:"??");
sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)
?sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)->name
:"??");
}
/* we are done with the query */
qstate->ext_state[id] = module_finished;
@ -676,6 +710,19 @@ cachedb_handle_query(struct module_qstate* qstate,
qstate->return_msg->rep);
/* store this result in internal cache */
cachedb_intcache_store(qstate);
/* In case we have expired data but there is a client timer for expired
* answers, pass execution to next module in order to try updating the
* data first.
* TODO: this needs revisit. The expired data stored from cachedb has
* 0 TTL which is picked up by iterator later when looking in the cache.
* Document that ext cachedb does not work properly with
* serve_stale_reply_ttl yet. */
if(qstate->need_refetch && qstate->serve_expired_data &&
qstate->serve_expired_data->timer) {
qstate->return_msg = NULL;
qstate->ext_state[id] = module_wait_module;
return;
}
/* we are done with the query */
qstate->ext_state[id] = module_finished;
return;

View File

@ -140,6 +140,7 @@ nodevrandom:
static inline void
_rs_init(u_char *buf, size_t n)
{
assert(buf);
if (n < KEYSZ + IVSZ)
return;

View File

@ -0,0 +1,62 @@
/* $OpenBSD: getentropy_freebsd.c,v 1.3 2016/08/07 03:27:21 tb Exp $ */
/*
* Copyright (c) 2014 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* Copyright (c) 2014 Brent Cook <bcook@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Emulation of getentropy(2) as documented at:
* http://man.openbsd.org/getentropy.2
*/
#include <sys/types.h>
#include <sys/sysctl.h>
#include <errno.h>
#include <stddef.h>
/*
* Derived from lib/libc/gen/arc4random.c from FreeBSD.
*/
static size_t
getentropy_sysctl(u_char *buf, size_t size)
{
int mib[2];
size_t len, done;
mib[0] = CTL_KERN;
mib[1] = KERN_ARND;
done = 0;
do {
len = size;
if (sysctl(mib, 2, buf, &len, NULL, 0) == -1)
return (done);
done += len;
buf += len;
size -= len;
} while (size > 0);
return (done);
}
int
getentropy(void *buf, size_t len)
{
if (len <= 256 && getentropy_sysctl(buf, len) == len)
return (0);
errno = EIO;
return (-1);
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: getentropy_linux.c,v 1.20 2014/07/12 15:43:49 beck Exp $ */
/* $OpenBSD: getentropy_linux.c,v 1.46 2018/11/20 08:04:28 deraadt Exp $ */
/*
* Copyright (c) 2014 Theo de Raadt <deraadt@openbsd.org>
@ -15,20 +15,23 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Emulation of getentropy(2) as documented at:
* http://man.openbsd.org/getentropy.2
*/
#include "config.h"
#include "config.h"
/*
#define _POSIX_C_SOURCE 199309L
#define _GNU_SOURCE 1
#define _POSIX_C_SOURCE 199309L
#define _GNU_SOURCE 1
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#ifdef HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#ifdef SYS__sysctl
#include <linux/sysctl.h>
#endif
#include <sys/statvfs.h>
#include <sys/socket.h>
@ -39,6 +42,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <link.h>
#include <termios.h>
#include <fcntl.h>
#include <signal.h>
@ -46,16 +50,18 @@
#include <errno.h>
#include <unistd.h>
#include <time.h>
#if defined(HAVE_SSL)
#ifndef HAVE_NETTLE
#include <openssl/sha.h>
#elif defined(HAVE_NETTLE)
#else
#include <nettle/sha.h>
#define SHA512_CTX struct sha512_ctx
#define SHA512_Init(x) sha512_init(x)
#define SHA512_Update(x, b, s) sha512_update(x, s, b)
#define SHA512_Final(r, c) sha512_digest(c, SHA512_DIGEST_SIZE, r)
#endif
#include <linux/types.h>
#include <linux/random.h>
#include <linux/sysctl.h>
#ifdef HAVE_GETAUXVAL
#include <sys/auxv.h>
#endif
@ -75,29 +81,13 @@
HD(b); \
} while (0)
#if defined(HAVE_SSL)
#define CRYPTO_SHA512_CTX SHA512_CTX
#define CRYPTO_SHA512_INIT(x) SHA512_Init(x)
#define CRYPTO_SHA512_FINAL(r, c) SHA512_Final(r, c)
#define HR(x, l) (SHA512_Update(&ctx, (char *)(x), (l)))
#define HD(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (x)))
#define HF(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (void*)))
#elif defined(HAVE_NETTLE)
#define CRYPTO_SHA512_CTX struct sha512_ctx
#define CRYPTO_SHA512_INIT(x) sha512_init(x)
#define CRYPTO_SHA512_FINAL(r, c) sha512_digest(c, SHA512_DIGEST_SIZE, r)
#define HR(x, l) (sha512_update(&ctx, (l), (uint8_t *)(x)))
#define HD(x) (sha512_update(&ctx, sizeof (x), (uint8_t *)&(x)))
#define HF(x) (sha512_update(&ctx, sizeof (void*), (uint8_t *)&(x)))
#endif
int getentropy(void *buf, size_t len);
#ifdef CAN_REFERENCE_MAIN
extern int main(int, char *argv[]);
#endif
static int gotdata(char *buf, size_t len);
#if defined(SYS_getrandom) && defined(__NR_getrandom)
#if defined(SYS_getrandom) && defined(GRND_NONBLOCK)
static int getentropy_getrandom(void *buf, size_t len);
#endif
static int getentropy_urandom(void *buf, size_t len);
@ -105,6 +95,7 @@ static int getentropy_urandom(void *buf, size_t len);
static int getentropy_sysctl(void *buf, size_t len);
#endif
static int getentropy_fallback(void *buf, size_t len);
static int getentropy_phdr(struct dl_phdr_info *info, size_t size, void *data);
int
getentropy(void *buf, size_t len)
@ -113,18 +104,21 @@ getentropy(void *buf, size_t len)
if (len > 256) {
errno = EIO;
return -1;
return (-1);
}
#if defined(SYS_getrandom) && defined(__NR_getrandom)
#if defined(SYS_getrandom) && defined(GRND_NONBLOCK)
/*
* Try descriptor-less getrandom()
* Try descriptor-less getrandom(), in non-blocking mode.
*
* The design of Linux getrandom is broken. It has an
* uninitialized phase coupled with blocking behaviour, which
* is unacceptable from within a library at boot time without
* possible recovery. See http://bugs.python.org/issue26839#msg267745
*/
ret = getentropy_getrandom(buf, len);
if (ret != -1)
return (ret);
if (errno != ENOSYS)
return (-1);
#endif
/*
@ -178,7 +172,7 @@ getentropy(void *buf, size_t len)
* - Do the best under the circumstances....
*
* This code path exists to bring light to the issue that Linux
* does not provide a failsafe API for entropy collection.
* still does not provide a failsafe API for entropy collection.
*
* We hope this demonstrates that Linux should either retain their
* sysctl ABI, or consider providing a new failsafe API which
@ -196,23 +190,7 @@ getentropy(void *buf, size_t len)
return (ret);
}
/*
* Basic sanity checking; wish we could do better.
*/
static int
gotdata(char *buf, size_t len)
{
char any_set = 0;
size_t i;
for (i = 0; i < len; ++i)
any_set |= buf[i];
if (any_set == 0)
return -1;
return 0;
}
#if defined(SYS_getrandom) && defined(__NR_getrandom)
#if defined(SYS_getrandom) && defined(GRND_NONBLOCK)
static int
getentropy_getrandom(void *buf, size_t len)
{
@ -221,7 +199,7 @@ getentropy_getrandom(void *buf, size_t len)
if (len > 256)
return (-1);
do {
ret = syscall(SYS_getrandom, buf, len, 0);
ret = syscall(SYS_getrandom, buf, len, GRND_NONBLOCK);
} while (ret == -1 && errno == EINTR);
if (ret != (int)len)
@ -269,7 +247,7 @@ start:
}
for (i = 0; i < len; ) {
size_t wanted = len - i;
ssize_t ret = read(fd, (char*)buf + i, wanted);
ssize_t ret = read(fd, (char *)buf + i, wanted);
if (ret == -1) {
if (errno == EAGAIN || errno == EINTR)
@ -280,13 +258,11 @@ start:
i += ret;
}
close(fd);
if (gotdata(buf, len) == 0) {
errno = save_errno;
return 0; /* satisfied */
}
errno = save_errno;
return (0); /* satisfied */
nodevrandom:
errno = EIO;
return -1;
return (-1);
}
#ifdef SYS__sysctl
@ -311,17 +287,15 @@ getentropy_sysctl(void *buf, size_t len)
goto sysctlfailed;
i += chunk;
}
if (gotdata(buf, len) == 0) {
errno = save_errno;
return (0); /* satisfied */
}
errno = save_errno;
return (0); /* satisfied */
sysctlfailed:
errno = EIO;
return -1;
return (-1);
}
#endif /* SYS__sysctl */
static int cl[] = {
static const int cl[] = {
CLOCK_REALTIME,
#ifdef CLOCK_MONOTONIC
CLOCK_MONOTONIC,
@ -346,6 +320,15 @@ static int cl[] = {
#endif
};
static int
getentropy_phdr(struct dl_phdr_info *info, size_t ATTR_UNUSED(size), void *data)
{
SHA512_CTX *ctx = data;
SHA512_Update(ctx, &info->dlpi_addr, sizeof (info->dlpi_addr));
return (0);
}
static int
getentropy_fallback(void *buf, size_t len)
{
@ -357,7 +340,7 @@ getentropy_fallback(void *buf, size_t len)
struct rusage ru;
sigset_t sigset;
struct stat st;
CRYPTO_SHA512_CTX ctx;
SHA512_CTX ctx;
static pid_t lastpid;
pid_t pid;
size_t i, ii, m;
@ -374,7 +357,7 @@ getentropy_fallback(void *buf, size_t len)
}
for (i = 0; i < len; ) {
int j;
CRYPTO_SHA512_INIT(&ctx);
SHA512_Init(&ctx);
for (j = 0; j < repeat; j++) {
HX((e = gettimeofday(&tv, NULL)) == -1, tv);
if (e != -1) {
@ -382,6 +365,8 @@ getentropy_fallback(void *buf, size_t len)
cnt += (int)tv.tv_usec;
}
dl_iterate_phdr(getentropy_phdr, &ctx);
for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]); ii++)
HX(clock_gettime(cl[ii], &ts) == -1, ts);
@ -401,9 +386,6 @@ getentropy_fallback(void *buf, size_t len)
HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1,
sigset);
#ifdef CAN_REFERENCE_MAIN
HF(main); /* an addr in program */
#endif
HF(getentropy); /* an addr in this library */
HF(printf); /* an addr in libc */
p = (char *)&p;
@ -528,33 +510,30 @@ getentropy_fallback(void *buf, size_t len)
HD(cnt);
}
#ifdef HAVE_GETAUXVAL
# ifdef AT_RANDOM
#ifdef AT_RANDOM
/* Not as random as you think but we take what we are given */
p = (char *) getauxval(AT_RANDOM);
if (p)
HR(p, 16);
# endif
# ifdef AT_SYSINFO_EHDR
#endif
#ifdef AT_SYSINFO_EHDR
p = (char *) getauxval(AT_SYSINFO_EHDR);
if (p)
HR(p, pgs);
# endif
# ifdef AT_BASE
#endif
#ifdef AT_BASE
p = (char *) getauxval(AT_BASE);
if (p)
HD(p);
# endif
#endif /* HAVE_GETAUXVAL */
#endif
#endif
CRYPTO_SHA512_FINAL(results, &ctx);
memcpy((char*)buf + i, results, min(sizeof(results), len - i));
SHA512_Final(results, &ctx);
memcpy((char *)buf + i, results, min(sizeof(results), len - i));
i += min(sizeof(results), len - i);
}
memset(results, 0, sizeof results);
if (gotdata(buf, len) == 0) {
errno = save_errno;
return 0; /* satisfied */
}
errno = EIO;
return -1;
explicit_bzero(&ctx, sizeof ctx);
explicit_bzero(results, sizeof results);
errno = save_errno;
return (0); /* satisfied */
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: getentropy_osx.c,v 1.3 2014/07/12 14:48:00 deraadt Exp $ */
/* $OpenBSD: getentropy_osx.c,v 1.12 2018/11/20 08:04:28 deraadt Exp $ */
/*
* Copyright (c) 2014 Theo de Raadt <deraadt@openbsd.org>
@ -15,9 +15,12 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Emulation of getentropy(2) as documented at:
* http://man.openbsd.org/getentropy.2
*/
#include "config.h"
#include <TargetConditionals.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
@ -43,14 +46,18 @@
#include <mach/mach_time.h>
#include <mach/mach_host.h>
#include <mach/host_info.h>
#if TARGET_OS_OSX
#include <sys/socketvar.h>
#include <sys/vmmeter.h>
#endif
#include <netinet/in.h>
#include <netinet/tcp.h>
#if TARGET_OS_OSX
#include <netinet/udp.h>
#include <netinet/ip_var.h>
#include <netinet/tcp_var.h>
#include <netinet/udp_var.h>
#endif
#include <CommonCrypto/CommonDigest.h>
#define SHA512_Update(a, b, c) (CC_SHA512_Update((a), (b), (c)))
#define SHA512_Init(xxx) (CC_SHA512_Init((xxx)))
@ -75,10 +82,6 @@
int getentropy(void *buf, size_t len);
#ifdef CAN_REFERENCE_MAIN
extern int main(int, char *argv[]);
#endif
static int gotdata(char *buf, size_t len);
static int getentropy_urandom(void *buf, size_t len);
static int getentropy_fallback(void *buf, size_t len);
@ -89,7 +92,7 @@ getentropy(void *buf, size_t len)
if (len > 256) {
errno = EIO;
return -1;
return (-1);
}
/*
@ -138,22 +141,6 @@ getentropy(void *buf, size_t len)
return (ret);
}
/*
* Basic sanity checking; wish we could do better.
*/
static int
gotdata(char *buf, size_t len)
{
char any_set = 0;
size_t i;
for (i = 0; i < len; ++i)
any_set |= buf[i];
if (any_set == 0)
return -1;
return 0;
}
static int
getentropy_urandom(void *buf, size_t len)
{
@ -188,7 +175,7 @@ start:
}
for (i = 0; i < len; ) {
size_t wanted = len - i;
ssize_t ret = read(fd, (char*)buf + i, wanted);
ssize_t ret = read(fd, (char *)buf + i, wanted);
if (ret == -1) {
if (errno == EAGAIN || errno == EINTR)
@ -199,18 +186,18 @@ start:
i += ret;
}
close(fd);
if (gotdata(buf, len) == 0) {
errno = save_errno;
return 0; /* satisfied */
}
errno = save_errno;
return (0); /* satisfied */
nodevrandom:
errno = EIO;
return -1;
return (-1);
}
#if TARGET_OS_OSX
static int tcpmib[] = { CTL_NET, AF_INET, IPPROTO_TCP, TCPCTL_STATS };
static int udpmib[] = { CTL_NET, AF_INET, IPPROTO_UDP, UDPCTL_STATS };
static int ipmib[] = { CTL_NET, AF_INET, IPPROTO_IP, IPCTL_STATS };
#endif
static int kmib[] = { CTL_KERN, KERN_USRSTACK };
static int hwmib[] = { CTL_HW, HW_USERMEM };
@ -230,9 +217,11 @@ getentropy_fallback(void *buf, size_t len)
pid_t pid;
size_t i, ii, m;
char *p;
#if TARGET_OS_OSX
struct tcpstat tcpstat;
struct udpstat udpstat;
struct ipstat ipstat;
#endif
u_int64_t mach_time;
unsigned int idata;
void *addr;
@ -267,6 +256,7 @@ getentropy_fallback(void *buf, size_t len)
HX(sysctl(hwmib, sizeof(hwmib) / sizeof(hwmib[0]),
&idata, &ii, NULL, 0) == -1, idata);
#if TARGET_OS_OSX
ii = sizeof(tcpstat);
HX(sysctl(tcpmib, sizeof(tcpmib) / sizeof(tcpmib[0]),
&tcpstat, &ii, NULL, 0) == -1, tcpstat);
@ -278,6 +268,7 @@ getentropy_fallback(void *buf, size_t len)
ii = sizeof(ipstat);
HX(sysctl(ipmib, sizeof(ipmib) / sizeof(ipmib[0]),
&ipstat, &ii, NULL, 0) == -1, ipstat);
#endif
HX((pid = getpid()) == -1, pid);
HX((pid = getsid(pid)) == -1, pid);
@ -295,9 +286,6 @@ getentropy_fallback(void *buf, size_t len)
HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1,
sigset);
#ifdef CAN_REFERENCE_MAIN
HF(main); /* an addr in program */
#endif
HF(getentropy); /* an addr in this library */
HF(printf); /* an addr in libc */
p = (char *)&p;
@ -419,14 +407,11 @@ getentropy_fallback(void *buf, size_t len)
}
SHA512_Final(results, &ctx);
memcpy((char*)buf + i, results, min(sizeof(results), len - i));
memcpy((char *)buf + i, results, min(sizeof(results), len - i));
i += min(sizeof(results), len - i);
}
memset(results, 0, sizeof results);
if (gotdata(buf, len) == 0) {
errno = save_errno;
return 0; /* satisfied */
}
errno = EIO;
return -1;
explicit_bzero(&ctx, sizeof ctx);
explicit_bzero(results, sizeof results);
errno = save_errno;
return (0); /* satisfied */
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: getentropy_solaris.c,v 1.3 2014/07/12 14:46:31 deraadt Exp $ */
/* $OpenBSD: getentropy_solaris.c,v 1.4 2014/07/12 20:41:47 wouter Exp $ */
/*
* Copyright (c) 2014 Theo de Raadt <deraadt@openbsd.org>
@ -204,7 +204,7 @@ start:
}
for (i = 0; i < len; ) {
size_t wanted = len - i;
ssize_t ret = read(fd, (char*)buf + i, wanted);
ssize_t ret = read(fd, (char *)buf + i, wanted);
if (ret == -1) {
if (errno == EAGAIN || errno == EINTR)
@ -428,7 +428,7 @@ getentropy_fallback(void *buf, size_t len)
HD(cnt);
}
SHA512_Final(results, &ctx);
memcpy((char*)buf + i, results, min(sizeof(results), len - i));
memcpy((char *)buf + i, results, min(sizeof(results), len - i));
i += min(sizeof(results), len - i);
}
memset(results, 0, sizeof results);

View File

@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: getentropy_win.c,v 1.5 2016/08/07 03:27:21 tb Exp $ */
/*
* Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
@ -15,6 +15,9 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Emulation of getentropy(2) as documented at:
* http://man.openbsd.org/getentropy.2
*/
#include <windows.h>
@ -37,7 +40,7 @@ getentropy(void *buf, size_t len)
if (len > 256) {
errno = EIO;
return -1;
return (-1);
}
if (CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,

View File

@ -5,7 +5,12 @@
#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

@ -658,7 +658,7 @@ int vsnprintf(char* str, size_t size, const char* format, va_list arg)
* are not their own functions. */
/* printout designation:
* conversion specifier: x, d, u, s, c, n, m, p
* conversion specifier: x, d, u, s, c, m, p
* flags: # not supported
* 0 zeropad (on the left)
* - left adjust (right by default)
@ -798,7 +798,10 @@ int vsnprintf(char* str, size_t size, const char* format, va_list arg)
minw, minus);
break;
case 'n':
*va_arg(arg, int*) = ret;
/* unsupported to harden against format string
* exploitation,
* handled like an unknown format specifier. */
/* *va_arg(arg, int*) = ret; */
break;
case 'm':
print_str(&at, &left, &ret, strerror(errno),

View File

@ -1,4 +1,4 @@
#! /bin/sh
#!/usr/bin/sh
# Attempt to guess a canonical system name.
# Copyright 1992-2016 Free Software Foundation, Inc.

View File

@ -15,6 +15,9 @@
/* Do sha512 definitions in config.h */
#undef COMPAT_SHA512
/* Command line arguments used with configure */
#undef CONFCMDLINE
/* Pathname to the Unbound configuration file */
#undef CONFIGFILE
@ -60,6 +63,15 @@
/* Whether the C compiler accepts the "weak" attribute */
#undef HAVE_ATTR_WEAK
/* If we have be64toh */
#undef HAVE_BE64TOH
/* Define to 1 if you have the <bsd/stdlib.h> header file. */
#undef HAVE_BSD_STDLIB_H
/* Define to 1 if you have the <bsd/string.h> header file. */
#undef HAVE_BSD_STRING_H
/* Define to 1 if you have the `chown' function. */
#undef HAVE_CHOWN
@ -69,6 +81,9 @@
/* Define to 1 if you have the `CRYPTO_cleanup_all_ex_data' function. */
#undef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
/* Define to 1 if you have the `CRYPTO_THREADID_set_callback' function. */
#undef HAVE_CRYPTO_THREADID_SET_CALLBACK
/* Define to 1 if you have the `ctime_r' function. */
#undef HAVE_CTIME_R
@ -83,6 +98,10 @@
if you don't. */
#undef HAVE_DECL_ARC4RANDOM_UNIFORM
/* Define to 1 if you have the declaration of `evsignal_assign', and to 0 if
you don't. */
#undef HAVE_DECL_EVSIGNAL_ASSIGN
/* Define to 1 if you have the declaration of `inet_ntop', and to 0 if you
don't. */
#undef HAVE_DECL_INET_NTOP
@ -163,6 +182,9 @@
/* Define to 1 if you have the `ERR_load_crypto_strings' function. */
#undef HAVE_ERR_LOAD_CRYPTO_STRINGS
/* Define to 1 if you have the `event_assign' function. */
#undef HAVE_EVENT_ASSIGN
/* Define to 1 if you have the `event_base_free' function. */
#undef HAVE_EVENT_BASE_FREE
@ -178,6 +200,9 @@
/* Define to 1 if you have the <event.h> header file. */
#undef HAVE_EVENT_H
/* Define to 1 if you have the `EVP_aes_256_cbc' function. */
#undef HAVE_EVP_AES_256_CBC
/* Define to 1 if you have the `EVP_cleanup' function. */
#undef HAVE_EVP_CLEANUP
@ -187,6 +212,9 @@
/* Define to 1 if you have the `EVP_dss1' function. */
#undef HAVE_EVP_DSS1
/* Define to 1 if you have the `EVP_EncryptInit_ex' function. */
#undef HAVE_EVP_ENCRYPTINIT_EX
/* Define to 1 if you have the `EVP_MD_CTX_new' function. */
#undef HAVE_EVP_MD_CTX_NEW
@ -259,9 +287,15 @@
/* Define to 1 if you have the <hiredis/hiredis.h> header file. */
#undef HAVE_HIREDIS_HIREDIS_H
/* Define to 1 if you have the `HMAC_Init_ex' function. */
#undef HAVE_HMAC_INIT_EX
/* If you have HMAC_Update */
#undef HAVE_HMAC_UPDATE
/* If we have htobe64 */
#undef HAVE_HTOBE64
/* Define to 1 if you have the `inet_aton' function. */
#undef HAVE_INET_ATON
@ -289,6 +323,9 @@
/* Define to 1 if you have the `kill' function. */
#undef HAVE_KILL
/* Use portable libbsd functions */
#undef HAVE_LIBBSD
/* Define to 1 if you have the <libkern/OSByteOrder.h> header file. */
#undef HAVE_LIBKERN_OSBYTEORDER_H
@ -394,7 +431,7 @@
/* Define to 1 if you have the `RAND_cleanup' function. */
#undef HAVE_RAND_CLEANUP
/* Define to 1 if you have the `reallocarray' function. */
/* If we have reallocarray(3) */
#undef HAVE_REALLOCARRAY
/* Define to 1 if you have the `recvmsg' function. */
@ -451,9 +488,15 @@
/* Define if you have the SSL libraries installed. */
#undef HAVE_SSL
/* Define to 1 if you have the `SSL_CTX_set_ciphersuites' function. */
#undef HAVE_SSL_CTX_SET_CIPHERSUITES
/* Define to 1 if you have the `SSL_CTX_set_security_level' function. */
#undef HAVE_SSL_CTX_SET_SECURITY_LEVEL
/* Define to 1 if you have the `SSL_CTX_set_tlsext_ticket_key_cb' function. */
#undef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_CB
/* Define to 1 if you have the `SSL_get0_peername' function. */
#undef HAVE_SSL_GET0_PEERNAME
@ -586,9 +629,15 @@
/* Define to 1 if you have the <ws2tcpip.h> header file. */
#undef HAVE_WS2TCPIP_H
/* Define to 1 if you have the `X509_VERIFY_PARAM_set1_host' function. */
#undef HAVE_X509_VERIFY_PARAM_SET1_HOST
/* Define to 1 if you have the `_beginthreadex' function. */
#undef HAVE__BEGINTHREADEX
/* If HMAC_Init_ex() returns void */
#undef HMAC_INIT_EX_RETURNS_VOID
/* if lex has yylex_destroy */
#undef LEX_HAS_YYLEX_DESTROY
@ -681,6 +730,9 @@
/* Shared data */
#undef SHARE_DIR
/* The size of `size_t', as computed by sizeof. */
#undef SIZEOF_SIZE_T
/* The size of `time_t', as computed by sizeof. */
#undef SIZEOF_TIME_T
@ -699,6 +751,9 @@
/* Use win32 resources and API */
#undef UB_ON_WINDOWS
/* the SYSLOG_FACILITY to use, default LOG_DAEMON */
#undef UB_SYSLOG_FACILITY
/* default username */
#undef UB_USERNAME
@ -747,6 +802,9 @@
/* Define to 1 to use ipsecmod support. */
#undef USE_IPSECMOD
/* Define to 1 to use ipset support */
#undef USE_IPSET
/* Define if you want to use internal select based events */
#undef USE_MINI_EVENT
@ -944,8 +1002,14 @@
#ifndef _OPENBSD_SOURCE
#define _OPENBSD_SOURCE 1
#endif
#ifndef UNBOUND_DEBUG
# ifndef NDEBUG
# define NDEBUG
# endif
#endif
/** Use small-ldns codebase */
@ -1178,6 +1242,15 @@ struct tm;
char *strptime(const char *s, const char *format, struct tm *tm);
#endif
#if !HAVE_DECL_REALLOCARRAY
void *reallocarray(void *ptr, size_t nmemb, size_t size);
#endif
#ifdef HAVE_LIBBSD
#include <bsd/string.h>
#include <bsd/stdlib.h>
#endif
#ifdef HAVE_LIBRESSL
# if !HAVE_DECL_STRLCPY
size_t strlcpy(char *dst, const char *src, size_t siz);
@ -1191,9 +1264,6 @@ uint32_t arc4random(void);
# if !HAVE_DECL_ARC4RANDOM_UNIFORM && defined(HAVE_ARC4RANDOM_UNIFORM)
uint32_t arc4random_uniform(uint32_t upper_bound);
# endif
# if !HAVE_DECL_REALLOCARRAY
void *reallocarray(void *ptr, size_t nmemb, size_t size);
# endif
#endif /* HAVE_LIBRESSL */
#ifndef HAVE_ARC4RANDOM
int getentropy(void* buf, size_t len);

View File

@ -1,4 +1,4 @@
#! /bin/sh
#!/usr/bin/sh
# Configuration validation subroutine script.
# Copyright 1992-2016 Free Software Foundation, Inc.

File diff suppressed because it is too large Load Diff

View File

@ -10,16 +10,16 @@ sinclude(dnscrypt/dnscrypt.m4)
# must be numbers. ac_defun because of later processing
m4_define([VERSION_MAJOR],[1])
m4_define([VERSION_MINOR],[8])
m4_define([VERSION_MINOR],[10])
m4_define([VERSION_MICRO],[1])
AC_INIT(unbound, m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]), unbound-bugs@nlnetlabs.nl, unbound)
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=8
LIBUNBOUND_REVISION=1
LIBUNBOUND_AGE=0
LIBUNBOUND_CURRENT=9
LIBUNBOUND_REVISION=8
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
# 1.0.2 had 0:14:0
@ -83,6 +83,17 @@ LIBUNBOUND_AGE=0
# 1.7.3 had 7:11:5
# 1.8.0 had 8:0:0 # changes the event callback function signature
# 1.8.1 had 8:1:0
# 1.8.2 had 8:2:0
# 1.8.3 had 8:3:0
# 1.9.0 had 9:0:1 # add ub_ctx_set_tls
# 1.9.1 had 9:1:1
# 1.9.2 had 9:2:1
# 1.9.3 had 9:3:1
# 1.9.4 had 9:4:1
# 1.9.5 had 9:5:1
# 1.9.6 had 9:6:1
# 1.10.0 had 9:7:1
# 1.10.1 had 9:8:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
@ -106,6 +117,10 @@ AC_SUBST(LIBUNBOUND_CURRENT)
AC_SUBST(LIBUNBOUND_REVISION)
AC_SUBST(LIBUNBOUND_AGE)
cmdln="`echo $@ | sed -e 's/\\\\/\\\\\\\\/g' | sed -e 's/"/\\\\"/'g`"
AC_DEFINE_UNQUOTED(CONFCMDLINE, ["$cmdln"], [Command line arguments used with configure])
CFLAGS="$CFLAGS"
AC_AIX
if test "$ac_cv_header_minix_config_h" = "yes"; then
@ -379,6 +394,8 @@ AC_CHECK_PROG(doxygen, doxygen, doxygen)
AC_CHECK_TOOL(STRIP, strip)
ACX_LIBTOOL_C_ONLY
PKG_PROG_PKG_CONFIG
# Checks for header files.
AC_CHECK_HEADERS([stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.h sys/socket.h sys/un.h sys/uio.h sys/resource.h arpa/inet.h syslog.h netdb.h sys/wait.h pwd.h glob.h grp.h login_cap.h winsock2.h ws2tcpip.h endian.h sys/endian.h libkern/OSByteOrder.h sys/ipc.h sys/shm.h],,, [AC_INCLUDES_DEFAULT])
@ -417,6 +434,7 @@ AC_INCLUDES_DEFAULT
# endif
#endif
])
AC_CHECK_SIZEOF(size_t)
# add option to disable the evil rpath
ACX_ARG_RPATH
@ -462,12 +480,16 @@ ACX_MKDIR_ONE_ARG
AC_CHECK_FUNCS([strptime],[AC_CHECK_STRPTIME_WORKS],[AC_LIBOBJ([strptime])])
# check if we can use SO_REUSEPORT
if echo "$host" | grep -i -e linux -e dragonfly >/dev/null; then
if echo "$host" | $GREP -i -e linux -e dragonfly >/dev/null; then
AC_DEFINE(REUSEPORT_DEFAULT, 1, [if REUSEPORT is enabled by default])
else
AC_DEFINE(REUSEPORT_DEFAULT, 0, [if REUSEPORT is enabled by default])
fi
# Include systemd.m4 - begin
sinclude(systemd.m4)
# Include systemd.m4 - end
# set memory allocation checking if requested
AC_ARG_ENABLE(alloc-checks, AC_HELP_STRING([--enable-alloc-checks],
[ enable to memory allocation statistics, for debug purposes ]),
@ -483,6 +505,10 @@ if test x_$enable_alloc_nonregional = x_yes; then
fi
if test x_$enable_alloc_checks = x_yes; then
AC_DEFINE(UNBOUND_ALLOC_STATS, 1, [use statistics for allocs and frees, for debug use])
SLDNS_ALLOCCHECK_EXTRA_OBJ="alloc.lo log.lo"
AC_SUBST(SLDNS_ALLOCCHECK_EXTRA_OBJ)
ASYNCLOOK_ALLOCCHECK_EXTRA_OBJ="alloc.lo"
AC_SUBST(ASYNCLOOK_ALLOCCHECK_EXTRA_OBJ)
else
if test x_$enable_alloc_lite = x_yes; then
AC_DEFINE(UNBOUND_ALLOC_LITE, 1, [use to enable lightweight alloc assertions, for debug use])
@ -586,6 +612,18 @@ fi
fi # end of non-mingw check of thread libraries
# Check for SYSLOG_FACILITY
AC_ARG_WITH(syslog-facility, AC_HELP_STRING([--with-syslog-facility=LOCAL0 - LOCAL7], [ set SYSLOG_FACILITY, default DAEMON ]),
[ UNBOUND_SYSLOG_FACILITY="$withval" ], [])
case "${UNBOUND_SYSLOG_FACILITY}" in
LOCAL[[0-7]]) UNBOUND_SYSLOG_FACILITY="LOG_${UNBOUND_SYSLOG_FACILITY}" ;;
*) UNBOUND_SYSLOG_FACILITY="LOG_DAEMON" ;;
esac
AC_DEFINE_UNQUOTED(UB_SYSLOG_FACILITY,${UNBOUND_SYSLOG_FACILITY},[the SYSLOG_FACILITY to use, default LOG_DAEMON])
# Check for PyUnbound
AC_ARG_WITH(pyunbound,
AC_HELP_STRING([--with-pyunbound],
@ -638,7 +676,6 @@ if test x_$ub_test_python != x_no; then
CPPFLAGS="$PYTHON_CPPFLAGS"
fi
ub_have_python=yes
PKG_PROG_PKG_CONFIG
PKG_CHECK_EXISTS(["python${PY_MAJOR_VERSION}"],
[PC_PY_DEPENDENCY="python${PY_MAJOR_VERSION}"],
[PC_PY_DEPENDENCY="python"])
@ -725,6 +762,8 @@ AC_ARG_WITH([nss], AC_HELP_STRING([--with-nss=path],
fi
LIBS="$LIBS -lnss3 -lnspr4"
SSLLIB=""
PC_CRYPTO_DEPENDENCY="nss nspr"
AC_SUBST(PC_CRYPTO_DEPENDENCY)
]
)
@ -745,6 +784,8 @@ AC_ARG_WITH([nettle], AC_HELP_STRING([--with-nettle=path],
fi
LIBS="$LIBS -lhogweed -lnettle -lgmp"
SSLLIB=""
PC_CRYPTO_DEPENDENCY="hogweed nettle"
AC_SUBST(PC_CRYPTO_DEPENDENCY)
]
)
@ -754,6 +795,9 @@ ACX_WITH_SSL
ACX_LIB_SSL
SSLLIB="-lssl"
PC_CRYPTO_DEPENDENCY="libcrypto libssl"
AC_SUBST(PC_CRYPTO_DEPENDENCY)
# check if -lcrypt32 is needed because CAPIENG needs that. (on windows)
BAKLIBS="$LIBS"
LIBS="-lssl $LIBS"
@ -773,17 +817,17 @@ if grep VERSION_TEXT $ssldir/include/openssl/opensslv.h | grep "LibreSSL" >/dev/
AC_DEFINE([HAVE_LIBRESSL], [1], [Define if we have LibreSSL])
# libressl provides these compat functions, but they may also be
# declared by the OS in libc. See if they have been declared.
AC_CHECK_DECLS([strlcpy,strlcat,arc4random,arc4random_uniform,reallocarray])
AC_CHECK_DECLS([strlcpy,strlcat,arc4random,arc4random_uniform])
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],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_FUNCS([OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup DSA_SIG_set0 EVP_dss1 EVP_DigestVerify])
AC_CHECK_FUNCS([OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup DSA_SIG_set0 EVP_dss1 EVP_DigestVerify SSL_CTX_set_tlsext_ticket_key_cb EVP_aes_256_cbc EVP_EncryptInit_ex HMAC_Init_ex CRYPTO_THREADID_set_callback])
# these check_funcs need -lssl
BAKLIBS="$LIBS"
LIBS="-lssl $LIBS"
AC_CHECK_FUNCS([OPENSSL_init_ssl SSL_CTX_set_security_level SSL_set1_host SSL_get0_peername])
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])
LIBS="$BAKLIBS"
AC_CHECK_DECLS([SSL_COMP_get_compression_methods,sk_SSL_COMP_pop_free,SSL_CTX_set_ecdh_auto], [], [], [
@ -806,9 +850,58 @@ AC_INCLUDES_DEFAULT
#include <openssl/ssl.h>
#include <openssl/evp.h>
])
if test "$ac_cv_func_HMAC_Init_ex" = "yes"; then
# check function return type.
AC_MSG_CHECKING(the return type of HMAC_Init_ex)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
#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>
], [
HMAC_CTX* hmac_ctx = NULL;
void* hmac_key = NULL;
const EVP_MD* digest = NULL;
int x = HMAC_Init_ex(hmac_ctx, hmac_key, 32, digest, NULL);
(void)x;
])], [
AC_MSG_RESULT(int)
], [
AC_MSG_RESULT(void)
AC_DEFINE([HMAC_INIT_EX_RETURNS_VOID], 1, [If HMAC_Init_ex() returns void])
])
fi
fi
AC_SUBST(SSLLIB)
# libbsd
AC_ARG_WITH([libbsd], AC_HELP_STRING([--with-libbsd], [Use portable libbsd functions]), [
AC_CHECK_HEADERS([bsd/string.h bsd/stdlib.h],,, [AC_INCLUDES_DEFAULT])
if test "x$ac_cv_header_bsd_string_h" = xyes -a "x$ac_cv_header_bsd_stdlib_h" = xyes; then
for func in strlcpy strlcat arc4random arc4random_uniform reallocarray; do
AC_SEARCH_LIBS([$func], [bsd], [
AC_DEFINE(HAVE_LIBBSD, 1, [Use portable libbsd functions])
PC_LIBBSD_DEPENDENCY=libbsd
AC_SUBST(PC_LIBBSD_DEPENDENCY)
])
done
fi
])
AC_ARG_ENABLE(sha1, AC_HELP_STRING([--disable-sha1], [Disable SHA1 RRSIG support, does not disable nsec3 support]))
case "$enable_sha1" in
@ -993,9 +1086,7 @@ esac
AC_ARG_ENABLE(dsa, AC_HELP_STRING([--disable-dsa], [Disable DSA support]))
use_dsa="no"
case "$enable_dsa" in
no)
;;
*)
yes)
# detect if DSA is supported, and turn it off if not.
if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then
AC_CHECK_FUNC(DSA_SIG_new, [
@ -1026,6 +1117,10 @@ AC_INCLUDES_DEFAULT
AC_DEFINE_UNQUOTED([USE_DSA], [1], [Define this to enable DSA support.])
fi
;;
*)
# disable dsa by default, RFC 8624 section 3.1, validators MUST NOT
# support DSA for DNSSEC Validation.
;;
esac
AC_ARG_ENABLE(ed25519, AC_HELP_STRING([--disable-ed25519], [Disable ED25519 support]))
@ -1194,6 +1289,14 @@ large outgoing port ranges. ])
AC_CHECK_FUNCS([event_base_get_method]) # only in libevent 1.4.3 and later
AC_CHECK_FUNCS([ev_loop]) # only in libev. (tested on 3.51)
AC_CHECK_FUNCS([ev_default_loop]) # only in libev. (tested on 4.00)
AC_CHECK_FUNCS([event_assign]) # in libevent, for thread-safety
AC_CHECK_DECLS([evsignal_assign], [], [], [AC_INCLUDES_DEFAULT
#ifdef HAVE_EVENT_H
# include <event.h>
#else
# include "event2/event.h"
#endif
])
PC_LIBEVENT_DEPENDENCY="libevent"
AC_SUBST(PC_LIBEVENT_DEPENDENCY)
if test -n "$BAK_LDFLAGS_SET"; then
@ -1262,11 +1365,11 @@ if test x_$withval = x_yes -o x_$withval != x_no; then
])
fi
# set static linking if requested
# set static linking for uninstalled libraries if requested
AC_SUBST(staticexe)
staticexe=""
AC_ARG_ENABLE(static-exe, AC_HELP_STRING([--enable-static-exe],
[ enable to compile executables statically against (event) libs, for debug purposes ]),
[ enable to compile executables statically against (event) uninstalled libs, for debug purposes ]),
, )
if test x_$enable_static_exe = x_yes; then
staticexe="-static"
@ -1282,9 +1385,22 @@ if test x_$enable_static_exe = x_yes; then
fi
fi
# Include systemd.m4 - begin
sinclude(systemd.m4)
# Include systemd.m4 - end
# set full static linking if requested
AC_ARG_ENABLE(fully-static, AC_HELP_STRING([--enable-fully-static],
[ enable to compile fully static ]),
, )
if test x_$enable_fully_static = x_yes; then
staticexe="-all-static"
if test "$on_mingw" = yes; then
# for static compile, include gdi32 and zlib here.
if echo $LIBS | grep 'lgdi32' >/dev/null; then
:
else
LIBS="$LIBS -lgdi32"
fi
LIBS="$LIBS -lz"
fi
fi
# set lock checking if requested
AC_ARG_ENABLE(lock_checks, AC_HELP_STRING([--enable-lock-checks],
@ -1375,6 +1491,35 @@ AC_INCLUDES_DEFAULT
#include <ws2tcpip.h>
#endif
])
AC_MSG_CHECKING([for htobe64])
AC_LINK_IFELSE([AC_LANG_PROGRAM([
#include <stdio.h>
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
], [unsigned long long x = htobe64(0); printf("%u", (unsigned)x);])],
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_HTOBE64, 1, [If we have htobe64]),
AC_MSG_RESULT(no))
AC_MSG_CHECKING([for be64toh])
AC_LINK_IFELSE([AC_LANG_PROGRAM([
#include <stdio.h>
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
], [unsigned long long x = be64toh(0); printf("%u", (unsigned)x);])],
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_BE64TOH, 1, [If we have be64toh]),
AC_MSG_RESULT(no))
AC_SEARCH_LIBS([setusercontext], [util])
AC_CHECK_FUNCS([tzset sigprocmask fcntl getpwnam endpwent getrlimit setrlimit setsid chroot kill chown sleep usleep random srandom recvmsg sendmsg writev socketpair glob initgroups strftime localtime_r setusercontext _beginthreadex endservent endprotoent fsync shmget accept4])
AC_CHECK_FUNCS([setresuid],,[AC_CHECK_FUNCS([setreuid])])
@ -1433,7 +1578,25 @@ AC_REPLACE_FUNCS(explicit_bzero)
dnl without CTIME, ARC4-functions and without reallocarray.
LIBOBJ_WITHOUT_CTIMEARC4="$LIBOBJS"
AC_SUBST(LIBOBJ_WITHOUT_CTIMEARC4)
AC_REPLACE_FUNCS(reallocarray)
AC_MSG_CHECKING([for reallocarray])
AC_LINK_IFELSE([AC_LANG_SOURCE(AC_INCLUDES_DEFAULT
[[
#ifndef _OPENBSD_SOURCE
#define _OPENBSD_SOURCE 1
#endif
#include <stdlib.h>
int main(void) {
void* p = reallocarray(NULL, 10, 100);
free(p);
return 0;
}
]])], [AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_REALLOCARRAY, 1, [If we have reallocarray(3)])
], [
AC_MSG_RESULT(no)
AC_LIBOBJ(reallocarray)
])
AC_CHECK_DECLS([reallocarray])
if test "$USE_NSS" = "no"; then
AC_REPLACE_FUNCS(arc4random)
AC_REPLACE_FUNCS(arc4random_uniform)
@ -1460,6 +1623,9 @@ if test "$USE_NSS" = "no"; then
fi
AC_SEARCH_LIBS([clock_gettime], [rt])
;;
*freebsd*|*FreeBSD)
AC_LIBOBJ(getentropy_freebsd)
;;
*linux*|Linux|*)
AC_LIBOBJ(getentropy_linux)
AC_CHECK_FUNCS([SHA512_Update],,[
@ -1572,6 +1738,47 @@ case "$enable_ipsecmod" in
;;
esac
# check for ipset if requested
AC_ARG_ENABLE(ipset, AC_HELP_STRING([--enable-ipset], [enable ipset module]))
case "$enable_ipset" in
yes)
AC_DEFINE([USE_IPSET], [1], [Define to 1 to use ipset support])
IPSET_SRC="ipset/ipset.c"
AC_SUBST(IPSET_SRC)
IPSET_OBJ="ipset.lo"
AC_SUBST(IPSET_OBJ)
# mnl
AC_ARG_WITH(libmnl, AC_HELP_STRING([--with-libmnl=path],
[specify explicit path for libmnl.]),
[ ],[ withval="yes" ])
found_libmnl="no"
AC_MSG_CHECKING(for libmnl)
if test x_$withval = x_ -o x_$withval = x_yes; then
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
fi
for dir in $withval ; do
if test -f "$dir/include/libmnl/libmnl.h"; then
found_libmnl="yes"
dnl assume /usr is in default path.
if test "$dir" != "/usr"; then
CPPFLAGS="$CPPFLAGS -I$dir/include"
LDFLAGS="$LDFLAGS -L$dir/lib"
fi
AC_MSG_RESULT(found in $dir)
LIBS="$LIBS -lmnl"
break;
fi
done
if test x_$found_libmnl != x_yes; then
AC_ERROR([Could not find libmnl, libmnl.h])
fi
;;
no|*)
# nothing
;;
esac
AC_MSG_CHECKING([if ${MAKE:-make} supports $< with implicit rule in scope])
# on openBSD, the implicit rule make $< work.
# on Solaris, it does not work ($? is changed sources, $^ lists dependencies).
@ -1646,8 +1853,14 @@ AHX_CONFIG_EXT_FLAGS
dnl includes
[
#ifndef _OPENBSD_SOURCE
#define _OPENBSD_SOURCE 1
#endif
#ifndef UNBOUND_DEBUG
# ifndef NDEBUG
# define NDEBUG
# endif
#endif
/** Use small-ldns codebase */
@ -1781,6 +1994,15 @@ struct tm;
char *strptime(const char *s, const char *format, struct tm *tm);
#endif
#if !HAVE_DECL_REALLOCARRAY
void *reallocarray(void *ptr, size_t nmemb, size_t size);
#endif
#ifdef HAVE_LIBBSD
#include <bsd/string.h>
#include <bsd/stdlib.h>
#endif
#ifdef HAVE_LIBRESSL
# if !HAVE_DECL_STRLCPY
size_t strlcpy(char *dst, const char *src, size_t siz);
@ -1794,9 +2016,6 @@ uint32_t arc4random(void);
# if !HAVE_DECL_ARC4RANDOM_UNIFORM && defined(HAVE_ARC4RANDOM_UNIFORM)
uint32_t arc4random_uniform(uint32_t upper_bound);
# endif
# if !HAVE_DECL_REALLOCARRAY
void *reallocarray(void *ptr, size_t nmemb, size_t size);
# endif
#endif /* HAVE_LIBRESSL */
#ifndef HAVE_ARC4RANDOM
int getentropy(void* buf, size_t len);
@ -1885,6 +2104,6 @@ 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'`])
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])
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_HEADER([config.h])
AC_OUTPUT

View File

@ -27,10 +27,12 @@ distribution but may be helpful.
works like the BIND feature (removes AAAA records unless AAAA-only domain).
Useful for certain 'broken IPv6 default route' scenarios.
Patch from Stephane Lapie for ASAHI Net.
* unbound_smf22.tar.gz: Solaris SMF installation/removal scripts.
* unbound_smf23.tar.gz: Solaris SMF installation/removal scripts.
Contributed by Yuri Voinov.
* unbound.socket and unbound.service: systemd files for unbound, install them
in /usr/lib/systemd/system. Contributed by Sami Kerola and Pavel Odintsov.
* unbound_portable.service.in: systemd file for use unbound as portable service,
see comments in the file. Contributed by Frzk.
* redirect-bogus.patch: Return configured address for bogus A and AAAA answers,
instead of SERVFAIL. Contributed by SIDN.
* fastrpz.patch: fastrpz support from Farsight Security.
@ -38,3 +40,16 @@ distribution but may be helpful.
* unbound-querycachedb.py: utility to show data stored in cachedb backend
for a particular query name and type. It requires dnspython and (for
redis backend) redis Python modules.
* unbound-fuzzme.patch: adds unbound-fuzzme program that parses a packet from
stdin. Used with fuzzers, patch from Jacob Hoffman-Andrews.
* unbound-fuzzers.tar.bz2: three programs for fuzzing, that are 1:1
replacements for unbound-fuzzme.c that gets created after applying
the contrib/unbound-fuzzme.patch. They are contributed by
Eric Sesterhenn from X41 D-Sec.
* drop-tld.diff: adds option drop-tld: yesno that drops 2 label queries,
to stop random floods. Apply with patch -p1 < contrib/drop-tld.diff and
compile. From Saksham Manchanda (Secure64). Please note that we think
this will drop DNSKEY and DS lookups for tlds and hence break DNSSEC
lookups for downstream clients.
* drop2rpz: perl script that converts the Spamhaus DROP-List in RPZ-Format,
contributed by Andreas Schulze.

View File

@ -9,12 +9,13 @@
# Variables
dst_dir="/etc/opt/csw/unbound"
work_dir="/tmp"
list_addr="http://pgl.yoyo.org/adservers/serverlist.php?hostformat=nohtml&showintro=1&startdate%5Bday%5D=&startdate%5Bmonth%5D=&startdate%5Byear%5D="
list_addr="https://pgl.yoyo.org/adservers/serverlist.php?hostformat=nohtml&showintro=1&startdate%5Bday%5D=&startdate%5Bmonth%5D=&startdate%5Byear%5D="
# OS commands
CAT=`which cat`
ECHO=`which echo`
WGET=`which wget`
TR=`which tr`
# Check Wget installed
if [ ! -f $WGET ]; then
@ -22,8 +23,10 @@ if [ ! -f $WGET ]; then
exit 1
fi
# remove special characters with tr to protect unbound.conf
$WGET -O $work_dir/yoyo_ad_servers "$list_addr" && \
$CAT $work_dir/yoyo_ad_servers | \
$TR -d '";$\\' | \
while read line ; \
do \
$ECHO "local-zone: \"$line\" redirect" ;\
@ -36,4 +39,4 @@ echo "Done."
# the unbound_ad_servers file:
#
# include: $dst_dir/unbound_ad_servers
#
#

View File

@ -0,0 +1,82 @@
diff --git a/daemon/worker.c b/daemon/worker.c
index 263fcdd..f787b70 100644
--- a/daemon/worker.c
+++ b/daemon/worker.c
@@ -1213,6 +1213,15 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip));
log_query_in(ip, qinfo.qname, qinfo.qtype, qinfo.qclass);
}
+
+ if(worker->env.cfg->drop_tld) {
+ int lab = dname_count_labels(qinfo.qname);
+ if (lab == 2) {
+ comm_point_drop_reply(repinfo);
+ verbose(VERB_ALGO, "Dropping one label query.");
+ return 0;
+ }
+ }
if(qinfo.qtype == LDNS_RR_TYPE_AXFR ||
qinfo.qtype == LDNS_RR_TYPE_IXFR) {
verbose(VERB_ALGO, "worker request: refused zone transfer.");
diff --git a/util/config_file.h b/util/config_file.h
index b3ef930..2791541 100644
--- a/util/config_file.h
+++ b/util/config_file.h
@@ -274,6 +274,8 @@ struct config_file {
int prefetch_key;
/** deny queries of type ANY with an empty answer */
int deny_any;
+ /** Drop TLD queries from clients **/
+ int drop_tld;
/** chrootdir, if not "" or chroot will be done */
char* chrootdir;
diff --git a/util/configlexer.lex b/util/configlexer.lex
index a86ddf5..9bbedbb 100644
--- a/util/configlexer.lex
+++ b/util/configlexer.lex
@@ -299,6 +299,7 @@ private-domain{COLON} { YDVAR(1, VAR_PRIVATE_DOMAIN) }
prefetch-key{COLON} { YDVAR(1, VAR_PREFETCH_KEY) }
prefetch{COLON} { YDVAR(1, VAR_PREFETCH) }
deny-any{COLON} { YDVAR(1, VAR_DENY_ANY) }
+drop-tld{COLON} { YDVAR(1, VAR_DROP_TLD) }
stub-zone{COLON} { YDVAR(0, VAR_STUB_ZONE) }
name{COLON} { YDVAR(1, VAR_NAME) }
stub-addr{COLON} { YDVAR(1, VAR_STUB_ADDR) }
diff --git a/util/configparser.y b/util/configparser.y
index 10227a2..567d68e 100644
--- a/util/configparser.y
+++ b/util/configparser.y
@@ -164,6 +164,7 @@ extern struct config_parser_state* cfg_parser;
%token VAR_FAST_SERVER_PERMIL VAR_FAST_SERVER_NUM
%token VAR_ALLOW_NOTIFY VAR_TLS_WIN_CERT VAR_TCP_CONNECTION_LIMIT
%token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL VAR_DENY_ANY
+%token VAR_DROP_TLD
%token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY
%token VAR_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES
%token VAR_TLS_SESSION_TICKET_KEYS
@@ -266,6 +267,7 @@ content_server: server_num_threads | server_verbosity | server_port |
server_tls_cert_bundle | server_tls_additional_port | server_low_rtt |
server_fast_server_permil | server_fast_server_num | server_tls_win_cert |
server_tcp_connection_limit | server_log_servfail | server_deny_any |
+ server_drop_tld |
server_unknown_server_time_limit | server_log_tag_queryreply |
server_stream_wait_size | server_tls_ciphers |
server_tls_ciphersuites | server_tls_session_ticket_keys
@@ -1466,6 +1468,16 @@ server_deny_any: VAR_DENY_ANY STRING_ARG
free($2);
}
;
+
+server_drop_tld: VAR_DROP_TLD STRING_ARG
+ {
+ OUTYY(("P(server_drop_tld:%s)\n", $2));
+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+ yyerror("expected yes or no.");
+ else cfg_parser->cfg->drop_tld = (strcmp($2, "yes")==0);
+ free($2);
+ }
+ ;
server_unwanted_reply_threshold: VAR_UNWANTED_REPLY_THRESHOLD STRING_ARG
{
OUTYY(("P(server_unwanted_reply_threshold:%s)\n", $2));

View File

@ -0,0 +1,39 @@
#!/usr/bin/perl
# usage: curl --silent https://www.spamhaus.org/drop/drop.txt | $0 > /path/to/spamhaus-drop.rpz.local
#
# unbound.conf:
# rpz:
# name: "spamhaus-drop.rpz.local."
# zonefile: "/path/tp/spamhaus-drop.rpz.local"
# rpz-log: yes
# rpz-log-name: "spamhaus-drop"
#
use strict;
use vars qw{$o1 $o2 $o3 $o4 $m};
# trailing dots required
my $origin = 'drop.spamhaus.org.rpz.local.';
my $mname = 'localhost.';
my $rname = 'root.localhost.';
my $ns = $mname;
my $rpz_action = '.'; # return NXDOMAIN
#my $rpz_action = '*.'; # return NODATA
#my $rpz_action = 'rpz-drop.'; # drop the query
print "$origin SOA $mname $rname 1 43200 7200 2419200 3600\n";
print "$origin NS $ns\n";
while(<>) {
if(($o1, $o2, $o3, $o4, $m) = m{(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)}) {
print "$m.$o4.$o3.$o2.$o1.rpz-ip.$origin CNAME $rpz_action\n";
} else {
print "$_";
}
}
# add a testpoint: ask for "dns.google"
# print "32.8.8.8.8.rpz-ip.$origin CNAME $rpz_action\n";
exit;

View File

@ -1,11 +1,11 @@
Description: based on the included patch contrib/fastrpz.patch
Author: fastrpz@farsightsecurity.com
---
Index: unboundfastrpz/Makefile.in
===================================================================
--- unboundfastrpz/Makefile.in (revision 4923)
+++ unboundfastrpz/Makefile.in (working copy)
@@ -23,6 +23,8 @@
diff --git a/Makefile.in b/Makefile.in
index a20058cc..495779cc 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -23,6 +23,8 @@ CHECKLOCK_SRC=testcode/checklocks.c
CHECKLOCK_OBJ=@CHECKLOCK_OBJ@
DNSTAP_SRC=@DNSTAP_SRC@
DNSTAP_OBJ=@DNSTAP_OBJ@
@ -14,25 +14,25 @@ Index: unboundfastrpz/Makefile.in
DNSCRYPT_SRC=@DNSCRYPT_SRC@
DNSCRYPT_OBJ=@DNSCRYPT_OBJ@
WITH_PYTHONMODULE=@WITH_PYTHONMODULE@
@@ -126,7 +128,7 @@
@@ -127,7 +129,7 @@ validator/val_sigcrypt.c validator/val_utils.c dns64/dns64.c \
edns-subnet/edns-subnet.c edns-subnet/subnetmod.c \
edns-subnet/addrtree.c edns-subnet/subnet-whitelist.c \
cachedb/cachedb.c cachedb/redis.c respip/respip.c $(CHECKLOCK_SRC) \
-$(DNSTAP_SRC) $(DNSCRYPT_SRC) $(IPSECMOD_SRC)
+$(DNSTAP_SRC) $(FASTRPZ_SRC) $(DNSCRYPT_SRC) $(IPSECMOD_SRC)
-$(DNSTAP_SRC) $(DNSCRYPT_SRC) $(IPSECMOD_SRC) $(IPSET_SRC)
+$(DNSTAP_SRC) $(FASTRPZ_SRC) $(DNSCRYPT_SRC) $(IPSECMOD_SRC) $(IPSET_SRC)
COMMON_OBJ_WITHOUT_NETCALL=dns.lo infra.lo rrset.lo dname.lo msgencode.lo \
as112.lo msgparse.lo msgreply.lo packed_rrset.lo iterator.lo iter_delegpt.lo \
iter_donotq.lo iter_fwd.lo iter_hints.lo iter_priv.lo iter_resptype.lo \
@@ -139,7 +141,7 @@
@@ -140,7 +142,7 @@ autotrust.lo val_anchor.lo rpz.lo \
validator.lo val_kcache.lo val_kentry.lo val_neg.lo val_nsec3.lo val_nsec.lo \
val_secalgo.lo val_sigcrypt.lo val_utils.lo dns64.lo cachedb.lo redis.lo authzone.lo \
$(SUBNET_OBJ) $(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ) $(DNSTAP_OBJ) $(DNSCRYPT_OBJ) \
-$(IPSECMOD_OBJ) respip.lo
+$(FASTRPZ_OBJ) $(IPSECMOD_OBJ) respip.lo
-$(IPSECMOD_OBJ) $(IPSET_OBJ) respip.lo
+$(FASTRPZ_OBJ) $(IPSECMOD_OBJ) $(IPSET_OBJ) respip.lo
COMMON_OBJ_WITHOUT_UB_EVENT=$(COMMON_OBJ_WITHOUT_NETCALL) netevent.lo listen_dnsport.lo \
outside_network.lo
COMMON_OBJ=$(COMMON_OBJ_WITHOUT_UB_EVENT) ub_event.lo
@@ -405,6 +407,11 @@
@@ -410,6 +412,11 @@ dnscrypt.lo dnscrypt.o: $(srcdir)/dnscrypt/dnscrypt.c config.h \
$(srcdir)/util/config_file.h $(srcdir)/util/log.h \
$(srcdir)/util/netevent.h
@ -44,11 +44,11 @@ Index: unboundfastrpz/Makefile.in
# Python Module
pythonmod.lo pythonmod.o: $(srcdir)/pythonmod/pythonmod.c config.h \
pythonmod/interface.h \
Index: unboundfastrpz/config.h.in
===================================================================
--- unboundfastrpz/config.h.in (revision 4923)
+++ unboundfastrpz/config.h.in (working copy)
@@ -1272,4 +1272,11 @@
diff --git a/config.h.in b/config.h.in
index 78d47fed..e33073e4 100644
--- a/config.h.in
+++ b/config.h.in
@@ -1345,4 +1345,11 @@ void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
/** the version of unbound-control that this software implements */
#define UNBOUND_CONTROL_VERSION 1
@ -61,11 +61,11 @@ Index: unboundfastrpz/config.h.in
+#undef FASTRPZ_LIB_OPEN
+/** turn on fastrpz response policy zones */
+#undef ENABLE_FASTRPZ
Index: unboundfastrpz/configure.ac
===================================================================
--- unboundfastrpz/configure.ac (revision 4923)
+++ unboundfastrpz/configure.ac (working copy)
@@ -6,6 +6,7 @@
diff --git a/configure.ac b/configure.ac
index 2b91dd3c..e6063d17 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6,6 +6,7 @@ sinclude(ax_pthread.m4)
sinclude(acx_python.m4)
sinclude(ac_pkg_swig.m4)
sinclude(dnstap/dnstap.m4)
@ -73,7 +73,7 @@ Index: unboundfastrpz/configure.ac
sinclude(dnscrypt/dnscrypt.m4)
# must be numbers. ac_defun because of later processing
@@ -1565,6 +1566,9 @@
@@ -1778,6 +1779,9 @@ case "$enable_ipset" in
;;
esac
@ -83,10 +83,10 @@ Index: unboundfastrpz/configure.ac
AC_MSG_CHECKING([if ${MAKE:-make} supports $< with implicit rule in scope])
# on openBSD, the implicit rule make $< work.
# on Solaris, it does not work ($? is changed sources, $^ lists dependencies).
Index: unboundfastrpz/daemon/daemon.c
===================================================================
--- unboundfastrpz/daemon/daemon.c (revision 4923)
+++ unboundfastrpz/daemon/daemon.c (working copy)
diff --git a/daemon/daemon.c b/daemon/daemon.c
index 8b0fc348..7ffb9221 100644
--- a/daemon/daemon.c
+++ b/daemon/daemon.c
@@ -91,6 +91,9 @@
#include "sldns/keyraw.h"
#include "respip/respip.h"
@ -97,36 +97,36 @@ Index: unboundfastrpz/daemon/daemon.c
#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
@@ -462,6 +465,14 @@
@@ -458,6 +461,14 @@ daemon_create_workers(struct daemon* daemon)
dt_apply_cfg(daemon->dtenv, daemon->cfg);
#else
fatal_exit("dnstap enabled in config but not built with dnstap support");
#endif
}
+#endif
+ }
+ if(daemon->cfg->rpz_enable) {
+#ifdef ENABLE_FASTRPZ
+ rpz_init(&daemon->rpz_clist, &daemon->rpz_client, daemon->cfg);
+#else
+ fatal_exit("fastrpz enabled in config"
+ " but not built with fastrpz");
+#endif
+ }
#endif
}
for(i=0; i<daemon->num; i++) {
if(!(daemon->workers[i] = worker_create(daemon, i,
shufport+numport*i/daemon->num,
@@ -719,6 +730,9 @@
@@ -731,6 +742,9 @@ daemon_cleanup(struct daemon* daemon)
#ifdef USE_DNSCRYPT
dnsc_delete(daemon->dnscenv);
daemon->dnscenv = NULL;
#endif
+#endif
+#ifdef ENABLE_FASTRPZ
+ rpz_delete(&daemon->rpz_clist, &daemon->rpz_client);
+#endif
#endif
daemon->cfg = NULL;
}
Index: unboundfastrpz/daemon/daemon.h
===================================================================
--- unboundfastrpz/daemon/daemon.h (revision 4923)
+++ unboundfastrpz/daemon/daemon.h (working copy)
@@ -136,6 +136,11 @@
diff --git a/daemon/daemon.h b/daemon/daemon.h
index 3effbafb..4d4c34da 100644
--- a/daemon/daemon.h
+++ b/daemon/daemon.h
@@ -138,6 +138,11 @@ struct daemon {
/** the dnscrypt environment */
struct dnsc_env* dnscenv;
#endif
@ -138,11 +138,11 @@ Index: unboundfastrpz/daemon/daemon.h
};
/**
Index: unboundfastrpz/daemon/worker.c
===================================================================
--- unboundfastrpz/daemon/worker.c (revision 4923)
+++ unboundfastrpz/daemon/worker.c (working copy)
@@ -75,6 +75,9 @@
diff --git a/daemon/worker.c b/daemon/worker.c
index eb7fdf2f..1982228d 100644
--- a/daemon/worker.c
+++ b/daemon/worker.c
@@ -76,6 +76,9 @@
#include "libunbound/context.h"
#include "libunbound/libworker.h"
#include "sldns/sbuffer.h"
@ -152,7 +152,7 @@ Index: unboundfastrpz/daemon/worker.c
#include "sldns/wire2str.h"
#include "util/shm_side/shm_main.h"
#include "dnscrypt/dnscrypt.h"
@@ -533,8 +536,27 @@
@@ -534,8 +537,27 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
/* not secure */
secure = 0;
break;
@ -180,10 +180,10 @@ Index: unboundfastrpz/daemon/worker.c
/* return this delegation from the cache */
edns_bak = *edns;
edns->edns_version = EDNS_ADVERTISED_VERSION;
@@ -702,6 +724,23 @@
secure = 0;
@@ -710,6 +732,23 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
*is_secure_answer = 0;
}
} else secure = 0;
} else *is_secure_answer = 0;
+#ifdef ENABLE_FASTRPZ
+ if(repinfo->rpz) {
+ /* Scan the cached answer for RPZ hits.
@ -204,7 +204,7 @@ Index: unboundfastrpz/daemon/worker.c
edns_bak = *edns;
edns->edns_version = EDNS_ADVERTISED_VERSION;
@@ -1407,6 +1446,15 @@
@@ -1435,6 +1474,15 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from",
&repinfo->addr, repinfo->addrlen);
goto send_reply;
@ -220,14 +220,14 @@ Index: unboundfastrpz/daemon/worker.c
}
/* If we've found a local alias, replace the qname with the alias
@@ -1455,12 +1503,21 @@
@@ -1485,12 +1533,21 @@ lookup_cache:
h = query_info_hash(lookup_qinfo, sldns_buffer_read_u16_at(c->buffer, 2));
if((e=slabhash_lookup(worker->env.msg_cache, h, lookup_qinfo, 0))) {
/* answer from cache - we have acquired a readlock on it */
- if(answer_from_cache(worker, &qinfo,
+ ret = answer_from_cache(worker, &qinfo,
cinfo, &need_drop, &alias_rrset, &partial_rep,
(struct reply_info*)e->data,
cinfo, &need_drop, &is_expired_answer, &is_secure_answer,
&alias_rrset, &partial_rep, (struct reply_info*)e->data,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
- &edns)) {
@ -244,7 +244,7 @@ Index: unboundfastrpz/daemon/worker.c
/* prefetch it if the prefetch TTL expired.
* Note that if there is more than one pass
* its qname must be that used for cache
@@ -1514,11 +1571,19 @@
@@ -1547,11 +1604,19 @@ lookup_cache:
lock_rw_unlock(&e->lock);
}
if(!LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) {
@ -266,11 +266,11 @@ Index: unboundfastrpz/daemon/worker.c
goto send_reply;
}
verbose(VERB_ALGO, "answer norec from cache -- "
Index: unboundfastrpz/doc/unbound.conf.5.in
===================================================================
--- unboundfastrpz/doc/unbound.conf.5.in (revision 4923)
+++ unboundfastrpz/doc/unbound.conf.5.in (working copy)
@@ -1728,6 +1728,81 @@
diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in
index 38c2d298..3b07f392 100644
--- a/doc/unbound.conf.5.in
+++ b/doc/unbound.conf.5.in
@@ -1828,6 +1828,81 @@ List domain for which the AAAA records are ignored and the A record is
used by dns64 processing instead. Can be entered multiple times, list a
new domain for which it applies, one per line. Applies also to names
underneath the name given.
@ -352,10 +352,11 @@ Index: unboundfastrpz/doc/unbound.conf.5.in
.SS "DNSCrypt Options"
.LP
The
Index: unboundfastrpz/fastrpz/librpz.h
===================================================================
--- unboundfastrpz/fastrpz/librpz.h (nonexistent)
+++ unboundfastrpz/fastrpz/librpz.h (working copy)
diff --git a/fastrpz/librpz.h b/fastrpz/librpz.h
new file mode 100644
index 00000000..645279d1
--- /dev/null
+++ b/fastrpz/librpz.h
@@ -0,0 +1,957 @@
+/*
+ * Define the interface from a DNS resolver to the Response Policy Zone
@ -1314,10 +1315,11 @@ Index: unboundfastrpz/fastrpz/librpz.h
+#endif /* LIBRPZ_LIB_OPEN */
+
+#endif /* LIBRPZ_H */
Index: unboundfastrpz/fastrpz/rpz.c
===================================================================
--- unboundfastrpz/fastrpz/rpz.c (nonexistent)
+++ unboundfastrpz/fastrpz/rpz.c (working copy)
diff --git a/fastrpz/rpz.c b/fastrpz/rpz.c
new file mode 100644
index 00000000..c5ab7801
--- /dev/null
+++ b/fastrpz/rpz.c
@@ -0,0 +1,1352 @@
+/*
+ * fastrpz/rpz.c - interface to the fastrpz response policy zone library
@ -2671,10 +2673,11 @@ Index: unboundfastrpz/fastrpz/rpz.c
+}
+
+#endif /* ENABLE_FASTRPZ */
Index: unboundfastrpz/fastrpz/rpz.h
===================================================================
--- unboundfastrpz/fastrpz/rpz.h (nonexistent)
+++ unboundfastrpz/fastrpz/rpz.h (working copy)
diff --git a/fastrpz/rpz.h b/fastrpz/rpz.h
new file mode 100644
index 00000000..5d7e31c5
--- /dev/null
+++ b/fastrpz/rpz.h
@@ -0,0 +1,138 @@
+/*
+ * fastrpz/rpz.h - interface to the fastrpz response policy zone library
@ -2814,10 +2817,11 @@ Index: unboundfastrpz/fastrpz/rpz.h
+
+#endif /* ENABLE_FASTRPZ */
+#endif /* UNBOUND_FASTRPZ_RPZ_H */
Index: unboundfastrpz/fastrpz/rpz.m4
===================================================================
--- unboundfastrpz/fastrpz/rpz.m4 (nonexistent)
+++ unboundfastrpz/fastrpz/rpz.m4 (working copy)
diff --git a/fastrpz/rpz.m4 b/fastrpz/rpz.m4
new file mode 100644
index 00000000..21235355
--- /dev/null
+++ b/fastrpz/rpz.m4
@@ -0,0 +1,64 @@
+# fastrpz/rpz.m4
+
@ -2883,10 +2887,10 @@ Index: unboundfastrpz/fastrpz/rpz.m4
+ AC_MSG_WARN([[dlopen and librpz.so needed for fastrpz]])
+ fi
+])
Index: unboundfastrpz/iterator/iterator.c
===================================================================
--- unboundfastrpz/iterator/iterator.c (revision 4923)
+++ unboundfastrpz/iterator/iterator.c (working copy)
diff --git a/iterator/iterator.c b/iterator/iterator.c
index 1e0113a8..2fcbf547 100644
--- a/iterator/iterator.c
+++ b/iterator/iterator.c
@@ -68,6 +68,9 @@
#include "sldns/str2wire.h"
#include "sldns/parseutil.h"
@ -2895,9 +2899,9 @@ Index: unboundfastrpz/iterator/iterator.c
+#include "fastrpz/rpz.h"
+#endif
int
iter_init(struct module_env* env, int id)
@@ -525,6 +528,23 @@
/* in msec */
int UNKNOWN_SERVER_NICENESS = 376;
@@ -555,6 +558,23 @@ handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq,
if(ntohs(r->rk.type) == LDNS_RR_TYPE_CNAME &&
query_dname_compare(*mname, r->rk.dname) == 0 &&
!iter_find_rrset_in_prepend_answer(iq, r)) {
@ -2921,7 +2925,7 @@ Index: unboundfastrpz/iterator/iterator.c
/* Add this relevant CNAME rrset to the prepend list.*/
if(!iter_add_prepend_answer(qstate, iq, r))
return 0;
@@ -533,6 +553,9 @@
@@ -563,6 +583,9 @@ handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq,
/* Other rrsets in the section are ignored. */
}
@ -2931,7 +2935,7 @@ Index: unboundfastrpz/iterator/iterator.c
/* add authority rrsets to authority prepend, for wildcarded CNAMEs */
for(i=msg->rep->an_numrrsets; i<msg->rep->an_numrrsets +
msg->rep->ns_numrrsets; i++) {
@@ -1216,6 +1239,7 @@
@@ -1199,6 +1222,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
uint8_t* delname;
size_t delnamelen;
struct dns_msg* msg = NULL;
@ -2939,7 +2943,7 @@ Index: unboundfastrpz/iterator/iterator.c
log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo);
/* check effort */
@@ -1302,8 +1326,7 @@
@@ -1285,8 +1309,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
}
if(msg) {
/* handle positive cache response */
@ -2949,7 +2953,7 @@ Index: unboundfastrpz/iterator/iterator.c
if(verbosity >= VERB_ALGO) {
log_dns_msg("msg from cache lookup", &msg->qinfo,
msg->rep);
@@ -1311,7 +1334,22 @@
@@ -1294,7 +1317,22 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
(int)msg->rep->ttl,
(int)msg->rep->prefetch_ttl);
}
@ -2972,7 +2976,7 @@ Index: unboundfastrpz/iterator/iterator.c
if(type == RESPONSE_TYPE_CNAME) {
uint8_t* sname = 0;
size_t slen = 0;
@@ -2716,6 +2754,62 @@
@@ -2718,6 +2756,62 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
sock_list_insert(&qstate->reply_origin,
&qstate->reply->addr, qstate->reply->addrlen,
qstate->region);
@ -3035,7 +3039,7 @@ Index: unboundfastrpz/iterator/iterator.c
if(iq->minimisation_state != DONOT_MINIMISE_STATE
&& !(iq->chase_flags & BIT_RD)) {
if(FLAGS_GET_RCODE(iq->response->rep->flags) !=
@@ -3462,6 +3556,10 @@
@@ -3471,12 +3565,44 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
* 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) {
@ -3046,7 +3050,6 @@ Index: unboundfastrpz/iterator/iterator.c
iter_dns_store(qstate->env, &qstate->qinfo,
iq->response->rep, 0, qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
@@ -3468,6 +3566,34 @@
qstate->region, qstate->query_flags);
}
}
@ -3081,11 +3084,11 @@ Index: unboundfastrpz/iterator/iterator.c
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = iq->response;
return 0;
Index: unboundfastrpz/iterator/iterator.h
===================================================================
--- unboundfastrpz/iterator/iterator.h (revision 4923)
+++ unboundfastrpz/iterator/iterator.h (working copy)
@@ -386,6 +386,16 @@
diff --git a/iterator/iterator.h b/iterator/iterator.h
index a2f1b570..e1e4a738 100644
--- a/iterator/iterator.h
+++ b/iterator/iterator.h
@@ -386,6 +386,16 @@ struct iter_qstate {
*/
int minimise_count;
@ -3102,11 +3105,11 @@ Index: unboundfastrpz/iterator/iterator.h
/**
* Count number of time-outs. Used to prevent resolving failures when
* the QNAME minimisation QTYPE is blocked. */
Index: unboundfastrpz/services/cache/dns.c
===================================================================
--- unboundfastrpz/services/cache/dns.c (revision 4923)
+++ unboundfastrpz/services/cache/dns.c (working copy)
@@ -928,6 +928,14 @@
diff --git a/services/cache/dns.c b/services/cache/dns.c
index 2a5bca4a..6de8863a 100644
--- a/services/cache/dns.c
+++ b/services/cache/dns.c
@@ -967,6 +967,14 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
struct regional* region, uint32_t flags)
{
struct reply_info* rep = NULL;
@ -3121,11 +3124,11 @@ Index: unboundfastrpz/services/cache/dns.c
/* alloc, malloc properly (not in region, like msg is) */
rep = reply_info_copy(msgrep, env->alloc, NULL);
if(!rep)
Index: unboundfastrpz/services/mesh.c
===================================================================
--- unboundfastrpz/services/mesh.c (revision 4923)
+++ unboundfastrpz/services/mesh.c (working copy)
@@ -60,6 +60,9 @@
diff --git a/services/mesh.c b/services/mesh.c
index 9114ef4c..3dc518e5 100644
--- a/services/mesh.c
+++ b/services/mesh.c
@@ -61,6 +61,9 @@
#include "sldns/wire2str.h"
#include "services/localzone.h"
#include "util/data/dname.h"
@ -3133,9 +3136,9 @@ Index: unboundfastrpz/services/mesh.c
+#include "fastrpz/rpz.h"
+#endif
#include "respip/respip.h"
#include "services/listen_dnsport.h"
/** subtract timers and the values do not overflow or become negative */
@@ -1057,6 +1060,13 @@
@@ -1195,6 +1198,13 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
else secure = 0;
if(!rep && rcode == LDNS_RCODE_NOERROR)
rcode = LDNS_RCODE_SERVFAIL;
@ -3149,7 +3152,7 @@ Index: unboundfastrpz/services/mesh.c
/* send the reply */
/* We don't reuse the encoded answer if either the previous or current
* response has a local alias. We could compare the alias records
@@ -1230,6 +1240,7 @@
@@ -1415,6 +1425,7 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh,
key.s.is_valrec = valrec;
key.s.qinfo = *qinfo;
key.s.query_flags = qflags;
@ -3157,7 +3160,7 @@ Index: unboundfastrpz/services/mesh.c
/* We are searching for a similar mesh state when we DO want to
* aggregate the state. Thus unique is set to NULL. (default when we
* desire aggregation).*/
@@ -1276,6 +1287,10 @@
@@ -1461,6 +1472,10 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
if(!r)
return 0;
r->query_reply = *rep;
@ -3168,11 +3171,11 @@ Index: unboundfastrpz/services/mesh.c
r->edns = *edns;
if(edns->opt_list) {
r->edns.opt_list = edns_opt_copy_region(edns->opt_list,
Index: unboundfastrpz/util/config_file.c
===================================================================
--- unboundfastrpz/util/config_file.c (revision 4923)
+++ unboundfastrpz/util/config_file.c (working copy)
@@ -1386,6 +1386,8 @@
diff --git a/util/config_file.c b/util/config_file.c
index 52ca5a18..0660248f 100644
--- a/util/config_file.c
+++ b/util/config_file.c
@@ -1460,6 +1460,8 @@ config_delete(struct config_file* cfg)
free(cfg->dnstap_socket_path);
free(cfg->dnstap_identity);
free(cfg->dnstap_version);
@ -3180,12 +3183,12 @@ Index: unboundfastrpz/util/config_file.c
+ free(cfg->rpz_cstr);
config_deldblstrlist(cfg->ratelimit_for_domain);
config_deldblstrlist(cfg->ratelimit_below_domain);
#ifdef USE_IPSECMOD
Index: unboundfastrpz/util/config_file.h
===================================================================
--- unboundfastrpz/util/config_file.h (revision 4923)
+++ unboundfastrpz/util/config_file.h (working copy)
@@ -468,6 +468,11 @@
config_delstrlist(cfg->python_script);
diff --git a/util/config_file.h b/util/config_file.h
index 8739ca2a..a2dcf215 100644
--- a/util/config_file.h
+++ b/util/config_file.h
@@ -499,6 +499,11 @@ struct config_file {
/** true to disable DNSSEC lameness check in iterator */
int disable_dnssec_lame_check;
@ -3197,11 +3200,11 @@ Index: unboundfastrpz/util/config_file.h
/** ratelimit for ip addresses. 0 is off, otherwise qps (unless overridden) */
int ip_ratelimit;
/** number of slabs for ip_ratelimit cache */
Index: unboundfastrpz/util/configlexer.lex
===================================================================
--- unboundfastrpz/util/configlexer.lex (revision 4923)
+++ unboundfastrpz/util/configlexer.lex (working copy)
@@ -429,6 +429,10 @@
diff --git a/util/configlexer.lex b/util/configlexer.lex
index deedffa5..301458a3 100644
--- a/util/configlexer.lex
+++ b/util/configlexer.lex
@@ -446,6 +446,10 @@ dnstap-log-forwarder-query-messages{COLON} {
YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES) }
dnstap-log-forwarder-response-messages{COLON} {
YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES) }
@ -3212,11 +3215,11 @@ Index: unboundfastrpz/util/configlexer.lex
disable-dnssec-lame-check{COLON} { YDVAR(1, VAR_DISABLE_DNSSEC_LAME_CHECK) }
ip-ratelimit{COLON} { YDVAR(1, VAR_IP_RATELIMIT) }
ratelimit{COLON} { YDVAR(1, VAR_RATELIMIT) }
Index: unboundfastrpz/util/configparser.y
===================================================================
--- unboundfastrpz/util/configparser.y (revision 4923)
+++ unboundfastrpz/util/configparser.y (working copy)
@@ -125,6 +125,7 @@
diff --git a/util/configparser.y b/util/configparser.y
index d471babe..cb6b1d63 100644
--- a/util/configparser.y
+++ b/util/configparser.y
@@ -125,6 +125,7 @@ extern struct config_parser_state* cfg_parser;
%token VAR_DNSTAP_LOG_CLIENT_RESPONSE_MESSAGES
%token VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES
%token VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES
@ -3224,7 +3227,7 @@ Index: unboundfastrpz/util/configparser.y
%token VAR_RESPONSE_IP_TAG VAR_RESPONSE_IP VAR_RESPONSE_IP_DATA
%token VAR_HARDEN_ALGO_DOWNGRADE VAR_IP_TRANSPARENT
%token VAR_DISABLE_DNSSEC_LAME_CHECK
@@ -164,7 +165,7 @@
@@ -173,7 +174,7 @@ extern struct config_parser_state* cfg_parser;
%%
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@ -3233,8 +3236,8 @@ Index: unboundfastrpz/util/configparser.y
forwardstart contents_forward | pythonstart contents_py |
rcstart contents_rc | dtstart contents_dt | viewstart contents_view |
dnscstart contents_dnsc | cachedbstart contents_cachedb |
@@ -2546,6 +2547,50 @@
(strcmp($2, "yes")==0);
@@ -2837,6 +2838,50 @@ dt_dnstap_log_forwarder_response_messages: VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MES
free($2);
}
;
+rpzstart: VAR_RPZ
@ -3261,8 +3264,8 @@ Index: unboundfastrpz/util/configparser.y
+
+ OUTYY(("P(rpz_zone:%s)\n", $2));
+ old_cstr = cfg_parser->cfg->rpz_cstr;
+ (void)asprintf(&new_cstr, "%s\nzone %s", old_cstr?old_cstr:"", $2);
+ if(!new_cstr)
+ if(asprintf(&new_cstr, "%s\nzone %s", old_cstr?old_cstr:"", $2) == -1) {new_cstr = NULL; yyerror("out of memory");}
+ else if(!new_cstr)
+ yyerror("out of memory");
+ free(old_cstr);
+ cfg_parser->cfg->rpz_cstr = new_cstr;
@ -3274,8 +3277,8 @@ Index: unboundfastrpz/util/configparser.y
+
+ OUTYY(("P(rpz_option:%s)\n", $2));
+ old_cstr = cfg_parser->cfg->rpz_cstr;
+ (void)asprintf(&new_cstr, "%s\n%s", old_cstr ? old_cstr : "", $2);
+ if(!new_cstr)
+ if(asprintf(&new_cstr, "%s\n%s", old_cstr ? old_cstr : "", $2) == -1) {new_cstr = NULL; yyerror("out of memory");}
+ else if(!new_cstr)
+ yyerror("out of memory");
+ free(old_cstr);
+ cfg_parser->cfg->rpz_cstr = new_cstr;
@ -3284,11 +3287,11 @@ Index: unboundfastrpz/util/configparser.y
pythonstart: VAR_PYTHON
{
OUTYY(("\nP(python:)\n"));
Index: unboundfastrpz/util/data/msgencode.c
===================================================================
--- unboundfastrpz/util/data/msgencode.c (revision 4923)
+++ unboundfastrpz/util/data/msgencode.c (working copy)
@@ -585,6 +585,35 @@
diff --git a/util/data/msgencode.c b/util/data/msgencode.c
index be69f628..f10773aa 100644
--- a/util/data/msgencode.c
+++ b/util/data/msgencode.c
@@ -592,6 +592,35 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
return RETVAL_OK;
}
@ -3324,10 +3327,10 @@ Index: unboundfastrpz/util/data/msgencode.c
/** store query section in wireformat buffer, return RETVAL */
static int
insert_query(struct query_info* qinfo, struct compress_tree_node** tree,
@@ -748,6 +777,19 @@
return 0;
@@ -779,6 +808,19 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
}
sldns_buffer_write_u16_at(buffer, 10, arcount);
}
sldns_buffer_write_u16_at(buffer, 10, arcount);
+#ifdef ENABLE_FASTRPZ
+ } else if(rep->security == sec_status_rpz_rewritten) {
+ /* Insert the RPZ SOA for rpz even with MINIMAL_RESPONSES */
@ -3344,11 +3347,11 @@ Index: unboundfastrpz/util/data/msgencode.c
}
sldns_buffer_flip(buffer);
return 1;
Index: unboundfastrpz/util/data/packed_rrset.c
===================================================================
--- unboundfastrpz/util/data/packed_rrset.c (revision 4923)
+++ unboundfastrpz/util/data/packed_rrset.c (working copy)
@@ -255,6 +255,10 @@
diff --git a/util/data/packed_rrset.c b/util/data/packed_rrset.c
index 4b0294f9..3b3838f6 100644
--- a/util/data/packed_rrset.c
+++ b/util/data/packed_rrset.c
@@ -256,6 +256,10 @@ sec_status_to_string(enum sec_status s)
case sec_status_insecure: return "sec_status_insecure";
case sec_status_secure_sentinel_fail: return "sec_status_secure_sentinel_fail";
case sec_status_secure: return "sec_status_secure";
@ -3359,11 +3362,11 @@ Index: unboundfastrpz/util/data/packed_rrset.c
}
return "unknown_sec_status_value";
}
Index: unboundfastrpz/util/data/packed_rrset.h
===================================================================
--- unboundfastrpz/util/data/packed_rrset.h (revision 4923)
+++ unboundfastrpz/util/data/packed_rrset.h (working copy)
@@ -193,7 +193,15 @@
diff --git a/util/data/packed_rrset.h b/util/data/packed_rrset.h
index 729877ba..ccd1a0c2 100644
--- a/util/data/packed_rrset.h
+++ b/util/data/packed_rrset.h
@@ -193,7 +193,15 @@ enum sec_status {
sec_status_secure_sentinel_fail,
/** SECURE means that the object (RRset or message) validated
* according to local policy. */
@ -3380,11 +3383,11 @@ Index: unboundfastrpz/util/data/packed_rrset.h
};
/**
Index: unboundfastrpz/util/netevent.c
===================================================================
--- unboundfastrpz/util/netevent.c (revision 4923)
+++ unboundfastrpz/util/netevent.c (working copy)
@@ -56,6 +56,9 @@
diff --git a/util/netevent.c b/util/netevent.c
index 9fe5da2d..037e70d1 100644
--- a/util/netevent.c
+++ b/util/netevent.c
@@ -57,6 +57,9 @@
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
@ -3394,7 +3397,7 @@ Index: unboundfastrpz/util/netevent.c
/* -------- Start of local definitions -------- */
/** if CMSG_ALIGN is not defined on this platform, a workaround */
@@ -588,6 +591,9 @@
@@ -590,6 +593,9 @@ comm_point_udp_ancil_callback(int fd, short event, void* arg)
struct cmsghdr* cmsg;
#endif /* S_SPLINT_S */
@ -3404,7 +3407,7 @@ Index: unboundfastrpz/util/netevent.c
rep.c = (struct comm_point*)arg;
log_assert(rep.c->type == comm_udp);
@@ -677,6 +683,9 @@
@@ -679,6 +685,9 @@ comm_point_udp_callback(int fd, short event, void* arg)
int i;
struct sldns_buffer *buffer;
@ -3414,7 +3417,7 @@ Index: unboundfastrpz/util/netevent.c
rep.c = (struct comm_point*)arg;
log_assert(rep.c->type == comm_udp);
@@ -720,6 +729,9 @@
@@ -722,6 +731,9 @@ comm_point_udp_callback(int fd, short event, void* arg)
(void)comm_point_send_udp_msg(rep.c, buffer,
(struct sockaddr*)&rep.addr, rep.addrlen);
}
@ -3424,9 +3427,9 @@ Index: unboundfastrpz/util/netevent.c
if(!rep.c || rep.c->fd != fd) /* commpoint closed to -1 or reused for
another UDP port. Note rep.c cannot be reused with TCP fd. */
break;
@@ -3035,6 +3047,9 @@
comm_point_start_listening(repinfo->c, -1,
repinfo->c->tcp_timeout_msec);
@@ -3192,6 +3204,9 @@ comm_point_send_reply(struct comm_reply *repinfo)
repinfo->c->tcp_timeout_msec);
}
}
+#ifdef ENABLE_FASTRPZ
+ rpz_end(repinfo);
@ -3434,31 +3437,31 @@ Index: unboundfastrpz/util/netevent.c
}
void
@@ -3044,6 +3059,9 @@
@@ -3201,6 +3216,9 @@ comm_point_drop_reply(struct comm_reply* repinfo)
return;
log_assert(repinfo && repinfo->c);
log_assert(repinfo->c);
log_assert(repinfo->c->type != comm_tcp_accept);
+#ifdef ENABLE_FASTRPZ
+ rpz_end(repinfo);
+#endif
if(repinfo->c->type == comm_udp)
return;
reclaim_tcp_handler(repinfo->c);
@@ -3063,6 +3081,9 @@
if(repinfo->c->tcp_req_info)
@@ -3222,6 +3240,9 @@ comm_point_start_listening(struct comm_point* c, int newfd, int msec)
{
verbose(VERB_ALGO, "comm point start listening %d",
c->fd==-1?newfd:c->fd);
verbose(VERB_ALGO, "comm point start listening %d (%d msec)",
c->fd==-1?newfd:c->fd, msec);
+#ifdef ENABLE_FASTRPZ
+ rpz_end(&c->repinfo);
+#endif
if(c->type == comm_tcp_accept && !c->tcp_free) {
/* no use to start listening no free slots. */
return;
Index: unboundfastrpz/util/netevent.h
===================================================================
--- unboundfastrpz/util/netevent.h (revision 4923)
+++ unboundfastrpz/util/netevent.h (working copy)
@@ -120,6 +120,10 @@
diff --git a/util/netevent.h b/util/netevent.h
index d80c72b3..0233292f 100644
--- a/util/netevent.h
+++ b/util/netevent.h
@@ -120,6 +120,10 @@ struct comm_reply {
/** return type 0 (none), 4(IP4), 6(IP6) */
int srctype;
/* DnsCrypt context */
@ -3469,11 +3472,11 @@ Index: unboundfastrpz/util/netevent.h
#ifdef USE_DNSCRYPT
uint8_t client_nonce[crypto_box_HALF_NONCEBYTES];
uint8_t nmkey[crypto_box_BEFORENMBYTES];
Index: unboundfastrpz/validator/validator.c
===================================================================
--- unboundfastrpz/validator/validator.c (revision 4923)
+++ unboundfastrpz/validator/validator.c (working copy)
@@ -2755,6 +2755,12 @@
diff --git a/validator/validator.c b/validator/validator.c
index c3ca0a27..15251988 100644
--- a/validator/validator.c
+++ b/validator/validator.c
@@ -2761,6 +2761,12 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
default:
/* NSEC proof did not work, try next */
break;
@ -3486,7 +3489,7 @@ Index: unboundfastrpz/validator/validator.c
}
sec = nsec3_prove_nods(qstate->env, ve,
@@ -2788,6 +2794,12 @@
@@ -2794,6 +2800,12 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
default:
/* NSEC3 proof did not work */
break;

View File

@ -7,8 +7,8 @@ Name: unbound
Description: Library with validating, recursive, and caching DNS resolver
URL: http://www.unbound.net
Version: @PACKAGE_VERSION@
Requires: libcrypto libssl @PC_LIBEVENT_DEPENDENCY@
Requires.private: @PC_PY_DEPENDENCY@
Libs: -L${libdir} -lunbound -lssl -lcrypto
Requires: @PC_CRYPTO_DEPENDENCY@ @PC_LIBEVENT_DEPENDENCY@
Requires.private: @PC_PY_DEPENDENCY@ @PC_LIBBSD_DEPENDENCY@
Libs: -L${libdir} -lunbound
Libs.private: @SSLLIB@ @LIBS@
Cflags: -I${includedir}
Cflags: -I${includedir}

View File

@ -14,6 +14,7 @@ int ub_ctx_set_option(ub_ctx*, string, string);
int ub_ctx_get_option(ub_ctx*, string, +string*);
int ub_ctx_config(ub_ctx*, string);
int ub_ctx_set_fwd(ub_ctx*, string);
int ub_ctx_set_tls(ub_ctx*, bool(int));
int ub_ctx_set_stub(ub_ctx*, string, string, bool(int));
int ub_ctx_resolvconf(ub_ctx*, string);
int ub_ctx_hosts(ub_ctx*, string);

View File

@ -0,0 +1,148 @@
>From cc9b927f8f29d989ddb8415fe6508a538546abca Mon Sep 17 00:00:00 2001
From: Jacob Hoffman-Andrews <github@hoffman-andrews.com>
Date: Wed, 2 Jan 2019 22:52:51 -0800
Subject: [PATCH] Add unbound-fuzzme.
This is a small program that simply parses a packet provided on stdout,
for the purposes of fuzzing.
---
.gitignore | 1 +
Makefile.in | 22 ++++++++++++++++++++--
smallapp/unbound-fuzzme.c | 38 ++++++++++++++++++++++++++++++++++++++
3 files changed, 59 insertions(+), 2 deletions(-)
create mode 100644 smallapp/unbound-fuzzme.c
diff --git a/.gitignore b/.gitignore
index f4527fd8..6163f905 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,7 @@
/unbound-checkconf
/unbound-control
/unbound-control-setup
+/unbound-fuzzme
/unbound-host
/unbound.h
/asynclook
diff --git a/Makefile.in b/Makefile.in
index af5b10f6..dacf1ab5 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -177,6 +177,10 @@ shm_main.lo remote.lo stats.lo unbound.lo \
worker.lo @WIN_DAEMON_OBJ@
DAEMON_OBJ_LINK=$(DAEMON_OBJ) $(COMMON_OBJ_ALL_SYMBOLS) $(SLDNS_OBJ) \
$(COMPAT_OBJ) @WIN_DAEMON_OBJ_LINK@
+FUZZME_SRC=smallapp/unbound-fuzzme.c
+FUZZME_OBJ=unbound-fuzzme.lo
+FUZZME_OBJ_LINK=$(FUZZME_OBJ) worker_cb.lo $(COMMON_OBJ_ALL_SYMBOLS) $(SLDNS_OBJ) \
+$(COMPAT_OBJ)
CHECKCONF_SRC=smallapp/unbound-checkconf.c smallapp/worker_cb.c
CHECKCONF_OBJ=unbound-checkconf.lo worker_cb.lo
CHECKCONF_OBJ_LINK=$(CHECKCONF_OBJ) $(COMMON_OBJ_ALL_SYMBOLS) $(SLDNS_OBJ) \
@@ -252,6 +256,7 @@ RSRC_OBJ=rsrc_svcinst.o rsrc_svcuninst.o rsrc_anchorupd.o rsrc_unbound.o \
rsrc_unbound_checkconf.o
ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \
+ $(FUZZME_SRC) \
$(TESTBOUND_SRC) $(LOCKVERIFY_SRC) $(PKTVIEW_SRC) \
$(MEMSTATS_SRC) $(CHECKCONF_SRC) $(LIBUNBOUND_SRC) $(HOST_SRC) \
$(ASYNCLOOK_SRC) $(STREAMTCP_SRC) $(PERF_SRC) $(DELAYER_SRC) \
@@ -259,6 +264,7 @@ ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \
$(PYTHONMOD_SRC) $(PYUNBOUND_SRC) $(WIN_DAEMON_THE_SRC)\
$(SVCINST_SRC) $(SVCUNINST_SRC) $(ANCHORUPD_SRC) $(SLDNS_SRC)
ALL_OBJ=$(COMMON_OBJ) $(UNITTEST_OBJ) $(DAEMON_OBJ) \
+ $(FUZZME_OBJ) \
$(TESTBOUND_OBJ) $(LOCKVERIFY_OBJ) $(PKTVIEW_OBJ) \
$(MEMSTATS_OBJ) $(CHECKCONF_OBJ) $(LIBUNBOUND_OBJ) $(HOST_OBJ) \
$(ASYNCLOOK_OBJ) $(STREAMTCP_OBJ) $(PERF_OBJ) $(DELAYER_OBJ) \
@@ -274,7 +280,7 @@ LINK_LIB=$(LIBTOOL) --tag=CC --mode=link $(CC) $(RUNTIME_PATH) $(CPPFLAGS) $(CFL
all: $(COMMON_OBJ) $(ALLTARGET)
-alltargets: unbound$(EXEEXT) unbound-checkconf$(EXEEXT) lib unbound-host$(EXEEXT) unbound-control$(EXEEXT) unbound-anchor$(EXEEXT) unbound-control-setup $(WINAPPS) $(PYUNBOUND_TARGET)
+alltargets: unbound$(EXEEXT) unbound-checkconf$(EXEEXT) lib unbound-host$(EXEEXT) unbound-control$(EXEEXT) unbound-anchor$(EXEEXT) unbound-control-setup unbound-fuzzme$(EXEEXT) $(WINAPPS) $(PYUNBOUND_TARGET)
# compat with BSD make, register suffix, and an implicit rule to actualise it.
.SUFFIXES: .lo
@@ -325,6 +331,9 @@ libunbound.la: $(LIBUNBOUND_OBJ_LINK)
unbound$(EXEEXT): $(DAEMON_OBJ_LINK) libunbound.la
$(LINK) -o $@ $(DAEMON_OBJ_LINK) $(EXTRALINK) $(SSLLIB) $(LIBS)
+unbound-fuzzme$(EXEEXT): $(FUZZME_OBJ_LINK) libunbound.la
+ $(LINK) -o $@ $(FUZZME_OBJ_LINK) $(EXTRALINK) $(SSLLIB) $(LIBS)
+
unbound-checkconf$(EXEEXT): $(CHECKCONF_OBJ_LINK) libunbound.la
$(LINK) -o $@ $(CHECKCONF_OBJ_LINK) $(EXTRALINK) $(SSLLIB) $(LIBS)
@@ -447,7 +456,7 @@ util/configparser.c util/configparser.h: $(srcdir)/util/configparser.y
clean:
rm -f *.o *.d *.lo *~ tags
- rm -f unbound$(EXEEXT) unbound-checkconf$(EXEEXT) unbound-host$(EXEEXT) unbound-control$(EXEEXT) unbound-anchor$(EXEEXT) unbound-control-setup libunbound.la unbound.h
+ rm -f unbound$(EXEEXT) unbound-checkconf$(EXEEXT) unbound-fuzzme$(EXEEXT) unbound-host$(EXEEXT) unbound-control$(EXEEXT) unbound-anchor$(EXEEXT) unbound-control-setup libunbound.la unbound.h
rm -f $(ALL_SRC:.c=.lint)
rm -f _unbound.la libunbound/python/libunbound_wrap.c libunbound/python/unbound.py pythonmod/interface.h pythonmod/unboundmodule.py
rm -rf autom4te.cache .libs build doc/html doc/xml
@@ -1183,6 +1192,15 @@ stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(s
$(srcdir)/util/storage/slabhash.h $(srcdir)/services/cache/infra.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/rtt.h $(srcdir)/services/authzone.h $(srcdir)/validator/val_kcache.h \
$(srcdir)/validator/val_neg.h
+unbound-fuzzme.lo unbound-fuzzme.o: $(srcdir)/smallapp/unbound-fuzzme.c \
+ $(srcdir)/util/locks.h $(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \
+ $(srcdir)/daemon/remote.h $(srcdir)/util/config_file.h \
+ $(srcdir)/util/storage/slabhash.h $(srcdir)/util/storage/lruhash.h $(srcdir)/services/listen_dnsport.h \
+ $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/services/cache/rrset.h \
+ $(srcdir)/util/data/packed_rrset.h $(srcdir)/services/cache/infra.h $(srcdir)/util/storage/dnstree.h \
+ $(srcdir)/util/rbtree.h $(srcdir)/util/rtt.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/fptr_wlist.h \
+ $(srcdir)/util/module.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h \
+ $(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/util/net_help.h $(srcdir)/util/ub_event.h
unbound.lo unbound.o: $(srcdir)/daemon/unbound.c config.h $(srcdir)/util/log.h $(srcdir)/daemon/daemon.h \
$(srcdir)/util/locks.h $(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \
$(srcdir)/daemon/remote.h \
diff --git a/smallapp/unbound-fuzzme.c b/smallapp/unbound-fuzzme.c
new file mode 100644
index 00000000..74ae5204
--- /dev/null
+++ b/smallapp/unbound-fuzzme.c
@@ -0,0 +1,38 @@
+/*
+ * unbound-fuzzme.c - parse a packet provided on stdin (for fuzzing).
+ *
+ */
+#include "config.h"
+#include "util/regional.h"
+#include "util/fptr_wlist.h"
+#include "sldns/sbuffer.h"
+
+#define SZ 10000
+
+int main() {
+ char buffer[SZ];
+ size_t n_read = fread(buffer, 1, SZ, stdin);
+ if (n_read == SZ) {
+ printf("input too big\n");
+ return 1;
+ }
+ sldns_buffer *pkt = sldns_buffer_new(n_read);
+ sldns_buffer_init_frm_data(pkt, buffer, n_read);
+
+ struct regional *region = regional_create();
+
+ struct msg_parse* prs;
+ struct edns_data edns;
+ prs = (struct msg_parse*)malloc(sizeof(struct msg_parse));
+ if(!prs) {
+ printf("out of memory on incoming message\n");
+ return 1;
+ }
+ memset(prs, 0, sizeof(*prs));
+ memset(&edns, 0, sizeof(edns));
+ sldns_buffer_set_position(pkt, 0);
+ if(parse_packet(pkt, prs, region) != LDNS_RCODE_NOERROR) {
+ printf("parse error\n");
+ return 1;
+ }
+}
--
2.17.1

View File

@ -39,13 +39,13 @@ start() {
# setup root jail
if [ -s /etc/localtime ]; then
[ -d ${rootdir}/etc ] || mkdir -p ${rootdir}/etc ;
if [ ! -e ${rootdir}/etc/localtime ] || /usr/bin/cmp -s /etc/localtime ${rootdir}/etc/localtime; then
if [ ! -e ${rootdir}/etc/localtime ] || ! /usr/bin/cmp -s /etc/localtime ${rootdir}/etc/localtime; then
cp -fp /etc/localtime ${rootdir}/etc/localtime
fi;
fi;
if [ -s /etc/resolv.conf ]; then
[ -d ${rootdir}/etc ] || mkdir -p ${rootdir}/etc ;
if [ ! -e ${rootdir}/etc/resolv.conf ] || /usr/bin/cmp -s /etc/resolv.conf ${rootdir}/etc/resolv.conf; then
if [ ! -e ${rootdir}/etc/resolv.conf ] || ! /usr/bin/cmp -s /etc/resolv.conf ${rootdir}/etc/resolv.conf; then
cp -fp /etc/resolv.conf ${rootdir}/etc/resolv.conf
fi;
fi;
@ -54,10 +54,10 @@ start() {
[ -e ${rootdir}/dev/log ] || touch ${rootdir}/dev/log
mount --bind -n /dev/log ${rootdir}/dev/log >/dev/null 2>&1;
fi;
if ! egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/random' /proc/mounts; then
if ! egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/urandom' /proc/mounts; then
[ -d ${rootdir}/dev ] || mkdir -p ${rootdir}/dev ;
[ -e ${rootdir}/dev/random ] || touch ${rootdir}/dev/random
mount --bind -n /dev/random ${rootdir}/dev/random >/dev/null 2>&1;
[ -e ${rootdir}/dev/urandom ] || touch ${rootdir}/dev/urandom
mount --bind -n /dev/urandom ${rootdir}/dev/urandom >/dev/null 2>&1;
fi;
# if not running, start it up here
@ -78,8 +78,8 @@ stop() {
if egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/log' /proc/mounts; then
umount ${rootdir}/dev/log >/dev/null 2>&1
fi;
if egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/random' /proc/mounts; then
umount ${rootdir}/dev/random >/dev/null 2>&1
if egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/urandom' /proc/mounts; then
umount ${rootdir}/dev/urandom >/dev/null 2>&1
fi;
return $retval
}

View File

@ -42,7 +42,7 @@ start() {
cp -fp /etc/localtime ${rootdir}/etc/localtime
fi;
mount --bind -n /dev/log ${rootdir}/dev/log >/dev/null 2>&1;
mount --bind -n /dev/random ${rootdir}/dev/random >/dev/null 2>&1;
mount --bind -n /dev/urandom ${rootdir}/dev/urandom >/dev/null 2>&1;
mount --bind -n /var/run/unbound ${rootdir}/var/run/unbound >/dev/null 2>&1;
# if not running, start it up here
@ -58,7 +58,7 @@ stop() {
killproc -p $pidfile unbound
retval=$?
[ $retval -eq 0 ] && rm -f $lockfile
for mountfile in /dev/log /dev/random /etc/localtime /etc/resolv.conf /var/run/unbound
for mountfile in /dev/log /dev/urandom /etc/localtime /etc/resolv.conf /var/run/unbound
do
if egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}''${mountfile}'' /proc/mounts; then
umount ${rootdir}$mountfile >/dev/null 2>&1

View File

@ -1,3 +1,44 @@
; For further details about the directives used in this unit file, including
; the below, please refer to systemd's official documentation, available at
; https://www.freedesktop.org/software/systemd/man/systemd.exec.html.
;
;
; - `ProtectSystem=strict` implies we mount the entire file system hierarchy
; read-only for the processes invoked by the unit except for the API file
; system subtrees /dev, /proc and /sys (which are protected by
; PrivateDevices=, ProtectKernelTunables=, ProtectControlGroups=).
;
; - `PrivateTmp=yes` secures access to temporary files of the process, and
; makes sharing between processes via /tmp or /var/tmp impossible.
;
; - `ProtectHome=yes` makes the directories /home, /root, and /run/user
; inaccessible and empty for processes invoked by the unit.
;
; - `ProtectControlGroups=yes` makes the Linux Control Groups hierarchies
; (accessible through /sys/fs/cgroup) read-only to all processes invoked by
; the unit. It also implies `MountAPIVFS=yes`.
;
; - `RuntimeDirectory=unbound` creates a /run/unbound directory, owned by the
; unit User and Group with read-write permissions (0755) as soon as the
; unit starts. This allows unbound to store its pidfile. The directory and
; its content are automatically removed by systemd when the unit stops.
;
; - `NoNewPrivileges=yes` ensures that the service process and all its
; children can never gain new privileges through execve().
;
; - `RestrictSUIDSGID=yes` ensures that any attempts to set the set-user-ID
; (SUID) or set-group-ID (SGID) bits on files or directories will be denied.
;
; - `RestrictRealTime=yes` ensures that any attempts to enable realtime
; scheduling in a process invoked by the unit will be denied.
;
; - `RestrictNamespaces=yes` ensures that access to any kind of namespacing
; is prohibited.
;
; - `LockPersonality=yes` locks down the personality system call so that the
; kernel execution domain may not be changed from the default.
;
;
[Unit]
Description=Validating, recursive, and caching DNS resolver
Documentation=man:unbound(8)
@ -9,11 +50,11 @@ Wants=nss-lookup.target
WantedBy=multi-user.target
[Service]
ExecReload=/bin/kill -HUP $MAINPID
ExecStart=@UNBOUND_SBIN_DIR@/unbound
ExecReload=+/bin/kill -HUP $MAINPID
ExecStart=@UNBOUND_SBIN_DIR@/unbound -d -p
NotifyAccess=main
Type=notify
CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_SYS_RESOURCE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_SYS_RESOURCE CAP_NET_RAW
MemoryDenyWriteExecute=true
NoNewPrivileges=true
PrivateDevices=true
@ -21,11 +62,23 @@ PrivateTmp=true
ProtectHome=true
ProtectControlGroups=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectSystem=strict
ReadWritePaths=@UNBOUND_SYSCONF_DIR@ @UNBOUND_LOCALSTATE_DIR@ /run @UNBOUND_RUN_DIR@
RuntimeDirectory=unbound
ConfigurationDirectory=unbound
StateDirectory=unbound
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictRealtime=true
SystemCallArchitectures=native
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module mount @obsolete @resources
RestrictNamespaces=yes
LockPersonality=yes
RestrictSUIDSGID=yes
ReadWritePaths=@UNBOUND_RUN_DIR@ @UNBOUND_CHROOT_DIR@
# Below rules are needed when chroot is enabled (usually it's enabled by default).
# If chroot is disabled like chrooot: "" then they may be safely removed.
TemporaryFileSystem=@UNBOUND_CHROOT_DIR@/dev:ro
TemporaryFileSystem=@UNBOUND_CHROOT_DIR@/run:ro
BindReadOnlyPaths=-/run/systemd/notify:@UNBOUND_CHROOT_DIR@/run/systemd/notify
BindReadOnlyPaths=-/dev/urandom:@UNBOUND_CHROOT_DIR@/dev/urandom
BindPaths=-/dev/log:@UNBOUND_CHROOT_DIR@/dev/log

View File

@ -242,6 +242,8 @@ if test "$1" = "config" ; then
p_config "total.num.prefetch" "cache prefetch" "ABSOLUTE"
p_config "num.query.tcp" "TCP queries" "ABSOLUTE"
p_config "num.query.tcpout" "TCP out queries" "ABSOLUTE"
p_config "num.query.tls" "TLS queries" "ABSOLUTE"
p_config "num.query.tls.resume" "TLS resumes" "ABSOLUTE"
p_config "num.query.ipv6" "IPv6 queries" "ABSOLUTE"
p_config "unwanted.queries" "queries that failed acl" "ABSOLUTE"
p_config "unwanted.replies" "unwanted or unsolicited replies" "ABSOLUTE"
@ -443,7 +445,8 @@ hits)
for x in `grep "^thread[0-9][0-9]*\.num\.queries=" $state |
sed -e 's/=.*//'` total.num.queries \
total.num.cachehits total.num.prefetch num.query.tcp \
num.query.tcpout num.query.ipv6 unwanted.queries \
num.query.tcpout num.query.tls num.query.tls.resume \
num.query.ipv6 unwanted.queries \
unwanted.replies; do
if grep "^"$x"=" $state >/dev/null 2>&1; then
print_value $x

View File

@ -0,0 +1,49 @@
; This unit file is provided to run unbound as portable service.
; https://systemd.io/PORTABLE_SERVICES/
;
; To use this unit file, please make sure you either compile unbound with the
; following options:
;
; - --with-chroot-dir=""
;
; Or put the following options in your unbound configuration file:
;
; - chroot: ""
;
;
[Unit]
Description=Validating, recursive, and caching DNS resolver
Documentation=man:unbound(8)
After=network.target
Before=network-online.target nss-lookup.target
Wants=nss-lookup.target
[Install]
WantedBy=multi-user.target
[Service]
ExecReload=+/bin/kill -HUP $MAINPID
ExecStart=@UNBOUND_SBIN_DIR@/unbound -d -p
NotifyAccess=main
Type=notify
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_SYS_RESOURCE CAP_NET_RAW
MemoryDenyWriteExecute=true
NoNewPrivileges=true
PrivateDevices=true
PrivateTmp=true
ProtectHome=true
ProtectControlGroups=true
ProtectKernelModules=true
ProtectSystem=strict
RuntimeDirectory=unbound
ConfigurationDirectory=unbound
StateDirectory=unbound
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictRealtime=true
SystemCallArchitectures=native
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module mount @obsolete @resources
RestrictNamespaces=yes
LockPersonality=yes
RestrictSUIDSGID=yes
BindPaths=/run/systemd/notify
BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout

View File

@ -221,7 +221,9 @@ daemon_init(void)
(void)sldns_key_EVP_load_gost_id();
# endif
# if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO)
# ifndef S_SPLINT_S
OpenSSL_add_all_algorithms();
# endif
# else
OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
| OPENSSL_INIT_ADD_ALL_DIGESTS
@ -248,8 +250,6 @@ daemon_init(void)
/* init timezone info while we are not chrooted yet */
tzset();
#endif
/* open /dev/random if needed */
ub_systemseed((unsigned)time(NULL)^(unsigned)getpid()^0xe67);
daemon->need_to_exit = 0;
modstack_init(&daemon->mods);
if(!(daemon->env = (struct module_env*)calloc(1,
@ -427,9 +427,7 @@ daemon_create_workers(struct daemon* daemon)
int* shufport;
log_assert(daemon && daemon->cfg);
if(!daemon->rand) {
unsigned int seed = (unsigned int)time(NULL) ^
(unsigned int)getpid() ^ 0x438;
daemon->rand = ub_initstate(seed, NULL);
daemon->rand = ub_initstate(NULL);
if(!daemon->rand)
fatal_exit("could not init random generator");
hash_set_raninit((uint32_t)ub_random(daemon->rand));
@ -575,6 +573,9 @@ void
daemon_fork(struct daemon* daemon)
{
int have_view_respip_cfg = 0;
#ifdef HAVE_SYSTEMD
int ret;
#endif
log_assert(daemon);
if(!(daemon->views = views_create()))
@ -616,7 +617,8 @@ daemon_fork(struct daemon* daemon)
have_view_respip_cfg;
/* read auth zonefiles */
if(!auth_zones_apply_cfg(daemon->env->auth_zones, daemon->cfg, 1))
if(!auth_zones_apply_cfg(daemon->env->auth_zones, daemon->cfg, 1,
&daemon->use_rpz))
fatal_exit("auth_zones could not be setup");
/* setup modules */
@ -628,6 +630,12 @@ daemon_fork(struct daemon* daemon)
if(daemon->use_response_ip &&
modstack_find(&daemon->mods, "respip") < 0)
fatal_exit("response-ip options require respip module");
/* RPZ response ip triggers don't work as expected without the respip
* module. To avoid run-time operational surprise we reject such
* configuration. */
if(daemon->use_rpz &&
modstack_find(&daemon->mods, "respip") < 0)
fatal_exit("RPZ requires the respip module");
/* first create all the worker structures, so we can pass
* them to the newly created threads.
@ -660,7 +668,12 @@ daemon_fork(struct daemon* daemon)
/* Start resolver service on main thread. */
#ifdef HAVE_SYSTEMD
sd_notify(0, "READY=1");
ret = sd_notify(0, "READY=1");
if(ret <= 0 && getenv("NOTIFY_SOCKET"))
fatal_exit("sd_notify failed %s: %s. Make sure that unbound has "
"access/permission to use the socket presented by systemd.",
getenv("NOTIFY_SOCKET"),
(ret==0?"no $NOTIFY_SOCKET": strerror(-ret)));
#endif
log_info("start of service (%s).", PACKAGE_STRING);
worker_work(daemon->workers[0]);
@ -749,6 +762,7 @@ daemon_delete(struct daemon* daemon)
free(daemon->pidfile);
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);
#endif
@ -769,7 +783,7 @@ daemon_delete(struct daemon* daemon)
# endif
# ifdef HAVE_OPENSSL_CONFIG
EVP_cleanup();
# if OPENSSL_VERSION_NUMBER < 0x10100000
# if (OPENSSL_VERSION_NUMBER < 0x10100000) && !defined(OPENSSL_NO_ENGINE)
ENGINE_cleanup();
# endif
CONF_modules_free();

View File

@ -132,6 +132,8 @@ struct daemon {
struct respip_set* respip_set;
/** some response-ip tags or actions are configured if true */
int use_response_ip;
/** some RPZ policies are configured */
int use_rpz;
#ifdef USE_DNSCRYPT
/** the dnscrypt environment */
struct dnsc_env* dnscenv;

View File

@ -69,6 +69,7 @@
#include "services/mesh.h"
#include "services/localzone.h"
#include "services/authzone.h"
#include "services/rpz.h"
#include "util/storage/slabhash.h"
#include "util/fptr_wlist.h"
#include "util/data/dname.h"
@ -499,7 +500,7 @@ int remote_accept_callback(struct comm_point* c, void* arg, int err,
goto close_exit;
}
SSL_set_accept_state(n->ssl);
(void)SSL_set_mode(n->ssl, SSL_MODE_AUTO_RETRY);
(void)SSL_set_mode(n->ssl, (long)SSL_MODE_AUTO_RETRY);
if(!SSL_set_fd(n->ssl, newfd)) {
log_crypto_err("could not SSL_set_fd");
SSL_free(n->ssl);
@ -674,19 +675,19 @@ static void send_ok(RES* ssl)
/** do the stop command */
static void
do_stop(RES* ssl, struct daemon_remote* rc)
do_stop(RES* ssl, struct worker* worker)
{
rc->worker->need_to_exit = 1;
comm_base_exit(rc->worker->base);
worker->need_to_exit = 1;
comm_base_exit(worker->base);
send_ok(ssl);
}
/** do the reload command */
static void
do_reload(RES* ssl, struct daemon_remote* rc)
do_reload(RES* ssl, struct worker* worker)
{
rc->worker->need_to_exit = 0;
comm_base_exit(rc->worker->base);
worker->need_to_exit = 0;
comm_base_exit(worker->base);
send_ok(ssl);
}
@ -719,8 +720,8 @@ print_stats(RES* ssl, const char* nm, struct ub_stats_info* s)
(unsigned long)s->svr.num_queries_missed_cache)) return 0;
if(!ssl_printf(ssl, "%s.num.prefetch"SQ"%lu\n", nm,
(unsigned long)s->svr.num_queries_prefetch)) return 0;
if(!ssl_printf(ssl, "%s.num.zero_ttl"SQ"%lu\n", nm,
(unsigned long)s->svr.zero_ttl_responses)) return 0;
if(!ssl_printf(ssl, "%s.num.expired"SQ"%lu\n", nm,
(unsigned long)s->svr.ans_expired)) return 0;
if(!ssl_printf(ssl, "%s.num.recursivereplies"SQ"%lu\n", nm,
(unsigned long)s->mesh_replies_sent)) return 0;
#ifdef USE_DNSCRYPT
@ -789,7 +790,8 @@ print_longnum(RES* ssl, const char* desc, size_t x)
/** print mem stats */
static int
print_mem(RES* ssl, struct worker* worker, struct daemon* daemon)
print_mem(RES* ssl, struct worker* worker, struct daemon* daemon,
struct ub_stats_info* s)
{
size_t msg, rrset, val, iter, respip;
#ifdef CLIENT_SUBNET
@ -847,6 +849,9 @@ print_mem(RES* ssl, struct worker* worker, struct daemon* daemon)
dnscrypt_nonce))
return 0;
#endif /* USE_DNSCRYPT */
if(!print_longnum(ssl, "mem.streamwait"SQ,
(size_t)s->svr.mem_stream_wait))
return 0;
return 1;
}
@ -969,6 +974,8 @@ print_ext(RES* ssl, struct ub_stats_info* s)
(unsigned long)s->svr.qtcp_outgoing)) return 0;
if(!ssl_printf(ssl, "num.query.tls"SQ"%lu\n",
(unsigned long)s->svr.qtls)) return 0;
if(!ssl_printf(ssl, "num.query.tls.resume"SQ"%lu\n",
(unsigned long)s->svr.qtls_resume)) return 0;
if(!ssl_printf(ssl, "num.query.ipv6"SQ"%lu\n",
(unsigned long)s->svr.qipv6)) return 0;
/* flags */
@ -1039,6 +1046,16 @@ print_ext(RES* ssl, struct ub_stats_info* s)
(unsigned)s->svr.infra_cache_count)) return 0;
if(!ssl_printf(ssl, "key.cache.count"SQ"%u\n",
(unsigned)s->svr.key_cache_count)) return 0;
/* applied RPZ actions */
for(i=0; i<UB_STATS_RPZ_ACTION_NUM; i++) {
if(i == RPZ_NO_OVERRIDE_ACTION)
continue;
if(inhibit_zero && s->svr.rpz_action[i] == 0)
continue;
if(!ssl_printf(ssl, "num.rpz.action.%s"SQ"%lu\n",
rpz_action_to_string(i),
(unsigned long)s->svr.rpz_action[i])) return 0;
}
#ifdef USE_DNSCRYPT
if(!ssl_printf(ssl, "dnscrypt_shared_secret.cache.count"SQ"%u\n",
(unsigned)s->svr.shared_secret_cache_count)) return 0;
@ -1064,9 +1081,9 @@ print_ext(RES* ssl, struct ub_stats_info* s)
/** do the stats command */
static void
do_stats(RES* ssl, struct daemon_remote* rc, int reset)
do_stats(RES* ssl, struct worker* worker, int reset)
{
struct daemon* daemon = rc->worker->daemon;
struct daemon* daemon = worker->daemon;
struct ub_stats_info total;
struct ub_stats_info s;
int i;
@ -1074,7 +1091,7 @@ do_stats(RES* ssl, struct daemon_remote* rc, int reset)
log_assert(daemon->num > 0);
/* gather all thread statistics in one place */
for(i=0; i<daemon->num; i++) {
server_stats_obtain(rc->worker, daemon->workers[i], &s, reset);
server_stats_obtain(worker, daemon->workers[i], &s, reset);
if(!print_thread_stats(ssl, i, &s))
return;
if(i == 0)
@ -1085,10 +1102,10 @@ do_stats(RES* ssl, struct daemon_remote* rc, int reset)
total.mesh_time_median /= (double)daemon->num;
if(!print_stats(ssl, "total", &total))
return;
if(!print_uptime(ssl, rc->worker, reset))
if(!print_uptime(ssl, worker, reset))
return;
if(daemon->cfg->stat_extended) {
if(!print_mem(ssl, rc->worker, daemon))
if(!print_mem(ssl, worker, daemon, &total))
return;
if(!print_hist(ssl, &total))
return;
@ -1428,6 +1445,28 @@ do_view_data_add(RES* ssl, struct worker* worker, char* arg)
lock_rw_unlock(&v->lock);
}
/** Add new RR data from stdin to view */
static void
do_view_datas_add(RES* ssl, struct worker* worker, char* arg)
{
struct view* v;
v = views_find_view(worker->daemon->views,
arg, 1 /* get write lock*/);
if(!v) {
ssl_printf(ssl,"no view with name: %s\n", arg);
return;
}
if(!v->local_zones) {
if(!(v->local_zones = local_zones_create())){
lock_rw_unlock(&v->lock);
ssl_printf(ssl,"error out of memory\n");
return;
}
}
do_datas_add(ssl, v->local_zones);
lock_rw_unlock(&v->lock);
}
/** Remove RR data from view */
static void
do_view_data_remove(RES* ssl, struct worker* worker, char* arg)
@ -1451,6 +1490,27 @@ do_view_data_remove(RES* ssl, struct worker* worker, char* arg)
lock_rw_unlock(&v->lock);
}
/** Remove RR data from stdin from view */
static void
do_view_datas_remove(RES* ssl, struct worker* worker, char* arg)
{
struct view* v;
v = views_find_view(worker->daemon->views,
arg, 1 /* get write lock*/);
if(!v) {
ssl_printf(ssl,"no view with name: %s\n", arg);
return;
}
if(!v->local_zones){
lock_rw_unlock(&v->lock);
ssl_printf(ssl, "removed 0 datas\n");
return;
}
do_datas_remove(ssl, v->local_zones);
lock_rw_unlock(&v->lock);
}
/** cache lookup of nameservers */
static void
do_lookup(RES* ssl, struct worker* worker, char* arg)
@ -1959,7 +2019,7 @@ parse_delegpt(RES* ssl, char* args, uint8_t* nm, int allow_names)
return NULL;
}
} else {
#ifndef HAVE_SSL_SET1_HOST
#if ! defined(HAVE_SSL_SET1_HOST) && ! defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
if(auth_name)
log_err("no name verification functionality in "
"ssl library, ignored name for %s", todo);
@ -2456,7 +2516,7 @@ do_auth_zone_reload(RES* ssl, struct worker* worker, char* arg)
(void)ssl_printf(ssl, "error no auth-zone %s\n", arg);
return;
}
if(!auth_zone_read_zonefile(z)) {
if(!auth_zone_read_zonefile(z, worker->env.cfg)) {
lock_rw_unlock(&z->lock);
(void)ssl_printf(ssl, "error failed to read %s\n", arg);
return;
@ -2478,8 +2538,10 @@ do_auth_zone_transfer(RES* ssl, struct worker* worker, char* arg)
if(!az || !auth_zones_startprobesequence(az, &worker->env, nm, nmlen,
LDNS_RR_CLASS_IN)) {
(void)ssl_printf(ssl, "error zone xfr task not found %s\n", arg);
free(nm);
return;
}
free(nm);
send_ok(ssl);
}
@ -2823,16 +2885,16 @@ execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd,
char* p = skipwhite(cmd);
/* compare command */
if(cmdcmp(p, "stop", 4)) {
do_stop(ssl, rc);
do_stop(ssl, worker);
return;
} else if(cmdcmp(p, "reload", 6)) {
do_reload(ssl, rc);
do_reload(ssl, worker);
return;
} else if(cmdcmp(p, "stats_noreset", 13)) {
do_stats(ssl, rc, 0);
do_stats(ssl, worker, 0);
return;
} else if(cmdcmp(p, "stats", 5)) {
do_stats(ssl, rc, 1);
do_stats(ssl, worker, 1);
return;
} else if(cmdcmp(p, "status", 6)) {
do_status(ssl, worker);
@ -2961,8 +3023,12 @@ execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd,
do_view_zone_add(ssl, worker, skipwhite(p+15));
} else if(cmdcmp(p, "view_local_data_remove", 22)) {
do_view_data_remove(ssl, worker, skipwhite(p+22));
} else if(cmdcmp(p, "view_local_datas_remove", 23)){
do_view_datas_remove(ssl, worker, skipwhite(p+23));
} else if(cmdcmp(p, "view_local_data", 15)) {
do_view_data_add(ssl, worker, skipwhite(p+15));
} else if(cmdcmp(p, "view_local_datas", 16)) {
do_view_datas_add(ssl, worker, skipwhite(p+16));
} else if(cmdcmp(p, "flush_zone", 10)) {
do_flush_zone(ssl, worker, skipwhite(p+10));
} else if(cmdcmp(p, "flush_type", 10)) {
@ -3095,7 +3161,7 @@ remote_handshake_later(struct daemon_remote* rc, struct rc_state* s,
} else {
if(r == 0)
log_err("remote control connection closed prematurely");
log_addr(1, "failed connection from",
log_addr(VERB_OPS, "failed connection from",
&s->c->repinfo.addr, s->c->repinfo.addrlen);
log_crypto_err("remote control failed ssl");
clean_point(rc, s);

View File

@ -66,6 +66,9 @@
#ifdef CLIENT_SUBNET
#include "edns-subnet/subnetmod.h"
#endif
#ifdef HAVE_SSL
#include <openssl/ssl.h>
#endif
/** add timers and the values do not overflow or become negative */
static void
@ -74,7 +77,7 @@ stats_timeval_add(long long* d_sec, long long* d_usec, long long add_sec, long l
#ifndef S_SPLINT_S
(*d_sec) += add_sec;
(*d_usec) += add_usec;
if((*d_usec) > 1000000) {
if((*d_usec) >= 1000000) {
(*d_usec) -= 1000000;
(*d_sec)++;
}
@ -268,8 +271,10 @@ server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset)
s->svr.ans_secure += (long long)worker->env.mesh->ans_secure;
s->svr.ans_bogus += (long long)worker->env.mesh->ans_bogus;
s->svr.ans_rcode_nodata += (long long)worker->env.mesh->ans_nodata;
for(i=0; i<16; i++)
for(i=0; i<UB_STATS_RCODE_NUM; i++)
s->svr.ans_rcode[i] += (long long)worker->env.mesh->ans_rcode[i];
for(i=0; i<UB_STATS_RPZ_ACTION_NUM; i++)
s->svr.rpz_action[i] += (long long)worker->env.mesh->rpz_action[i];
timehist_export(worker->env.mesh->histogram, s->svr.hist,
NUM_BUCKETS_HIST);
/* values from outside network */
@ -328,6 +333,8 @@ server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset)
}
lock_rw_unlock(&worker->env.auth_zones->lock);
}
s->svr.mem_stream_wait =
(long long)tcp_req_info_get_stream_buffer_size();
/* Set neg cache usage numbers */
set_neg_cache_stats(worker, &s->svr, reset);
@ -393,6 +400,7 @@ void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a)
total->svr.num_queries_missed_cache += a->svr.num_queries_missed_cache;
total->svr.num_queries_prefetch += a->svr.num_queries_prefetch;
total->svr.sum_query_list_size += a->svr.sum_query_list_size;
total->svr.ans_expired += a->svr.ans_expired;
#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;
@ -412,6 +420,7 @@ void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a)
total->svr.qtcp += a->svr.qtcp;
total->svr.qtcp_outgoing += a->svr.qtcp_outgoing;
total->svr.qtls += a->svr.qtls;
total->svr.qtls_resume += a->svr.qtls_resume;
total->svr.qipv6 += a->svr.qipv6;
total->svr.qbit_QR += a->svr.qbit_QR;
total->svr.qbit_AA += a->svr.qbit_AA;
@ -424,7 +433,6 @@ void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a)
total->svr.qEDNS += a->svr.qEDNS;
total->svr.qEDNS_DO += a->svr.qEDNS_DO;
total->svr.ans_rcode_nodata += a->svr.ans_rcode_nodata;
total->svr.zero_ttl_responses += a->svr.zero_ttl_responses;
total->svr.ans_secure += a->svr.ans_secure;
total->svr.ans_bogus += a->svr.ans_bogus;
total->svr.unwanted_replies += a->svr.unwanted_replies;
@ -440,6 +448,8 @@ void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a)
total->svr.ans_rcode[i] += a->svr.ans_rcode[i];
for(i=0; i<NUM_BUCKETS_HIST; i++)
total->svr.hist[i] += a->svr.hist[i];
for(i=0; i<UB_STATS_RPZ_ACTION_NUM; i++)
total->svr.rpz_action[i] += a->svr.rpz_action[i];
}
total->mesh_num_states += a->mesh_num_states;
@ -468,8 +478,13 @@ void server_stats_insquery(struct ub_server_stats* stats, struct comm_point* c,
stats->qopcode[ LDNS_OPCODE_WIRE(sldns_buffer_begin(c->buffer)) ]++;
if(c->type != comm_udp) {
stats->qtcp++;
if(c->ssl != NULL)
if(c->ssl != NULL) {
stats->qtls++;
#ifdef HAVE_SSL
if(SSL_session_reused(c->ssl))
stats->qtls_resume++;
#endif
}
}
if(repinfo && addr_is_ip6(&repinfo->addr, repinfo->addrlen))
stats->qipv6++;

View File

@ -67,6 +67,7 @@
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#include <openssl/ssl.h>
#ifndef S_SPLINT_S
/* splint chokes on this system header file */
@ -87,31 +88,20 @@
# include "nss.h"
#endif
/** print usage. */
static void usage(void)
/** print build options. */
static void
print_build_options(void)
{
const char** m;
const char *evnm="event", *evsys="", *evmethod="";
time_t t;
struct timeval now;
struct ub_event_base* base;
printf("usage: local-unbound [options]\n");
printf(" start unbound daemon DNS resolver.\n");
printf("-h this help\n");
printf("-c file config file to read instead of %s\n", CONFIGFILE);
printf(" file format is described in unbound.conf(5).\n");
printf("-d do not fork into the background.\n");
printf("-p do not create a pidfile.\n");
printf("-v verbose (more times to increase verbosity)\n");
#ifdef UB_ON_WINDOWS
printf("-w opt windows option: \n");
printf(" install, remove - manage the services entry\n");
printf(" service - used to start from services control panel\n");
#endif
printf("Version %s\n", PACKAGE_VERSION);
printf("Version %s\n\n", PACKAGE_VERSION);
printf("Configure line: %s\n", CONFCMDLINE);
base = ub_default_event_base(0,&t,&now);
ub_get_event_sys(base, &evnm, &evsys, &evmethod);
printf("linked libs: %s %s (it uses %s), %s\n",
printf("Linked libs: %s %s (it uses %s), %s\n",
evnm, evsys, evmethod,
#ifdef HAVE_SSL
# ifdef SSLEAY_VERSION
@ -125,16 +115,42 @@ static void usage(void)
"nettle"
#endif
);
printf("linked modules:");
printf("Linked modules:");
for(m = module_list_avail(); *m; m++)
printf(" %s", *m);
printf("\n");
#ifdef USE_DNSCRYPT
printf("DNSCrypt feature available\n");
#endif
#ifdef USE_TCP_FASTOPEN
printf("TCP Fastopen feature available\n");
#endif
ub_event_base_free(base);
printf("\nBSD licensed, see LICENSE in source package for details.\n");
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
}
/** print usage. */
static void
usage(void)
{
printf("usage: unbound [options]\n");
printf(" start unbound daemon DNS resolver.\n");
printf("-h this help.\n");
printf("-c file config file to read instead of %s\n", CONFIGFILE);
printf(" file format is described in unbound.conf(5).\n");
printf("-d do not fork into the background.\n");
printf("-p do not create a pidfile.\n");
printf("-v verbose (more times to increase verbosity).\n");
printf("-V show version number and build options.\n");
#ifdef UB_ON_WINDOWS
printf("-w opt windows option: \n");
printf(" install, remove - manage the services entry\n");
printf(" service - used to start from services control panel\n");
#endif
printf("\nVersion %s\n", PACKAGE_VERSION);
printf("BSD licensed, see LICENSE in source package for details.\n");
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
ub_event_base_free(base);
}
#ifndef unbound_testbound
@ -243,21 +259,10 @@ checkrlimits(struct config_file* cfg)
#endif /* S_SPLINT_S */
}
/** set default logfile identity based on value from argv[0] at startup **/
static void
log_ident_set_fromdefault(struct config_file* cfg,
const char *log_default_identity)
{
if(cfg->log_identity == NULL || cfg->log_identity[0] == 0)
log_ident_set(log_default_identity);
else
log_ident_set(cfg->log_identity);
}
/** set verbosity, check rlimits, cache settings */
static void
apply_settings(struct daemon* daemon, struct config_file* cfg,
int cmdline_verbose, int debug_mode, const char* log_default_identity)
apply_settings(struct daemon* daemon, struct config_file* cfg,
int cmdline_verbose, int debug_mode)
{
/* apply if they have changed */
verbosity = cmdline_verbose + cfg->verbosity;
@ -273,7 +278,7 @@ apply_settings(struct daemon* daemon, struct config_file* cfg,
log_warn("use-systemd and do-daemonize should not be enabled at the same time");
}
log_ident_set_fromdefault(cfg, log_default_identity);
log_ident_set_or_default(cfg->log_identity);
}
#ifdef HAVE_KILL
@ -430,6 +435,24 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
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)))
@ -605,11 +628,10 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
* @param cmdline_verbose: verbosity resulting from commandline -v.
* These increase verbosity as specified in the config file.
* @param debug_mode: if set, do not daemonize.
* @param log_default_identity: Default identity to report in logs
* @param need_pidfile: if false, no pidfile is checked or created.
*/
static void
run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode, const char* log_default_identity, int need_pidfile)
run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode, int need_pidfile)
{
struct config_file* cfg = NULL;
struct daemon* daemon = NULL;
@ -633,7 +655,7 @@ run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode, const char*
"or unbound-checkconf", cfgfile);
log_warn("Continuing with default config settings");
}
apply_settings(daemon, cfg, cmdline_verbose, debug_mode, log_default_identity);
apply_settings(daemon, cfg, cmdline_verbose, debug_mode);
if(!done_setup)
config_lookup_uid(cfg);
@ -699,9 +721,10 @@ main(int argc, char* argv[])
log_init(NULL, 0, NULL);
log_ident_default = strrchr(argv[0],'/')?strrchr(argv[0],'/')+1:argv[0];
log_ident_set_default(log_ident_default);
log_ident_set(log_ident_default);
/* parse the options */
while( (c=getopt(argc, argv, "c:dhpvw:")) != -1) {
while( (c=getopt(argc, argv, "c:dhpvw:V")) != -1) {
switch(c) {
case 'c':
cfgfile = optarg;
@ -722,6 +745,9 @@ main(int argc, char* argv[])
case 'w':
winopt = optarg;
break;
case 'V':
print_build_options();
return 0;
case '?':
case 'h':
default:
@ -746,11 +772,11 @@ main(int argc, char* argv[])
return 1;
}
run_daemon(cfgfile, cmdline_verbose, debug_mode, log_ident_default, need_pidfile);
run_daemon(cfgfile, cmdline_verbose, debug_mode, need_pidfile);
log_init(NULL, 0, NULL); /* close logfile */
#ifndef unbound_testbound
if(log_get_lock()) {
lock_quick_destroy((lock_quick_type*)log_get_lock());
lock_basic_destroy((lock_basic_type*)log_get_lock());
}
#endif
return 0;

View File

@ -61,6 +61,7 @@
#include "services/authzone.h"
#include "services/mesh.h"
#include "services/localzone.h"
#include "services/rpz.h"
#include "util/data/msgparse.h"
#include "util/data/msgencode.h"
#include "util/data/dname.h"
@ -572,9 +573,10 @@ static int
apply_respip_action(struct worker* worker, const struct query_info* qinfo,
struct respip_client_info* cinfo, struct reply_info* rep,
struct comm_reply* repinfo, struct ub_packed_rrset_key** alias_rrset,
struct reply_info** encode_repp)
struct reply_info** encode_repp, struct auth_zones* az)
{
struct respip_action_info actinfo = {respip_none, NULL};
struct respip_action_info actinfo = {0};
actinfo.action = respip_none;
if(qinfo->qtype != LDNS_RR_TYPE_A &&
qinfo->qtype != LDNS_RR_TYPE_AAAA &&
@ -582,7 +584,7 @@ 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))
alias_rrset, 0, worker->scratchpad, az))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
@ -595,9 +597,19 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo,
/* If address info is returned, it means the action should be an
* 'inform' variant and the information should be logged. */
if(actinfo.addrinfo) {
respip_inform_print(actinfo.addrinfo, qinfo->qname,
respip_inform_print(&actinfo, qinfo->qname,
qinfo->qtype, qinfo->qclass, qinfo->local_alias,
repinfo);
if(worker->stats.extended && actinfo.rpz_used) {
if(actinfo.rpz_disabled)
worker->stats.rpz_action[RPZ_DISABLED_ACTION]++;
if(actinfo.rpz_cname_override)
worker->stats.rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
else
worker->stats.rpz_action[
respip_action_to_rpz_action(actinfo.action)]++;
}
}
return 1;
@ -613,10 +625,10 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo,
* be completely dropped, '*need_drop' will be set to 1. */
static int
answer_from_cache(struct worker* worker, struct query_info* qinfo,
struct respip_client_info* cinfo, int* need_drop,
struct ub_packed_rrset_key** alias_rrset,
struct respip_client_info* cinfo, int* need_drop, int* is_expired_answer,
int* is_secure_answer, struct ub_packed_rrset_key** alias_rrset,
struct reply_info** partial_repp,
struct reply_info* rep, uint16_t id, uint16_t flags,
struct reply_info* rep, uint16_t id, uint16_t flags,
struct comm_reply* repinfo, struct edns_data* edns)
{
struct edns_data edns_bak;
@ -624,46 +636,42 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
uint16_t udpsize = edns->udp_size;
struct reply_info* encode_rep = rep;
struct reply_info* partial_rep = *partial_repp;
int secure;
int must_validate = (!(flags&BIT_CD) || worker->env.cfg->ignore_cd)
&& worker->env.need_to_validate;
*partial_repp = NULL; /* avoid accidental further pass */
if(worker->env.cfg->serve_expired) {
if(worker->env.cfg->serve_expired_ttl &&
rep->serve_expired_ttl < timenow)
return 0;
if(!rrset_array_lock(rep->ref, rep->rrset_count, 0))
return 0;
/* below, rrsets with ttl before timenow become TTL 0 in
* the response */
/* This response was served with zero TTL */
if (timenow >= rep->ttl) {
worker->stats.zero_ttl_responses++;
}
} else {
/* see if it is possible */
if(rep->ttl < timenow) {
*partial_repp = NULL; /* avoid accidental further pass */
/* Check TTL */
if(rep->ttl < timenow) {
/* Check if we need to serve expired now */
if(worker->env.cfg->serve_expired &&
!worker->env.cfg->serve_expired_client_timeout) {
if(worker->env.cfg->serve_expired_ttl &&
rep->serve_expired_ttl < timenow)
return 0;
if(!rrset_array_lock(rep->ref, rep->rrset_count, 0))
return 0;
*is_expired_answer = 1;
} else {
/* the rrsets may have been updated in the meantime.
* we will refetch the message format from the
* authoritative server
* authoritative server
*/
return 0;
}
} else {
if(!rrset_array_lock(rep->ref, rep->rrset_count, timenow))
return 0;
/* locked and ids and ttls are OK. */
}
/* locked and ids and ttls are OK. */
/* check CNAME chain (if any) */
if(rep->an_numrrsets > 0 && (rep->rrsets[0]->rk.type ==
htons(LDNS_RR_TYPE_CNAME) || rep->rrsets[0]->rk.type ==
if(rep->an_numrrsets > 0 && (rep->rrsets[0]->rk.type ==
htons(LDNS_RR_TYPE_CNAME) || rep->rrsets[0]->rk.type ==
htons(LDNS_RR_TYPE_DNAME))) {
if(!reply_check_cname_chain(qinfo, rep)) {
/* cname chain invalid, redo iterator steps */
verbose(VERB_ALGO, "Cache reply: cname chain broken");
bail_out:
rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count);
return 0;
goto bail_out;
}
}
/* check security status of the cached answer */
@ -677,31 +685,31 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, rep,
LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad))
goto bail_out;
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, id, flags, edns);
rrset_array_unlock_touch(worker->env.rrset_cache,
rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count);
if(worker->stats.extended) {
worker->stats.ans_bogus ++;
worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL] ++;
}
return 1;
} else if( rep->security == sec_status_unchecked && must_validate) {
} else if(rep->security == sec_status_unchecked && must_validate) {
verbose(VERB_ALGO, "Cache reply: unchecked entry needs "
"validation");
goto bail_out; /* need to validate cache entry first */
} else if(rep->security == sec_status_secure) {
if(reply_all_rrsets_secure(rep))
secure = 1;
else {
if(reply_all_rrsets_secure(rep)) {
*is_secure_answer = 1;
} else {
if(must_validate) {
verbose(VERB_ALGO, "Cache reply: secure entry"
" changed status");
goto bail_out; /* rrset changed, re-verify */
}
secure = 0;
*is_secure_answer = 0;
}
} else secure = 0;
} else *is_secure_answer = 0;
edns_bak = *edns;
edns->edns_version = EDNS_ADVERTISED_VERSION;
@ -712,20 +720,22 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
(int)(flags&LDNS_RCODE_MASK), edns, repinfo, worker->scratchpad))
goto bail_out;
*alias_rrset = NULL; /* avoid confusion if caller set it to non-NULL */
if(worker->daemon->use_response_ip && !partial_rep &&
!apply_respip_action(worker, qinfo, cinfo, rep, repinfo, alias_rrset,
&encode_rep)) {
if((worker->daemon->use_response_ip || worker->daemon->use_rpz) &&
!partial_rep && !apply_respip_action(worker, qinfo, cinfo, rep,
repinfo, alias_rrset,
&encode_rep, worker->env.auth_zones)) {
goto bail_out;
} else if(partial_rep &&
!respip_merge_cname(partial_rep, qinfo, rep, cinfo,
must_validate, &encode_rep, worker->scratchpad)) {
must_validate, &encode_rep, worker->scratchpad,
worker->env.auth_zones)) {
goto bail_out;
}
if(encode_rep != rep)
secure = 0; /* if rewritten, it can't be considered "secure" */
if(encode_rep != rep) {
/* if rewritten, it can't be considered "secure" */
*is_secure_answer = 0;
}
if(!encode_rep || *alias_rrset) {
sldns_buffer_clear(repinfo->c->buffer);
sldns_buffer_flip(repinfo->c->buffer);
if(!encode_rep)
*need_drop = 1;
else {
@ -741,7 +751,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
repinfo->c, worker->scratchpad) ||
!reply_info_answer_encode(qinfo, encode_rep, id, flags,
repinfo->c->buffer, timenow, 1, worker->scratchpad,
udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
udpsize, edns, (int)(edns->bits & EDNS_DO), *is_secure_answer)) {
if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL,
LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad))
edns->opt_list = NULL;
@ -752,26 +762,30 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
* is bad while holding locks. */
rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad,
rep->ref, rep->rrset_count);
if(worker->stats.extended) {
if(secure) worker->stats.ans_secure++;
server_stats_insrcode(&worker->stats, repinfo->c->buffer);
}
/* go and return this buffer to the client */
return 1;
bail_out:
rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count);
return 0;
}
/** Reply to client and perform prefetch to keep cache up to date.
* If the buffer for the reply is empty, it indicates that only prefetch is
* necessary and the reply should be suppressed (because it's dropped or
* being deferred). */
/** Reply to client and perform prefetch to keep cache up to date. */
static void
reply_and_prefetch(struct worker* worker, struct query_info* qinfo,
uint16_t flags, struct comm_reply* repinfo, time_t leeway)
uint16_t flags, struct comm_reply* repinfo, time_t leeway, int noreply)
{
/* first send answer to client to keep its latency
* as small as a cachereply */
if(sldns_buffer_limit(repinfo->c->buffer) != 0)
if(!noreply) {
if(repinfo->c->tcp_req_info) {
sldns_buffer_copy(
repinfo->c->tcp_req_info->spool_buffer,
repinfo->c->buffer);
}
comm_point_send_reply(repinfo);
}
server_stats_prefetch(&worker->stats, worker);
/* create the prefetch in the mesh as a normal lookup without
@ -1082,17 +1096,19 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
struct acl_addr* acladdr;
int rc = 0;
int need_drop = 0;
int is_expired_answer = 0;
int is_secure_answer = 0;
/* We might have to chase a CNAME chain internally, in which case
* we'll have up to two replies and combine them to build a complete
* answer. These variables control this case. */
struct ub_packed_rrset_key* alias_rrset = NULL;
struct reply_info* partial_rep = NULL;
struct query_info* lookup_qinfo = &qinfo;
struct query_info qinfo_tmp; /* placeholdoer for lookup_qinfo */
struct query_info qinfo_tmp; /* placeholder for lookup_qinfo */
struct respip_client_info* cinfo = NULL, cinfo_tmp;
memset(&qinfo, 0, sizeof(qinfo));
if(error != NETEVENT_NOERROR) {
if(error != NETEVENT_NOERROR || !repinfo) {
/* some bad tcp query DNS formats give these error calls */
verbose(VERB_ALGO, "handle request called with err=%d", error);
return 0;
@ -1171,11 +1187,11 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
/* check if this query should be dropped based on source ip rate limiting */
if(!infra_ip_ratelimit_inc(worker->env.infra_cache, repinfo,
*worker->env.now)) {
*worker->env.now, c->buffer)) {
/* See if we are passed through with slip factor */
if(worker->env.cfg->ip_ratelimit_factor != 0 &&
ub_random_max(worker->env.rnd,
worker->env.cfg->ip_ratelimit_factor) == 1) {
worker->env.cfg->ip_ratelimit_factor) == 0) {
char addrbuf[128];
addr_to_str(&repinfo->addr, repinfo->addrlen,
@ -1208,7 +1224,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
if(worker->env.cfg->log_queries) {
char ip[128];
addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip));
log_nametypeclass(0, ip, qinfo.qname, qinfo.qtype, qinfo.qclass);
log_query_in(ip, qinfo.qname, qinfo.qtype, qinfo.qclass);
}
if(qinfo.qtype == LDNS_RR_TYPE_AXFR ||
qinfo.qtype == LDNS_RR_TYPE_IXFR) {
@ -1361,6 +1377,18 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
server_stats_insrcode(&worker->stats, c->buffer);
goto send_reply;
}
if(worker->env.auth_zones &&
rpz_apply_qname_trigger(worker->env.auth_zones,
&worker->env, &qinfo, &edns, c->buffer, worker->scratchpad,
repinfo, acladdr->taglist, acladdr->taglen, &worker->stats)) {
regional_free_all(worker->scratchpad);
if(sldns_buffer_limit(c->buffer) == 0) {
comm_point_drop_reply(repinfo);
return 0;
}
server_stats_insrcode(&worker->stats, c->buffer);
goto send_reply;
}
if(worker->env.auth_zones &&
auth_zones_answer(worker->env.auth_zones, &worker->env,
&qinfo, &edns, repinfo, c->buffer, worker->scratchpad)) {
@ -1431,7 +1459,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
/* If we may apply IP-based actions to the answer, build the client
* information. As this can be expensive, skip it if there is
* absolutely no possibility of it. */
if(worker->daemon->use_response_ip &&
if((worker->daemon->use_response_ip || worker->daemon->use_rpz) &&
(qinfo.qtype == LDNS_RR_TYPE_A ||
qinfo.qtype == LDNS_RR_TYPE_AAAA ||
qinfo.qtype == LDNS_RR_TYPE_ANY)) {
@ -1452,12 +1480,14 @@ lookup_cache:
* each pass. We should still pass the original qinfo to
* answer_from_cache(), however, since it's used to build the reply. */
if(!edns_bypass_cache_stage(edns.opt_list, &worker->env)) {
is_expired_answer = 0;
is_secure_answer = 0;
h = query_info_hash(lookup_qinfo, sldns_buffer_read_u16_at(c->buffer, 2));
if((e=slabhash_lookup(worker->env.msg_cache, h, lookup_qinfo, 0))) {
/* answer from cache - we have acquired a readlock on it */
if(answer_from_cache(worker, &qinfo,
cinfo, &need_drop, &alias_rrset, &partial_rep,
(struct reply_info*)e->data,
cinfo, &need_drop, &is_expired_answer, &is_secure_answer,
&alias_rrset, &partial_rep, (struct reply_info*)e->data,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
&edns)) {
@ -1465,9 +1495,11 @@ lookup_cache:
* Note that if there is more than one pass
* its qname must be that used for cache
* lookup. */
if((worker->env.cfg->prefetch || worker->env.cfg->serve_expired)
&& *worker->env.now >=
((struct reply_info*)e->data)->prefetch_ttl) {
if((worker->env.cfg->prefetch && *worker->env.now >=
((struct reply_info*)e->data)->prefetch_ttl) ||
(worker->env.cfg->serve_expired &&
*worker->env.now >= ((struct reply_info*)e->data)->ttl)) {
time_t leeway = ((struct reply_info*)e->
data)->ttl - *worker->env.now;
if(((struct reply_info*)e->data)->ttl
@ -1476,7 +1508,8 @@ lookup_cache:
lock_rw_unlock(&e->lock);
reply_and_prefetch(worker, lookup_qinfo,
sldns_buffer_read_u16_at(c->buffer, 2),
repinfo, leeway);
repinfo, leeway,
(partial_rep || need_drop));
if(!partial_rep) {
rc = 0;
regional_free_all(worker->scratchpad);
@ -1551,6 +1584,13 @@ send_reply_rc:
comm_point_drop_reply(repinfo);
return 0;
}
if(is_expired_answer) {
worker->stats.ans_expired++;
}
if(worker->stats.extended) {
if(is_secure_answer) worker->stats.ans_secure++;
server_stats_insrcode(&worker->stats, repinfo->c->buffer);
}
#ifdef USE_DNSTAP
if(worker->dtenv.log_client_response_messages)
dt_msg_send_client_response(&worker->dtenv, &repinfo->addr,
@ -1558,9 +1598,19 @@ send_reply_rc:
#endif
if(worker->env.cfg->log_replies)
{
struct timeval tv = {0, 0};
log_reply_info(0, &qinfo, &repinfo->addr, repinfo->addrlen,
tv, 1, c->buffer);
struct timeval tv;
memset(&tv, 0, sizeof(tv));
if(qinfo.local_alias && qinfo.local_alias->rrset &&
qinfo.local_alias->rrset->rk.dname) {
/* log original qname, before the local alias was
* used to resolve that CNAME to something else */
qinfo.qname = qinfo.local_alias->rrset->rk.dname;
log_reply_info(NO_VERBOSE, &qinfo, &repinfo->addr, repinfo->addrlen,
tv, 1, c->buffer);
} else {
log_reply_info(NO_VERBOSE, &qinfo, &repinfo->addr, repinfo->addrlen,
tv, 1, c->buffer);
}
}
#ifdef USE_DNSCRYPT
if(!dnsc_handle_uncurved_request(repinfo)) {
@ -1667,11 +1717,7 @@ worker_create(struct daemon* daemon, int id, int* ports, int n)
return NULL;
}
/* create random state here to avoid locking trouble in RAND_bytes */
seed = (unsigned int)time(NULL) ^ (unsigned int)getpid() ^
(((unsigned int)worker->thread_num)<<17);
/* shift thread_num so it does not match out pid bits */
if(!(worker->rndstate = ub_initstate(seed, daemon->rand))) {
explicit_bzero(&seed, sizeof(seed));
if(!(worker->rndstate = ub_initstate(daemon->rand))) {
log_err("could not init random numbers.");
tube_delete(worker->cmd);
free(worker->ports);
@ -1802,8 +1848,6 @@ worker_init(struct worker* worker, struct config_file *cfg,
alloc_set_id_cleanup(&worker->alloc, &worker_alloc_cleanup, worker);
worker->env = *worker->daemon->env;
comm_base_timept(worker->base, &worker->env.now, &worker->env.now_tv);
if(worker->thread_num == 0)
log_set_time(worker->env.now);
worker->env.worker = worker;
worker->env.worker_base = worker->base;
worker->env.send_query = &worker_send_query;
@ -1822,6 +1866,10 @@ worker_init(struct worker* worker, struct config_file *cfg,
return 0;
}
worker->env.mesh = mesh_create(&worker->daemon->mods, &worker->env);
/* Pass on daemon variables that we would need in the mesh area */
worker->env.mesh->use_response_ip = worker->daemon->use_response_ip;
worker->env.mesh->use_rpz = worker->daemon->use_rpz;
worker->env.detach_subs = &mesh_detach_subs;
worker->env.attach_sub = &mesh_attach_sub;
worker->env.add_sub = &mesh_add_sub;
@ -1909,7 +1957,6 @@ worker_delete(struct worker* worker)
comm_timer_delete(worker->env.probe_timer);
free(worker->ports);
if(worker->thread_num == 0) {
log_set_time(NULL);
#ifdef UB_ON_WINDOWS
wsvc_desetup_worker(worker);
#endif /* UB_ON_WINDOWS */

View File

@ -70,12 +70,9 @@ static const char DEFAULT_DNS64_PREFIX[] = "64:ff9b::/96";
#define MAX_PTR_QNAME_IPV4 30
/**
* Per-query module-specific state. This is usually a dynamically-allocated
* structure, but in our case we only need to store one variable describing the
* state the query is in. So we repurpose the minfo pointer by storing an
* integer in there.
* State of DNS64 processing for a query.
*/
enum dns64_qstate {
enum dns64_state {
DNS64_INTERNAL_QUERY, /**< Internally-generated query, no DNS64
processing. */
DNS64_NEW_QUERY, /**< Query for which we're the first module in
@ -84,6 +81,19 @@ enum dns64_qstate {
for which this sub-query is finished. */
};
/**
* Per-query module-specific state. For the DNS64 module.
*/
struct dns64_qstate {
/** State of the DNS64 module. */
enum dns64_state state;
/** If the dns64 module started with no_cache bool set in the qstate,
* a message to tell it to not modify the cache contents, then this
* is true. The dns64 module is then free to modify that flag for
* its own purposes.
* Otherwise, it is false, the dns64 module was not told to no_cache */
int started_no_cache_store;
};
/******************************************************************************
* *
@ -181,16 +191,19 @@ uitoa(unsigned n, char* s)
*
* \param ipv6 IPv6 address represented as a 128-bit array in big-endian
* order.
* \param ipv6_len length of the ipv6 byte array.
* \param offset Index of the MSB of the IPv4 address embedded in the IPv6
* address.
*/
static uint32_t
extract_ipv4(const uint8_t ipv6[16], const int offset)
extract_ipv4(const uint8_t ipv6[], size_t ipv6_len, const int offset)
{
uint32_t ipv4 = (uint32_t)ipv6[offset/8+0] << (24 + (offset%8))
| (uint32_t)ipv6[offset/8+1] << (16 + (offset%8))
| (uint32_t)ipv6[offset/8+2] << ( 8 + (offset%8))
| (uint32_t)ipv6[offset/8+3] << ( 0 + (offset%8));
uint32_t ipv4;
log_assert(ipv6_len == 16); (void)ipv6_len;
ipv4 = (uint32_t)ipv6[offset/8+0] << (24 + (offset%8))
| (uint32_t)ipv6[offset/8+1] << (16 + (offset%8))
| (uint32_t)ipv6[offset/8+2] << ( 8 + (offset%8))
| (uint32_t)ipv6[offset/8+3] << ( 0 + (offset%8));
if (offset/8+4 < 16)
ipv4 |= (uint32_t)ipv6[offset/8+4] >> (8 - offset%8);
return ipv4;
@ -204,22 +217,26 @@ extract_ipv4(const uint8_t ipv6[16], const int offset)
* \param ipv4 IPv4 address represented as an unsigned 32-bit number.
* \param ptr The result will be written here. Must be large enough, be
* careful!
* \param nm_len length of the ptr buffer.
*
* \return The number of characters written.
*/
static size_t
ipv4_to_ptr(uint32_t ipv4, char ptr[MAX_PTR_QNAME_IPV4])
ipv4_to_ptr(uint32_t ipv4, char ptr[], size_t nm_len)
{
static const char IPV4_PTR_SUFFIX[] = "\07in-addr\04arpa";
int i;
char* c = ptr;
log_assert(nm_len == MAX_PTR_QNAME_IPV4);
for (i = 0; i < 4; ++i) {
*c = uitoa((unsigned int)(ipv4 % 256), c + 1);
c += *c + 1;
log_assert(c < ptr+nm_len);
ipv4 /= 256;
}
log_assert(c + sizeof(IPV4_PTR_SUFFIX) <= ptr+nm_len);
memmove(c, IPV4_PTR_SUFFIX, sizeof(IPV4_PTR_SUFFIX));
return c + sizeof(IPV4_PTR_SUFFIX) - ptr;
@ -231,13 +248,15 @@ ipv4_to_ptr(uint32_t ipv4, char ptr[MAX_PTR_QNAME_IPV4])
*
* \param ptr The domain name. (e.g. "\011[...]\010\012\016\012\03ip6\04arpa")
* \param ipv6 The result will be written here, in network byte order.
* \param ipv6_len length of the ipv6 byte array.
*
* \return 1 on success, 0 on failure.
*/
static int
ptr_to_ipv6(const char* ptr, uint8_t ipv6[16])
ptr_to_ipv6(const char* ptr, uint8_t ipv6[], size_t ipv6_len)
{
int i;
log_assert(ipv6_len == 16); (void)ipv6_len;
for (i = 0; i < 64; i++) {
int x;
@ -265,14 +284,20 @@ ptr_to_ipv6(const char* ptr, uint8_t ipv6[16])
* Synthesize an IPv6 address based on an IPv4 address and the DNS64 prefix.
*
* \param prefix_addr DNS64 prefix address.
* \param prefix_addr_len length of the prefix_addr buffer.
* \param prefix_net CIDR length of the DNS64 prefix. Must be between 0 and 96.
* \param a IPv4 address.
* \param a_len length of the a buffer.
* \param aaaa IPv6 address. The result will be written here.
* \param aaaa_len length of the aaaa buffer.
*/
static void
synthesize_aaaa(const uint8_t prefix_addr[16], int prefix_net,
const uint8_t a[4], uint8_t aaaa[16])
synthesize_aaaa(const uint8_t prefix_addr[], size_t prefix_addr_len,
int prefix_net, const uint8_t a[], size_t a_len, uint8_t aaaa[],
size_t aaaa_len)
{
log_assert(prefix_addr_len == 16 && a_len == 4 && aaaa_len == 16);
(void)prefix_addr_len; (void)a_len; (void)aaaa_len;
memcpy(aaaa, prefix_addr, 16);
aaaa[prefix_net/8+0] |= a[0] >> (0+prefix_net%8);
aaaa[prefix_net/8+1] |= a[0] << (8-prefix_net%8);
@ -437,7 +462,8 @@ handle_ipv6_ptr(struct module_qstate* qstate, int id)
/* Convert the PTR query string to an IPv6 address. */
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
if (!ptr_to_ipv6((char*)qstate->qinfo.qname, sin6.sin6_addr.s6_addr))
if (!ptr_to_ipv6((char*)qstate->qinfo.qname, sin6.sin6_addr.s6_addr,
sizeof(sin6.sin6_addr.s6_addr)))
return module_wait_module; /* Let other module handle this. */
/*
@ -460,7 +486,8 @@ handle_ipv6_ptr(struct module_qstate* qstate, int id)
if (!(qinfo.qname = regional_alloc(qstate->region, MAX_PTR_QNAME_IPV4)))
return module_error;
qinfo.qname_len = ipv4_to_ptr(extract_ipv4(sin6.sin6_addr.s6_addr,
dns64_env->prefix_net), (char*)qinfo.qname);
sizeof(sin6.sin6_addr.s6_addr), dns64_env->prefix_net),
(char*)qinfo.qname, MAX_PTR_QNAME_IPV4);
/* Create the new sub-query. */
fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
@ -470,7 +497,7 @@ handle_ipv6_ptr(struct module_qstate* qstate, int id)
if (subq) {
subq->curmod = id;
subq->ext_state[id] = module_state_initial;
subq->minfo[id] = NULL;
subq->minfo[id] = NULL;
}
return module_wait_subquery;
@ -540,7 +567,8 @@ dns64_always_synth_for_qname(struct module_qstate* qstate, int id)
static enum module_ext_state
handle_event_pass(struct module_qstate* qstate, int id)
{
if ((uintptr_t)qstate->minfo[id] == DNS64_NEW_QUERY
struct dns64_qstate* iq = (struct dns64_qstate*)qstate->minfo[id];
if (iq && iq->state == DNS64_NEW_QUERY
&& qstate->qinfo.qtype == LDNS_RR_TYPE_PTR
&& qstate->qinfo.qname_len == 74
&& !strcmp((char*)&qstate->qinfo.qname[64], "\03ip6\04arpa"))
@ -548,12 +576,12 @@ handle_event_pass(struct module_qstate* qstate, int id)
return handle_ipv6_ptr(qstate, id);
if (qstate->env->cfg->dns64_synthall &&
(uintptr_t)qstate->minfo[id] == DNS64_NEW_QUERY
iq && iq->state == DNS64_NEW_QUERY
&& qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA)
return generate_type_A_query(qstate, id);
if(dns64_always_synth_for_qname(qstate, id) &&
(uintptr_t)qstate->minfo[id] == DNS64_NEW_QUERY
iq && iq->state == DNS64_NEW_QUERY
&& !(qstate->query_flags & BIT_CD)
&& qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) {
verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway");
@ -561,7 +589,7 @@ handle_event_pass(struct module_qstate* qstate, int id)
}
/* We are finished when our sub-query is finished. */
if ((uintptr_t)qstate->minfo[id] == DNS64_SUBQUERY_FINISHED)
if (iq && iq->state == DNS64_SUBQUERY_FINISHED)
return module_finished;
/* Otherwise, pass request to next module. */
@ -582,6 +610,7 @@ handle_event_pass(struct module_qstate* qstate, int id)
static enum module_ext_state
handle_event_moddone(struct module_qstate* qstate, int id)
{
struct dns64_qstate* iq = (struct dns64_qstate*)qstate->minfo[id];
/*
* In many cases we have nothing special to do. From most to least common:
*
@ -593,7 +622,7 @@ handle_event_moddone(struct module_qstate* qstate, int id)
* synthesize in (sec 5.1.2 of RFC6147).
* - A successful AAAA query with an answer.
*/
if((enum dns64_qstate)qstate->minfo[id] != DNS64_INTERNAL_QUERY
if((!iq || iq->state != DNS64_INTERNAL_QUERY)
&& qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA
&& !(qstate->query_flags & BIT_CD)
&& !(qstate->return_msg &&
@ -604,7 +633,7 @@ handle_event_moddone(struct module_qstate* qstate, int id)
* So, this is a AAAA noerror/nodata answer */
return generate_type_A_query(qstate, id);
if((enum dns64_qstate)qstate->minfo[id] != DNS64_INTERNAL_QUERY
if((!iq || iq->state != DNS64_INTERNAL_QUERY)
&& qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA
&& !(qstate->query_flags & BIT_CD)
&& dns64_always_synth_for_qname(qstate, id)) {
@ -614,6 +643,13 @@ handle_event_moddone(struct module_qstate* qstate, int id)
return generate_type_A_query(qstate, id);
}
/* Store the response in cache. */
if ( (!iq || !iq->started_no_cache_store) &&
qstate->return_msg && qstate->return_msg->rep &&
!dns_cache_store(qstate->env, &qstate->qinfo, qstate->return_msg->rep,
0, 0, 0, NULL, qstate->query_flags))
log_err("out of memory");
/* do nothing */
return module_finished;
}
@ -634,6 +670,7 @@ void
dns64_operate(struct module_qstate* qstate, enum module_ev event, int id,
struct outbound_entry* outbound)
{
struct dns64_qstate* iq;
(void)outbound;
verbose(VERB_QUERY, "dns64[module %d] operate: extstate:%s event:%s",
id, strextstate(qstate->ext_state[id]),
@ -643,7 +680,12 @@ dns64_operate(struct module_qstate* qstate, enum module_ev event, int id,
switch(event) {
case module_event_new:
/* Tag this query as being new and fall through. */
qstate->minfo[id] = (void*)DNS64_NEW_QUERY;
iq = (struct dns64_qstate*)regional_alloc(
qstate->region, sizeof(*iq));
qstate->minfo[id] = iq;
iq->state = DNS64_NEW_QUERY;
iq->started_no_cache_store = qstate->no_cache_store;
qstate->no_cache_store = 1;
/* fallthrough */
case module_event_pass:
qstate->ext_state[id] = handle_event_pass(qstate, id);
@ -655,6 +697,11 @@ dns64_operate(struct module_qstate* qstate, enum module_ev event, int id,
qstate->ext_state[id] = module_finished;
break;
}
if(qstate->ext_state[id] == module_finished) {
iq = (struct dns64_qstate*)qstate->minfo[id];
if(iq && iq->state != DNS64_INTERNAL_QUERY)
qstate->no_cache_store = iq->started_no_cache_store;
}
}
static void
@ -710,8 +757,10 @@ dns64_synth_aaaa_data(const struct ub_packed_rrset_key* fk,
dd->rr_data[i][1] = 16;
synthesize_aaaa(
((struct sockaddr_in6*)&dns64_env->prefix_addr)->sin6_addr.s6_addr,
sizeof(((struct sockaddr_in6*)&dns64_env->prefix_addr)->sin6_addr.s6_addr),
dns64_env->prefix_net, &fd->rr_data[i][2],
&dd->rr_data[i][2] );
fd->rr_len[i]-2, &dd->rr_data[i][2],
dd->rr_len[i]-2);
dd->rr_ttl[i] = fd->rr_ttl[i];
}
@ -867,9 +916,10 @@ dns64_adjust_ptr(struct module_qstate* qstate, struct module_qstate* super)
* initial query's domain name.
*/
answer = reply_find_answer_rrset(&qstate->qinfo, super->return_msg->rep);
log_assert(answer);
answer->rk.dname = super->qinfo.qname;
answer->rk.dname_len = super->qinfo.qname_len;
if(answer) {
answer->rk.dname = super->qinfo.qname;
answer->rk.dname_len = super->qinfo.qname_len;
}
}
/**
@ -885,6 +935,7 @@ void
dns64_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super)
{
struct dns64_qstate* super_dq = (struct dns64_qstate*)super->minfo[id];
log_query_info(VERB_ALGO, "dns64: inform_super, sub is",
&qstate->qinfo);
log_query_info(VERB_ALGO, "super is", &super->qinfo);
@ -893,15 +944,27 @@ dns64_inform_super(struct module_qstate* qstate, int id,
* Signal that the sub-query is finished, no matter whether we are
* successful or not. This lets the state machine terminate.
*/
super->minfo[id] = (void*)DNS64_SUBQUERY_FINISHED;
if(!super_dq) {
super_dq = (struct dns64_qstate*)regional_alloc(super->region,
sizeof(*super_dq));
if(!super_dq) {
log_err("out of memory");
super->return_rcode = LDNS_RCODE_SERVFAIL;
super->return_msg = NULL;
return;
}
super->minfo[id] = super_dq;
memset(super_dq, 0, sizeof(*super_dq));
super_dq->started_no_cache_store = super->no_cache_store;
}
super_dq->state = DNS64_SUBQUERY_FINISHED;
/* If there is no successful answer, we're done. */
if (qstate->return_rcode != LDNS_RCODE_NOERROR
|| !qstate->return_msg
|| !qstate->return_msg->rep
|| !reply_find_answer_rrset(&qstate->qinfo,
qstate->return_msg->rep))
|| !qstate->return_msg->rep) {
return;
}
/* Use return code from A query in response to client. */
if (super->return_rcode != LDNS_RCODE_NOERROR)
@ -916,7 +979,7 @@ dns64_inform_super(struct module_qstate* qstate, int id,
}
/* Store the generated response in cache. */
if (!super->no_cache_store &&
if ( (!super_dq || !super_dq->started_no_cache_store) &&
!dns_cache_store(super->env, &super->qinfo, super->return_msg->rep,
0, 0, 0, NULL, super->query_flags))
log_err("out of memory");

View File

@ -316,15 +316,15 @@ dnscrypt_server_uncurve(struct dnsc_env* env,
#else
return -1;
#endif
} else {
if (crypto_box_beforenm(nmkey,
query_header->publickey,
cert->keypair->crypt_secretkey) != 0) {
return -1;
}
}
// Cache the shared secret we just computed.
dnsc_shared_secret_cache_insert(env->shared_secrets_cache,
} else {
if (crypto_box_beforenm(nmkey,
query_header->publickey,
cert->keypair->crypt_secretkey) != 0) {
return -1;
}
}
// Cache the shared secret we just computed.
dnsc_shared_secret_cache_insert(env->shared_secrets_cache,
key,
hash,
nmkey);
@ -442,20 +442,7 @@ dnscrypt_hrtime(void)
static void
add_server_nonce(uint8_t *nonce)
{
uint64_t ts;
uint64_t tsn;
uint32_t suffix;
ts = dnscrypt_hrtime();
// TODO? dnscrypt-wrapper does some logic with context->nonce_ts_last
// unclear if we really need it, so skipping it for now.
tsn = (ts << 10) | (randombytes_random() & 0x3ff);
#if (BYTE_ORDER == LITTLE_ENDIAN)
tsn =
(((uint64_t)htonl((uint32_t)tsn)) << 32) | htonl((uint32_t)(tsn >> 32));
#endif
memcpy(nonce + crypto_box_HALF_NONCEBYTES, &tsn, 8);
suffix = randombytes_random();
memcpy(nonce + crypto_box_HALF_NONCEBYTES + 8, &suffix, 4);
randombytes_buf(nonce + crypto_box_HALF_NONCEBYTES, 8/*tsn*/+4/*suffix*/);
}
/**
@ -732,6 +719,11 @@ dnsc_load_local_data(struct dnsc_env* dnscenv, struct config_file *cfg)
);
continue;
}
if((unsigned)strlen(dnscenv->provider_name) >= (unsigned)0xffff0000) {
/* guard against integer overflow in rrlen calculation */
verbose(VERB_OPS, "cert #%" PRIu32 " is too long", serial);
continue;
}
rrlen = strlen(dnscenv->provider_name) +
strlen(ttl_class_type) +
4 * sizeof(struct SignedCert) + // worst case scenario
@ -746,9 +738,9 @@ dnsc_load_local_data(struct dnsc_env* dnscenv, struct config_file *cfg)
for(j=0; j<sizeof(struct SignedCert); j++) {
int c = (int)*((const uint8_t *) cert + j);
if (isprint(c) && c != '"' && c != '\\') {
snprintf(rr + strlen(rr), rrlen - 1 - strlen(rr), "%c", c);
snprintf(rr + strlen(rr), rrlen - strlen(rr), "%c", c);
} else {
snprintf(rr + strlen(rr), rrlen - 1 - strlen(rr), "\\%03d", c);
snprintf(rr + strlen(rr), rrlen - strlen(rr), "\\%03d", c);
}
}
verbose(VERB_OPS,
@ -757,7 +749,7 @@ dnsc_load_local_data(struct dnsc_env* dnscenv, struct config_file *cfg)
" to local-data to config: %s",
serial, rr
);
snprintf(rr + strlen(rr), rrlen - 1 - strlen(rr), "\"");
snprintf(rr + strlen(rr), rrlen - strlen(rr), "\"");
cfg_strlist_insert(&cfg->local_data, strdup(rr));
free(rr);
}
@ -772,12 +764,13 @@ key_get_es_version(uint8_t version[2])
const char *name;
};
const int num_versions = 2;
struct es_version es_versions[] = {
{{0x00, 0x01}, "X25519-XSalsa20Poly1305"},
{{0x00, 0x02}, "X25519-XChacha20Poly1305"},
};
int i;
for(i=0; i < (int)sizeof(es_versions); i++){
for(i=0; i < num_versions; i++){
if(es_versions[i].es_version[0] == version[0] &&
es_versions[i].es_version[1] == version[1]){
return es_versions[i].name;
@ -876,7 +869,7 @@ sodium_misuse_handler(void)
fatal_exit(
"dnscrypt: libsodium could not be initialized, this typically"
" happens when no good source of entropy is found. If you run"
" unbound in a chroot, make sure /dev/random is available. See"
" unbound in a chroot, make sure /dev/urandom is available. See"
" https://www.unbound.net/documentation/unbound.conf.html");
}

View File

@ -39,6 +39,10 @@
#include "config.h"
#include <string.h>
#include <sys/time.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <errno.h>
#include "sldns/sbuffer.h"
#include "util/config_file.h"
#include "util/net_help.h"
@ -118,6 +122,18 @@ dt_msg_init(const struct dt_env *env,
}
}
/* check that the socket file can be opened and exists, print error if not */
static void
check_socket_file(const char* socket_path)
{
struct stat statbuf;
memset(&statbuf, 0, sizeof(statbuf));
if(stat(socket_path, &statbuf) < 0) {
log_warn("could not open dnstap-socket-path: %s, %s",
socket_path, strerror(errno));
}
}
struct dt_env *
dt_create(const char *socket_path, unsigned num_workers)
{
@ -134,6 +150,7 @@ dt_create(const char *socket_path, unsigned num_workers)
socket_path);
log_assert(socket_path != NULL);
log_assert(num_workers > 0);
check_socket_file(socket_path);
env = (struct dt_env *) calloc(1, sizeof(struct dt_env));
if (!env)

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
README for Unbound 1.8.1
README for Unbound 1.10.1
Copyright 2007 NLnet Labs
http://unbound.net
@ -99,7 +99,7 @@ o If you are not receiving the correct source IP address on replies (e.g.
the config file is an alternative. The interface-automatic option uses
non portable socket options, Linux and FreeBSD should work fine.
o The warning 'openssl has no entropy, seeding with time', with chroot
enabled, may be solved with a symbolic link to /dev/random from <chrootdir>.
enabled, may be solved with a symbolic link to /dev/urandom from <chrootdir>.
o On Solaris 5.10 some libtool packages from repositories do not work with
gcc, showing errors gcc: unrecognized option `-KPIC'
To solve this do ./configure libtool=./libtool [your options...].

View File

@ -0,0 +1,65 @@
## Created a module to support the ipset that could add the domain's ip to a list easily.
### Purposes:
* In my case, I can't access the facebook, twitter, youtube and thousands web site for some reason. VPN is a solution. But the internet too slow whether all traffics pass through the vpn.
So, I set up a transparent proxy to proxy the traffic which has been blocked only.
At the final step, I need to install a dns service which would work with ipset well to launch the system.
I did some research for this. Unfortunately, Unbound, My favorite dns service doesn't support ipset yet. So, I decided to implement it by my self and contribute the patch. It's good for me and the community.
```
# unbound.conf
server:
...
local-zone: "facebook.com" ipset
local-zone: "twitter.com" ipset
local-zone: "instagram.com" ipset
more social website
ipset:
name-v4: "gfwlist"
```
```
# iptables
iptables -A PREROUTING -p tcp -m set --match-set gfwlist dst -j REDIRECT --to-ports 10800
iptables -A OUTPUT -p tcp -m set --match-set gfwlist dst -j REDIRECT --to-ports 10800
```
* This patch could work with iptables rules to batch block the IPs.
```
# unbound.conf
server:
...
local-zone: "facebook.com" ipset
local-zone: "twitter.com" ipset
local-zone: "instagram.com" ipset
more social website
ipset:
name-v4: "blacklist"
name-v6: "blacklist6"
```
```
# iptables
iptables -A INPUT -m set --set blacklist src -j DROP
ip6tables -A INPUT -m set --set blacklist6 src -j DROP
```
### Notes:
* To enable this module the root privileges is required.
* Please create a set with ipset command first. eg. **ipset -N blacklist iphash**
### How to use:
```
./configure --enable-ipset
make && make install
```
### Configuration:
```
# unbound.conf
server:
...
local-zone: "example.com" ipset
ipset:
name-v4: "blacklist"
```

View File

@ -29,7 +29,7 @@ o support OPT record placement on recv anywhere in the additional section.
o add local-file: config with authority features.
o (option) to make local-data answers be secure for libunbound (default=no)
o (option) to make chroot: copy all needed files into jail (or make jail)
perhaps also print reminder to link /dev/random and sysloghack.
perhaps also print reminder to link /dev/urandom and sysloghack.
o overhaul outside-network servicedquery to merge with udpwait and tcpwait,
to make timers in servicedquery independent of udpwait queues.
o check into rebinding ports for efficiency, configure time test.

View File

@ -1,7 +1,7 @@
#
# Example configuration file.
#
# See unbound.conf(5) man page, version 1.8.1.
# See unbound.conf(5) man page, version 1.9.2.
#
# this is a comment.
@ -103,6 +103,7 @@ server:
# so-sndbuf: 0
# use SO_REUSEPORT to distribute queries over threads.
# at extreme load it could be better to turn it off to distribute even.
# so-reuseport: yes
# use IP_TRANSPARENT so the interface: addresses can be non-local
@ -123,6 +124,9 @@ server:
# Suggested values are 512 to 4096. Default is 4096. 65536 disables it.
# max-udp-size: 4096
# max memory to use for stream(tcp and tls) waiting result buffers.
# stream-wait-size: 4m
# buffer size for handling DNS data. No messages larger than this
# size can be sent or received, by UDP or TCP. In bytes.
# msg-buffer-size: 65552
@ -145,6 +149,10 @@ server:
# msec to wait before close of port on timeout UDP. 0 disables.
# delay-close: 0
# msec for waiting for an unknown server to reply. Increase if you
# are behind a slow satellite link, to eg. 1128.
# unknown-server-time-limit: 376
# 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
@ -318,6 +326,10 @@ server:
# timetoresolve, fromcache and responsesize.
# log-replies: no
# log with tag 'query' and 'reply' instead of 'info' for
# filtering log-queries and log-replies from the log.
# log-tag-queryreply: no
# log the local-zone actions, like local-zone type inform is enabled
# also for the other local zone types.
# log-local-actions: no
@ -449,6 +461,9 @@ server:
# if yes, perform key lookups adjacent to normal lookups.
# prefetch-key: no
# deny queries of type ANY with an empty response.
# deny-any: no
# if yes, Unbound rotates RRSet order in response.
# rrset-roundrobin: no
@ -461,6 +476,9 @@ server:
# module configuration of the server. A string with identifiers
# separated by spaces. Syntax: "[dns64] [validator] iterator"
# most modules have to be listed at the beginning of the line,
# except cachedb(just before iterator), and python (at the beginning,
# or, just before the iterator).
# module-config: "validator iterator"
# File with trusted keys, kept uptodate using RFC5011 probes,
@ -475,7 +493,7 @@ server:
# trust anchor signaling sends a RFC8145 key tag query after priming.
# trust-anchor-signaling: yes
# Root key trust anchor sentinel (draft-ietf-dnsop-kskroll-sentinel)
# root-key-sentinel: yes
@ -659,6 +677,7 @@ server:
# o typetransparent resolves normally for other types and other names
# o inform acts like transparent, but logs client IP address
# o inform_deny drops queries and logs client IP address
# o inform_redirect redirects queries and logs client IP address
# o always_transparent, always_refuse, always_nxdomain, resolve in
# that way but ignore local data for that name
# o noview breaks out of that view towards global local-zones.
@ -701,6 +720,19 @@ server:
# tls-service-pem: "path/to/publiccertfile.pem"
# tls-port: 853
# cipher setting for TLSv1.2
# tls-ciphers: "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256"
# cipher setting for TLSv1.3
# tls-ciphersuites: "TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"
# Add the secret file for TLS Session Ticket.
# Secret file must be 80 bytes of random data.
# First key use to encrypt and decrypt TLS session tickets.
# Other keys use to decrypt only.
# requires restart to take effect.
# tls-session-ticket-keys: "path/to/secret_file1"
# tls-session-ticket-keys: "path/to/secret_file2"
# request upstream over TLS (with plain DNS inside the TLS stream).
# Default is no. Can be turned on and off with unbound-control.
# tls-upstream: no
@ -757,11 +789,11 @@ server:
# Limit the number of connections simultaneous from a netblock
# tcp-connection-limit: 192.0.2.0/24 12
# what is considered a low rtt (ping time for upstream server), in msec
# low-rtt: 45
# select low rtt this many times out of 1000. 0 means the fast server
# select is disabled. prefetches are not sped up.
# low-rtt-permil: 0
# select from the fastest servers this many times out of 1000. 0 means
# the fast server select is disabled. prefetches are not sped up.
# fast-server-permil: 0
# the number of servers that will be used in the fast server selection.
# fast-server-num: 3
# Specific options for ipsecmod. unbound needs to be configured with
# --enable-ipsecmod for these to take effect.
@ -795,6 +827,8 @@ server:
# Python config section. To enable:
# o use --with-pythonmodule to configure before compiling.
# o list python in the module-config string (above) to enable.
# It can be at the start, it gets validated results, or just before
# the iterator and process before DNSSEC validation.
# o and give a python-script to run.
python:
# Script file to load
@ -879,15 +913,25 @@ remote-control:
# notifies.
# auth-zone:
# name: "."
# master: 199.9.14.201 # b.root-servers.net
# master: 192.33.4.12 # c.root-servers.net
# master: 199.7.91.13 # d.root-servers.net
# master: 192.5.5.241 # f.root-servers.net
# master: 192.112.36.4 # g.root-servers.net
# master: 193.0.14.129 # k.root-servers.net
# master: 192.0.47.132 # xfr.cjr.dns.icann.org
# master: 192.0.32.132 # xfr.lax.dns.icann.org
# master: 2001:500:200::b # b.root-servers.net
# master: 2001:500:2::c # c.root-servers.net
# master: 2001:500:2d::d # d.root-servers.net
# master: 2001:500:2f::f # f.root-servers.net
# master: 2001:500:12::d0d # g.root-servers.net
# master: 2001:7fd::1 # k.root-servers.net
# master: 2620:0:2830:202::132 # xfr.cjr.dns.icann.org
# master: 2620:0:2d0:202::132 # xfr.lax.dns.icann.org
# fallback-enabled: yes
# for-downstream: no
# for-upstream: yes
# fallback-enabled: yes
# master: b.root-servers.net
# master: c.root-servers.net
# master: e.root-servers.net
# master: f.root-servers.net
# master: g.root-servers.net
# master: k.root-servers.net
# auth-zone:
# name: "example.org"
# for-downstream: yes
@ -935,7 +979,7 @@ remote-control:
# Enable external backend DB as auxiliary cache. Specify the backend name
# (default is "testframe", which has no use other than for debugging and
# testing) and backend-specific options. The 'cachedb' module must be
# included in module-config.
# included in module-config, just before the iterator module.
# cachedb:
# backend: "testframe"
# # secret seed string to calculate hashed keys

View File

@ -1,7 +1,7 @@
#
# Example configuration file.
#
# See unbound.conf(5) man page, version 1.8.1.
# See unbound.conf(5) man page, version 1.10.1.
#
# this is a comment.
@ -103,6 +103,7 @@ server:
# so-sndbuf: 0
# use SO_REUSEPORT to distribute queries over threads.
# at extreme load it could be better to turn it off to distribute even.
# so-reuseport: yes
# use IP_TRANSPARENT so the interface: addresses can be non-local
@ -123,6 +124,9 @@ server:
# Suggested values are 512 to 4096. Default is 4096. 65536 disables it.
# max-udp-size: 4096
# max memory to use for stream(tcp and tls) waiting result buffers.
# stream-wait-size: 4m
# buffer size for handling DNS data. No messages larger than this
# size can be sent or received, by UDP or TCP. In bytes.
# msg-buffer-size: 65552
@ -145,6 +149,10 @@ server:
# msec to wait before close of port on timeout UDP. 0 disables.
# delay-close: 0
# msec for waiting for an unknown server to reply. Increase if you
# are behind a slow satellite link, to eg. 1128.
# unknown-server-time-limit: 376
# 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
@ -278,7 +286,7 @@ server:
# The pid file can be absolute and outside of the chroot, it is
# written just prior to performing the chroot and dropping permissions.
#
# Additionally, unbound may need to access /dev/random (for entropy).
# Additionally, unbound may need to access /dev/urandom (for entropy).
# How to do this is specific to your OS.
#
# If you give "" no chroot is performed. The path must not end in a /.
@ -318,6 +326,10 @@ server:
# timetoresolve, fromcache and responsesize.
# log-replies: no
# log with tag 'query' and 'reply' instead of 'info' for
# filtering log-queries and log-replies from the log.
# log-tag-queryreply: no
# log the local-zone actions, like local-zone type inform is enabled
# also for the other local zone types.
# log-local-actions: no
@ -449,6 +461,9 @@ server:
# if yes, perform key lookups adjacent to normal lookups.
# prefetch-key: no
# deny queries of type ANY with an empty response.
# deny-any: no
# if yes, Unbound rotates RRSet order in response.
# rrset-roundrobin: no
@ -461,6 +476,9 @@ server:
# module configuration of the server. A string with identifiers
# separated by spaces. Syntax: "[dns64] [validator] iterator"
# most modules have to be listed at the beginning of the line,
# except cachedb(just before iterator), and python (at the beginning,
# or, just before the iterator).
# module-config: "validator iterator"
# File with trusted keys, kept uptodate using RFC5011 probes,
@ -475,7 +493,7 @@ server:
# trust anchor signaling sends a RFC8145 key tag query after priming.
# trust-anchor-signaling: yes
# Root key trust anchor sentinel (draft-ietf-dnsop-kskroll-sentinel)
# root-key-sentinel: yes
@ -540,8 +558,8 @@ server:
# that set CD but cannot validate themselves.
# ignore-cd-flag: no
# Serve expired responses from cache, with TTL 0 in the response,
# and then attempt to fetch the data afresh.
# Serve expired responses from cache, with serve-expired-reply-ttl in
# the response, and then attempt to fetch the data afresh.
# serve-expired: no
#
# Limit serving of expired responses to configured seconds after
@ -553,6 +571,16 @@ server:
# that the expired records will be served as long as there are queries
# for it.
# serve-expired-ttl-reset: no
#
# TTL value to use when replying with expired data.
# serve-expired-reply-ttl: 30
#
# Time in milliseconds before replying to the client with expired data.
# This essentially enables the serve-stale behavior as specified in
# draft-ietf-dnsop-serve-stale-10 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
# Have the validator log failed validations for your diagnosis.
# 0: off. 1: A line per failed user query. 2: With reason and bad IP.
@ -636,6 +664,9 @@ server:
# local-zone: "8.b.d.0.1.0.0.2.ip6.arpa." nodefault
# And for 64.100.in-addr.arpa. to 127.100.in-addr.arpa.
# Add example.com into ipset
# local-zone: "example.com" ipset
# If unbound is running service for the local host then it is useful
# to perform lan-wide lookups to the upstream, and unblock the
# long list of local-zones above. If this unbound is a dns server
@ -659,6 +690,7 @@ server:
# o typetransparent resolves normally for other types and other names
# o inform acts like transparent, but logs client IP address
# o inform_deny drops queries and logs client IP address
# o inform_redirect redirects queries and logs client IP address
# o always_transparent, always_refuse, always_nxdomain, resolve in
# that way but ignore local data for that name
# o noview breaks out of that view towards global local-zones.
@ -701,6 +733,19 @@ server:
# tls-service-pem: "path/to/publiccertfile.pem"
# tls-port: 853
# cipher setting for TLSv1.2
# tls-ciphers: "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256"
# cipher setting for TLSv1.3
# tls-ciphersuites: "TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"
# Add the secret file for TLS Session Ticket.
# Secret file must be 80 bytes of random data.
# First key use to encrypt and decrypt TLS session tickets.
# Other keys use to decrypt only.
# requires restart to take effect.
# tls-session-ticket-keys: "path/to/secret_file1"
# tls-session-ticket-keys: "path/to/secret_file2"
# request upstream over TLS (with plain DNS inside the TLS stream).
# Default is no. Can be turned on and off with unbound-control.
# tls-upstream: no
@ -757,11 +802,11 @@ server:
# Limit the number of connections simultaneous from a netblock
# tcp-connection-limit: 192.0.2.0/24 12
# what is considered a low rtt (ping time for upstream server), in msec
# low-rtt: 45
# select low rtt this many times out of 1000. 0 means the fast server
# select is disabled. prefetches are not sped up.
# low-rtt-permil: 0
# select from the fastest servers this many times out of 1000. 0 means
# the fast server select is disabled. prefetches are not sped up.
# fast-server-permil: 0
# the number of servers that will be used in the fast server selection.
# fast-server-num: 3
# Specific options for ipsecmod. unbound needs to be configured with
# --enable-ipsecmod for these to take effect.
@ -795,6 +840,8 @@ server:
# Python config section. To enable:
# o use --with-pythonmodule to configure before compiling.
# o list python in the module-config string (above) to enable.
# It can be at the start, it gets validated results, or just before
# the iterator and process before DNSSEC validation.
# o and give a python-script to run.
python:
# Script file to load
@ -879,15 +926,25 @@ remote-control:
# notifies.
# auth-zone:
# name: "."
# master: 199.9.14.201 # b.root-servers.net
# master: 192.33.4.12 # c.root-servers.net
# master: 199.7.91.13 # d.root-servers.net
# master: 192.5.5.241 # f.root-servers.net
# master: 192.112.36.4 # g.root-servers.net
# master: 193.0.14.129 # k.root-servers.net
# master: 192.0.47.132 # xfr.cjr.dns.icann.org
# master: 192.0.32.132 # xfr.lax.dns.icann.org
# master: 2001:500:200::b # b.root-servers.net
# master: 2001:500:2::c # c.root-servers.net
# master: 2001:500:2d::d # d.root-servers.net
# master: 2001:500:2f::f # f.root-servers.net
# master: 2001:500:12::d0d # g.root-servers.net
# master: 2001:7fd::1 # k.root-servers.net
# master: 2620:0:2830:202::132 # xfr.cjr.dns.icann.org
# master: 2620:0:2d0:202::132 # xfr.lax.dns.icann.org
# fallback-enabled: yes
# for-downstream: no
# for-upstream: yes
# fallback-enabled: yes
# master: b.root-servers.net
# master: c.root-servers.net
# master: e.root-servers.net
# master: f.root-servers.net
# master: g.root-servers.net
# master: k.root-servers.net
# auth-zone:
# name: "example.org"
# for-downstream: yes
@ -935,7 +992,7 @@ remote-control:
# Enable external backend DB as auxiliary cache. Specify the backend name
# (default is "testframe", which has no use other than for debugging and
# testing) and backend-specific options. The 'cachedb' module must be
# included in module-config.
# included in module-config, just before the iterator module.
# cachedb:
# backend: "testframe"
# # secret seed string to calculate hashed keys
@ -948,3 +1005,31 @@ remote-control:
# redis-server-port: 6379
# # timeout (in ms) for communication with the redis server
# redis-timeout: 100
# IPSet
# Add specify domain into set via ipset.
# Note: To enable ipset needs run unbound as root user.
# ipset:
# # set name for ip v4 addresses
# name-v4: "list-v4"
# # set name for ip v6 addresses
# name-v6: "list-v6"
#
# Response Policy Zones
# RPZ policies. Applied in order of configuration. QNAME and Response IP
# Address trigger are the only supported triggers. Supported actions are:
# NXDOMAIN, NODATA, PASSTHRU, DROP and Local Data. Policies can be loaded from
# file, using zone transfer, or using HTTP. The respip module needs to be added
# to the module-config, e.g.: module-config: "respip validator iterator".
# rpz:
# name: "rpz.example.com"
# zonefile: "rpz.example.com"
# master: 192.0.2.0
# allow-notify: 192.0.2.0/32
# url: http://www.example.com/rpz.example.org.zone
# rpz-action-override: cname
# rpz-cname-override: www.example.org
# rpz-log: yes
# rpz-log-name: "example policy"
# tags: "example"

View File

@ -1,4 +1,4 @@
.TH "libunbound" "3" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "libunbound" "3" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2"
.\"
.\" libunbound.3 -- unbound library functions manual
.\"
@ -20,6 +20,7 @@
.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,
@ -43,7 +44,7 @@
.B ub_ctx_zone_remove,
.B ub_ctx_data_add,
.B ub_ctx_data_remove
\- Unbound DNS validating resolver 1.8.1 functions.
\- Unbound DNS validating resolver 1.9.2 functions.
.SH "SYNOPSIS"
.B #include <unbound.h>
.LP
@ -72,6 +73,9 @@
\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
@ -227,6 +231,12 @@ 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.
.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

View File

@ -1,4 +1,4 @@
.TH "libunbound" "3" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "libunbound" "3" "May 19, 2020" "NLnet Labs" "unbound 1.10.1"
.\"
.\" libunbound.3 -- unbound library functions manual
.\"
@ -20,6 +20,7 @@
.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,
@ -43,7 +44,7 @@
.B ub_ctx_zone_remove,
.B ub_ctx_data_add,
.B ub_ctx_data_remove
\- Unbound DNS validating resolver 1.8.1 functions.
\- Unbound DNS validating resolver 1.10.1 functions.
.SH "SYNOPSIS"
.B #include <unbound.h>
.LP
@ -72,6 +73,9 @@
\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
@ -227,6 +231,12 @@ 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.
.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
@ -386,12 +396,13 @@ The result of the DNS resolution and validation is returned as
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 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 */
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 */
};
.fi

View File

@ -1,4 +1,4 @@
.TH "unbound-anchor" "8" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "unbound-anchor" "8" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2"
.\"
.\" unbound-anchor.8 -- unbound anchor maintenance utility manual
.\"

View File

@ -1,4 +1,4 @@
.TH "unbound-anchor" "8" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "unbound-anchor" "8" "May 19, 2020" "NLnet Labs" "unbound 1.10.1"
.\"
.\" unbound-anchor.8 -- unbound anchor maintenance utility manual
.\"
@ -69,6 +69,10 @@ 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.
You can pass an IPv4 address or IPv6 address (no brackets) if you want.
.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).
.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.

View File

@ -1,4 +1,4 @@
.TH "unbound-checkconf" "8" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "unbound-checkconf" "8" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2"
.\"
.\" unbound-checkconf.8 -- unbound configuration checker manual
.\"

View File

@ -1,4 +1,4 @@
.TH "unbound-checkconf" "8" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "unbound-checkconf" "8" "May 19, 2020" "NLnet Labs" "unbound 1.10.1"
.\"
.\" unbound-checkconf.8 -- unbound configuration checker manual
.\"

View File

@ -1,4 +1,4 @@
.TH "unbound-control" "8" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "unbound-control" "8" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2"
.\"
.\" unbound-control.8 -- unbound remote control manual
.\"
@ -322,6 +322,9 @@ serial check). And then the zone is transferred for a newer zone version.
.TP
.B view_local_data_remove \fIview\fR \fIname
\fIlocal_data_remove\fR for given view.
.TP
.B view_local_datas \fIview\fR
Add a list of \fIlocal_data\fR for given view from stdin. Like local_datas.
.SH "EXIT CODE"
The unbound\-control program exits with status code 1 on error, 0 on success.
.SH "SET UP"
@ -496,6 +499,10 @@ Memory in bytes in use by the iterator module.
Memory in bytes in use by the validator module. Includes the key cache and
negative cache.
.TP
.I mem.streamwait
Memory in bytes in used by the TCP and TLS stream wait buffers. These are
answers waiting to be written back to the clients.
.TP
.I histogram.<sec>.<usec>.to.<sec>.<usec>
Shows a histogram, summed over all threads. Every element counts the
recursive queries whose reply time fit between the lower and upper bound.
@ -531,6 +538,10 @@ other servers.
Number of queries that were made using TLS towards the unbound server.
These are also counted in num.query.tcp, because TLS uses TCP.
.TP
.I num.query.tls.resume
Number of TLS session resumptions, these are queries over TLS towards
the unbound server where the client negotiated a TLS session resumption key.
.TP
.I num.query.ipv6
Number of queries that were made using IPv6 towards the unbound server.
.TP

View File

@ -1,4 +1,4 @@
.TH "unbound-control" "8" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "unbound-control" "8" "May 19, 2020" "NLnet Labs" "unbound 1.10.1"
.\"
.\" unbound-control.8 -- unbound remote control manual
.\"
@ -322,6 +322,12 @@ serial check). And then the zone is transferred for a newer zone version.
.TP
.B view_local_data_remove \fIview\fR \fIname
\fIlocal_data_remove\fR for given view.
.TP
.B view_local_datas_remove \fIview\fR
Remove a list of \fIlocal_data\fR for given view from stdin. Like local_datas_remove.
.TP
.B view_local_datas \fIview\fR
Add a list of \fIlocal_data\fR for given view from stdin. Like local_datas.
.SH "EXIT CODE"
The unbound\-control program exits with status code 1 on error, 0 on success.
.SH "SET UP"
@ -376,8 +382,8 @@ and resulted in recursive processing, taking a slot in the requestlist.
Not part of the recursivereplies (or the histogram thereof) or cachemiss,
as a cache response was sent.
.TP
.I threadX.num.zero_ttl
number of replies with ttl zero, because they served an expired cache entry.
.I threadX.num.expired
number of replies that served an expired cache entry.
.TP
.I threadX.num.recursivereplies
The number of replies sent to queries that needed recursive processing. Could be smaller than threadX.num.cachemiss if due to timeouts no replies were sent for some queries.
@ -440,7 +446,7 @@ summed over threads.
.I total.num.prefetch
summed over threads.
.TP
.I total.num.zero_ttl
.I total.num.expired
summed over threads.
.TP
.I total.num.recursivereplies
@ -496,6 +502,10 @@ Memory in bytes in use by the iterator module.
Memory in bytes in use by the validator module. Includes the key cache and
negative cache.
.TP
.I mem.streamwait
Memory in bytes in used by the TCP and TLS stream wait buffers. These are
answers waiting to be written back to the clients.
.TP
.I histogram.<sec>.<usec>.to.<sec>.<usec>
Shows a histogram, summed over all threads. Every element counts the
recursive queries whose reply time fit between the lower and upper bound.
@ -531,6 +541,10 @@ other servers.
Number of queries that were made using TLS towards the unbound server.
These are also counted in num.query.tcp, because TLS uses TCP.
.TP
.I num.query.tls.resume
Number of TLS session resumptions, these are queries over TLS towards
the unbound server where the client negotiated a TLS session resumption key.
.TP
.I num.query.ipv6
Number of queries that were made using IPv6 towards the unbound server.
.TP
@ -649,6 +663,11 @@ Number of queries that got an answer that contained EDNS client subnet data.
Number of queries answered from the edns client subnet cache. These are
counted as cachemiss by the main counters, but hit the client subnet
specific cache, after getting processed by the edns client subnet module.
.TP
.I num.rpz.action.<rpz_action>
Number of queries answered using configured RPZ policy, per RPZ action type.
Possible actions are: nxdomain, nodata, passthru, drop, local_data, disabled,
and cname_override.
.SH "FILES"
.TP
.I @ub_conf_file@

View File

@ -1,4 +1,4 @@
.TH "unbound\-host" "1" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "unbound\-host" "1" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2"
.\"
.\" unbound-host.1 -- unbound DNS lookup utility
.\"

View File

@ -1,4 +1,4 @@
.TH "unbound\-host" "1" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "unbound\-host" "1" "May 19, 2020" "NLnet Labs" "unbound 1.10.1"
.\"
.\" unbound-host.1 -- unbound DNS lookup utility
.\"

View File

@ -1,4 +1,4 @@
.TH "unbound" "8" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "unbound" "8" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2"
.\"
.\" unbound.8 -- unbound manual
.\"
@ -9,7 +9,7 @@
.\"
.SH "NAME"
.B unbound
\- Unbound DNS validating resolver 1.8.1.
\- Unbound DNS validating resolver 1.9.2.
.SH "SYNOPSIS"
.B unbound
.RB [ \-h ]

View File

@ -1,4 +1,4 @@
.TH "unbound" "8" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "unbound" "8" "May 19, 2020" "NLnet Labs" "unbound 1.10.1"
.\"
.\" unbound.8 -- unbound manual
.\"
@ -9,7 +9,7 @@
.\"
.SH "NAME"
.B unbound
\- Unbound DNS validating resolver 1.8.1.
\- Unbound DNS validating resolver 1.10.1.
.SH "SYNOPSIS"
.B unbound
.RB [ \-h ]
@ -54,7 +54,7 @@ resolvers are using the same port number (53).
The available options are:
.TP
.B \-h
Show the version and commandline option help.
Show the version number and commandline option help, and exit.
.TP
.B \-c\fI cfgfile
Set the config file with settings for unbound to read instead of reading the
@ -76,6 +76,9 @@ concurrently.
.B \-v
Increase verbosity. If given multiple times, more information is logged.
This is in addition to the verbosity (if any) from the config file.
.TP
.B \-V
Show the version number and build options, and exit.
.SH "SEE ALSO"
\fIunbound.conf\fR(5),
\fIunbound\-checkconf\fR(8),

View File

@ -1,4 +1,4 @@
.TH "unbound.conf" "5" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "unbound.conf" "5" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2"
.\"
.\" unbound.conf.5 -- unbound.conf manual
.\"
@ -207,6 +207,16 @@ Maximum UDP response size (not applied to TCP response). 65536 disables the
udp response size maximum, and uses the choice from the client, always.
Suggested values are 512 to 4096. Default is 4096.
.TP
.B stream\-wait\-size: \fI<number>
Number of bytes size maximum to use for waiting stream buffers. Default is
4 megabytes. A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes,
megabytes or gigabytes (1024*1024 bytes in a megabyte). As TCP and TLS streams
queue up multiple results, the amount of memory used for these buffers does
not exceed this number, otherwise the responses are dropped. This manages
the total memory usage of the server (under heavy use), the number of requests
that can be queued up per connection is also limited, with further requests
waiting in TCP buffers.
.TP
.B msg\-buffer\-size: \fI<number>
Number of bytes size of the message buffers. Default is 65552 bytes, enough
for 64 Kb packets, the maximum DNS message size. No message larger than this
@ -253,6 +263,12 @@ eg. 1500 msec. When timeouts happen you need extra sockets, it checks
the ID and remote IP of packets, and unwanted packets are added to the
unwanted packet counter.
.TP
.B unknown\-server\-time\-limit: \fI<msec>
The wait time in msec for waiting for an unknown server to reply.
Increase this if you are behind a slow satellite link, to eg. 1128.
That would then avoid re\-querying every initial query because it times out.
Default is 376 msec.
.TP
.B so\-rcvbuf: \fI<number>
If not 0, then set the SO_RCVBUF socket option to get more buffer
space on UDP port 53 incoming queries. So that short spikes on busy
@ -284,6 +300,8 @@ it may also work. You can enable it (on any platform and kernel),
it then attempts to open the port and passes the option if it was available
at compile time, if that works it is used, if it fails, it continues
silently (unless verbosity 3) without the option.
At extreme load it could be better to turn it off to distribute the queries
evenly, reported for Linux systems (4.4.x).
.TP
.B ip\-transparent: \fI<yes or no>
If yes, then use IP_TRANSPARENT socket option on sockets where unbound
@ -314,11 +332,9 @@ Must be set to a power of 2.
.TP
.B cache\-max\-ttl: \fI<seconds>
Time to live maximum for RRsets and messages in the cache. Default is
86400 seconds (1 day). If the maximum kicks in, responses to clients
still get decrementing TTLs based on the original (larger) values.
When the internal TTL expires, the cache item has expired.
86400 seconds (1 day). When the TTL expires, the cache item has expired.
Can be set lower to force the resolver to query for data often, and not
trust (very large) TTL values.
trust (very large) TTL values. Downstream clients also see the lower TTL.
.TP
.B cache\-min\-ttl: \fI<seconds>
Time to live minimum for RRsets and messages in the cache. Default is 0.
@ -436,20 +452,23 @@ TCP wireformat. The other server must support this (see
\fBtls\-service\-key\fR).
If you enable this, also configure a tls\-cert\-bundle or use tls\-win\-cert to
load CA certs, otherwise the connections cannot be authenticated.
This option enables TLS for all of them, but if you do not set this you can
configure TLS specifically for some forward zones with forward\-tls\-upstream. And also with stub\-tls\-upstream.
.TP
.B ssl\-upstream: \fI<yes or no>
Alternate syntax for \fBtls\-upstream\fR. If both are present in the config
file the last is used.
.TP
.B tls\-service\-key: \fI<file>
If enabled, the server provider TLS service on its TCP sockets. The clients
have to use tls\-upstream: yes. The file is the private key for the TLS
session. The public certificate is in the tls\-service\-pem file. Default
is "", turned off. Requires a restart (a reload is not enough) if changed,
because the private key is read while root permissions are held and before
chroot (if any). Normal DNS TCP service is not provided and gives errors,
this service is best run with a different \fBport:\fR config or \fI@port\fR
suffixes in the \fBinterface\fR config.
If enabled, the server provides TLS service on the TCP ports marked
implicitly or explicitly for TLS service with tls\-port. The file must
contain the private key for the TLS session, the public certificate is in
the tls\-service\-pem file and it must also be specified if tls\-service\-key
is specified. The default is "", turned off. Enabling or disabling
this service requires a restart (a reload is not enough), because the
key is read while root permissions are held and before chroot (if any).
The ports enabled implicitly or explicitly via \fBtls\-port:\fR do not provide
normal DNS TCP service.
.TP
.B ssl\-service\-key: \fI<file>
Alternate syntax for \fBtls\-service\-key\fR.
@ -488,6 +507,27 @@ List portnumbers as tls\-additional\-port, and when interfaces are defined,
eg. with the @port suffix, as this port number, they provide dns over TLS
service. Can list multiple, each on a new statement.
.TP
.B tls-session-ticket-keys: \fI<file>
If not "", lists files with 80 bytes of random contents that are used to
perform TLS session resumption for clients using the unbound server.
These files contain the secret key for the TLS session tickets.
First key use to encrypt and decrypt TLS session tickets.
Other keys use to decrypt only. With this you can roll over to new keys,
by generating a new first file and allowing decrypt of the old file by
listing it after the first file for some time, after the wait clients are not
using the old key any more and the old key can be removed.
One way to create the file is dd if=/dev/random bs=1 count=80 of=ticket.dat
The first 16 bytes should be different from the old one if you create a second key, that is the name used to identify the key. Then there is 32 bytes random
data for an AES key and then 32 bytes random data for the HMAC key.
.TP
.B tls\-ciphers: \fI<string with cipher list>
Set the list of ciphers to allow when serving TLS. Use "" for defaults,
and that is the default.
.TP
.B tls\-ciphersuites: \fI<string with ciphersuites list>
Set the list of ciphersuites to allow when serving TLS. This is for newer
TLS 1.3 connections. Use "" for defaults, and that is the default.
.TP
.B use\-systemd: \fI<yes or no>
Enable or disable systemd socket activation.
Default is no.
@ -508,6 +548,7 @@ classless network block. The action can be \fIdeny\fR, \fIrefuse\fR,
\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIdeny_non_local\fR or
\fIrefuse_non_local\fR.
The most specific netblock match is used, if none match \fIdeny\fR is used.
The order of the access\-control statements therefore does not matter.
.IP
The action \fIdeny\fR stops queries from hosts from that netblock.
.IP
@ -655,6 +696,11 @@ Default is no. Note that it takes time to print these
lines which makes the server (significantly) slower. Odd (nonprintable)
characters in names are printed as '?'.
.TP
.B log\-tag\-queryreply: \fI<yes or no>
Prints the word 'query' and 'reply' with log\-queries and log\-replies.
This makes filtering logs easier. The default is off (for backwards
compatibility).
.TP
.B log\-local\-actions: \fI<yes or no>
Print log lines to inform about local zone actions. These lines are like the
local\-zone type inform prints out, but they are also printed for the other
@ -784,7 +830,7 @@ Can be given multiple times, for different domains.
.TP
.B qname\-minimisation: \fI<yes or no>
Send minimum amount of information to upstream servers to enhance privacy.
Only sent minimum required labels of the QNAME and set QTYPE to A when
Only send minimum required labels of the QNAME and set QTYPE to A when
possible. Best effort approach; full QNAME and original QTYPE will be sent when
upstream replies with a RCODE other than NOERROR, except when receiving
NXDOMAIN from a DNSSEC signed zone. Default is yes.
@ -848,12 +894,18 @@ keep the cache up to date. Default is no. Turning it on gives about
10 percent more traffic and load on the machine, but popular items do
not expire from the cache.
.TP
.B prefetch-key: \fI<yes or no>
.B prefetch\-key: \fI<yes or no>
If yes, fetch the DNSKEYs earlier in the validation process, when a DS
record is encountered. This lowers the latency of requests. It does use
a little more CPU. Also if the cache is set to 0, it is no use. Default is no.
.TP
.B rrset-roundrobin: \fI<yes or no>
.B deny\-any: \fI<yes or no>
If yes, deny queries of type ANY with an empty response. Default is no.
If disabled, unbound responds with a short list of resource records if some
can be found in the cache and makes the upstream type ANY query if there
are none.
.TP
.B rrset\-roundrobin: \fI<yes or no>
If yes, Unbound rotates RRSet order in response (the random number is taken
from the query ID, for speed and thread safety). Default is no.
.TP
@ -881,6 +933,12 @@ Setting this to "iterator" will result in a non\-validating server.
Setting this to "validator iterator" will turn on DNSSEC validation.
The ordering of the modules is important.
You must also set trust\-anchors for validation to be useful.
The default is "validator iterator". When the server is built with
EDNS client subnet support the default is "subnetcache validator iterator".
Most modules that need to be listed here have to be listed at the beginning
of the line. The cachedb module has to be listed just before the iterator.
The python module can be listed in different places, it then processes the
output of the module it is just before.
.TP
.B trust\-anchor\-file: \fI<filename>
File with trusted keys for validation. Both DS and DNSKEY entries can appear
@ -1092,7 +1150,7 @@ address space are not validated. This is usually required whenever
Configure a local zone. The type determines the answer to give if
there is no match from local\-data. The types are deny, refuse, static,
transparent, redirect, nodefault, typetransparent, inform, inform_deny,
always_transparent, always_refuse, always_nxdomain, noview,
inform_redirect, always_transparent, always_refuse, always_nxdomain, noview,
and are explained below. After that the default settings are listed. Use
local\-data: to enter data into the local zone. Answers for local zones
are authoritative DNS answers. By default the zones are class IN.
@ -1153,6 +1211,10 @@ looking up infected names are logged, eg. to run antivirus on them.
The query is dropped, like 'deny', and logged, like 'inform'. Ie. find
infected machines without answering the queries.
.TP 10
\h'5'\fIinform_redirect\fR
The query is redirected, like 'redirect', and logged, like 'inform'.
Ie. answer queries with fixed data and also log the machines that ask.
.TP 10
\h'5'\fIalways_transparent\fR
Like transparent, but ignores local data and resolves normally.
.TP 10
@ -1308,7 +1370,8 @@ TTL can be inserted like this: "2001:DB8::4 7200 www.example.com"
Assign tags to localzones. Tagged localzones will only be applied when the
used access-control element has a matching tag. Tags must be defined in
\fIdefine\-tags\fR. Enclose list of tags in quotes ("") and put spaces between
tags.
tags. When there are multiple tags it checks if the intersection of the
list of tags for the query and local\-zone\-tag is non-empty.
.TP 5
.B local\-zone\-override: \fI<zone> <IP netblock> <type>
Override the localzone type for queries from addresses matching netblock.
@ -1391,22 +1454,20 @@ This can make ordinary queries complete (if repeatedly queried for),
and enter the cache, whilst also mitigating the traffic flow by the
factor given.
.TP 5
.B low\-rtt: \fI<msec time>
Set the time in millisecond that is considere a low ping time for fast
server selection with the low\-rtt\-permil option, that turns this on or off.
The default is 45 msec, a number from IPv6 quick response documents.
.B fast\-server\-permil: \fI<number>
Specify how many times out of 1000 to pick from the set of fastest servers.
0 turns the feature off. A value of 900 would pick from the fastest
servers 90 percent of the time, and would perform normal exploration of random
servers for the remaining time. When prefetch is enabled (or serve\-expired),
such prefetches are not sped up, because there is no one waiting for it, and it
presents a good moment to perform server exploration. The
\fBfast\-server\-num\fR option can be used to specify the size of the fastest
servers set. The default for fast\-server\-permil is 0.
.TP 5
.B low\-rtt\-permil: \fI<number>
Specify how many times out of 1000 to pick the fast server from the low
rtt band. 0 turns the feature off. A value of 900 would pick the fast
server when such fast servers are available 90 percent of the time, and
the remaining time perform normal exploration of random servers.
When prefetch is enabled (or serve\-expired), such prefetches are not
sped up, because there is no one waiting for it, and it presents a good
moment to perform server exploration. The low\-rtt option can be used
to specify which servers are picked for fast server selection, servers
with a ping roundtrip time below that value are considered.
The default for low\-rtt\-permil is 0.
.B fast\-server\-num: \fI<number>
Set the number of servers that should be used for fast server selection. Only
use the fastest specified number of servers with the fast\-server\-permil
option, that turns this on or off. The default is to use the fastest 3 servers.
.SS "Remote Control Options"
In the
.B remote\-control:
@ -1568,13 +1629,11 @@ the '@' and '#', the '@' comes first.
At high verbosity it logs the TLS certificate, with TLS enabled.
If you leave out the '#' and auth name from the forward\-addr, any
name is accepted. The cert must also match a CA from the tls\-cert\-bundle.
The cert name match code needs OpenSSL 1.1.0 or later to be enabled.
.TP
.B forward\-first: \fI<yes or no>
If enabled, a query is attempted without the forward clause if it fails.
The data could not be retrieved and would have caused SERVFAIL because
the servers are unreachable, instead it is tried without this clause.
The default is no.
If a forwarded query is met with a SERVFAIL error, and this option is
enabled, unbound will fall back to normal recursive resolution for this
query as if no query forwarding had been specified. The default is "no".
.TP
.B forward\-tls\-upstream: \fI<yes or no>
Enabled or disable whether the queries to this forwarder use TLS for transport.
@ -1604,6 +1663,13 @@ lookups of that data.
Authority zones can be read from zonefile. And can be kept updated via
AXFR and IXFR. After update the zonefile is rewritten. The update mechanism
uses the SOA timer values and performs SOA UDP queries to detect zone changes.
.LP
If the update fetch fails, the timers in the SOA record are used to time
another fetch attempt. Until the SOA expiry timer is reached. Then the
zone is expired. When a zone is expired, queries are SERVFAIL, and
any new serial number is accepted from the master (even if older), and if
fallback is enabled, the fallback activates to fetch from the upstream instead
of the SERVFAIL.
.TP
.B name: \fI<zone name>
Name of the authority zone.
@ -1611,6 +1677,7 @@ Name of the authority zone.
.B master: \fI<IP address or host name>
Where to download a copy of the zone from, with AXFR and IXFR. Multiple
masters can be specified. They are all tried if one fails.
With the "ip#name" notation a AXFR over TLS can be used.
.TP
.B url: \fI<url to zonefile>
Where to download a zonefile for the zone. With http or https. An example
@ -1662,7 +1729,9 @@ data (eg. from the master servers).
There may be multiple
.B view:
clauses. Each with a \fBname:\fR and zero or more \fBlocal\-zone\fR and
\fBlocal\-data\fR elements. View can be mapped to requests by specifying the
\fBlocal\-data\fR elements. Views can also contain view\-first,
response\-ip, response\-ip\-data and local\-data\-ptr elements.
View can be mapped to requests by specifying the
view name in an \fBaccess\-control\-view\fR element. Options from matching
views will override global options. Global options will be used if no matching
view is found, or when the matching view does not have the option specified.
@ -1843,6 +1912,24 @@ to expose to third parties for IPv6. Defaults to 56.
.B max\-client\-subnet\-ipv4: \fI<number>\fR
Specifies the maximum prefix length of the client source address we are willing
to expose to third parties for IPv4. Defaults to 24.
.TP
.B min\-client\-subnet\-ipv6: \fI<number>\fR
Specifies the minimum prefix length of the IPv6 source mask we are willing to
accept in queries. Shorter source masks result in REFUSED answers. Source mask
of 0 is always accepted. Default is 0.
.TP
.B min\-client\-subnet\-ipv4: \fI<number>\fR
Specifies the minimum prefix length of the IPv4 source mask we are willing to
accept in queries. Shorter source masks result in REFUSED answers. Source mask
of 0 is always accepted. Default is 0.
.TP
.B max\-ecs\-tree\-size\-ipv4: \fI<number>\fR
Specifies the maximum number of subnets ECS answers kept in the ECS radix tree.
This number applies for each qname/qclass/qtype tuple. Defaults to 100.
.TP
.B max\-ecs\-tree\-size\-ipv6: \fI<number>\fR
Specifies the maximum number of subnets ECS answers kept in the ECS radix tree.
This number applies for each qname/qclass/qtype tuple. Defaults to 100.
.SS "Opportunistic IPsec Support Module Options"
.LP
The IPsec module must be configured in the \fBmodule\-config:\fR "ipsecmod

View File

@ -1,4 +1,4 @@
.TH "unbound.conf" "5" "Oct 8, 2018" "NLnet Labs" "unbound 1.8.1"
.TH "unbound.conf" "5" "May 19, 2020" "NLnet Labs" "unbound 1.10.1"
.\"
.\" unbound.conf.5 -- unbound.conf manual
.\"
@ -50,7 +50,7 @@ server:
username: unbound
# make sure unbound can access entropy from inside the chroot.
# e.g. on linux the use these commands (on BSD, devfs(8) is used):
# mount \-\-bind \-n /dev/random /etc/unbound/dev/random
# mount \-\-bind \-n /dev/urandom /etc/unbound/dev/urandom
# and mount \-\-bind \-n /dev/log /etc/unbound/dev/log
chroot: "/etc/unbound"
# logfile: "/etc/unbound/unbound.log" #uncomment to use logfile.
@ -63,8 +63,10 @@ server:
access\-control: 2001:DB8::/64 allow
.fi
.SH "FILE FORMAT"
There must be whitespace between keywords. Attribute keywords end with a colon ':'.
An attribute is followed by its containing attributes, or a value.
There must be whitespace between keywords. Attribute keywords end with a
colon ':'. An attribute is followed by a value, or its containing attributes
in which case it is referred to as a clause. Clauses can be repeated throughout
the file (or included files) to group attributes under the same clause.
.P
Files can be included using the
.B include:
@ -207,6 +209,16 @@ Maximum UDP response size (not applied to TCP response). 65536 disables the
udp response size maximum, and uses the choice from the client, always.
Suggested values are 512 to 4096. Default is 4096.
.TP
.B stream\-wait\-size: \fI<number>
Number of bytes size maximum to use for waiting stream buffers. Default is
4 megabytes. A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes,
megabytes or gigabytes (1024*1024 bytes in a megabyte). As TCP and TLS streams
queue up multiple results, the amount of memory used for these buffers does
not exceed this number, otherwise the responses are dropped. This manages
the total memory usage of the server (under heavy use), the number of requests
that can be queued up per connection is also limited, with further requests
waiting in TCP buffers.
.TP
.B msg\-buffer\-size: \fI<number>
Number of bytes size of the message buffers. Default is 65552 bytes, enough
for 64 Kb packets, the maximum DNS message size. No message larger than this
@ -253,6 +265,12 @@ eg. 1500 msec. When timeouts happen you need extra sockets, it checks
the ID and remote IP of packets, and unwanted packets are added to the
unwanted packet counter.
.TP
.B unknown\-server\-time\-limit: \fI<msec>
The wait time in msec for waiting for an unknown server to reply.
Increase this if you are behind a slow satellite link, to eg. 1128.
That would then avoid re\-querying every initial query because it times out.
Default is 376 msec.
.TP
.B so\-rcvbuf: \fI<number>
If not 0, then set the SO_RCVBUF socket option to get more buffer
space on UDP port 53 incoming queries. So that short spikes on busy
@ -284,6 +302,8 @@ it may also work. You can enable it (on any platform and kernel),
it then attempts to open the port and passes the option if it was available
at compile time, if that works it is used, if it fails, it continues
silently (unless verbosity 3) without the option.
At extreme load it could be better to turn it off to distribute the queries
evenly, reported for Linux systems (4.4.x).
.TP
.B ip\-transparent: \fI<yes or no>
If yes, then use IP_TRANSPARENT socket option on sockets where unbound
@ -314,11 +334,9 @@ Must be set to a power of 2.
.TP
.B cache\-max\-ttl: \fI<seconds>
Time to live maximum for RRsets and messages in the cache. Default is
86400 seconds (1 day). If the maximum kicks in, responses to clients
still get decrementing TTLs based on the original (larger) values.
When the internal TTL expires, the cache item has expired.
86400 seconds (1 day). When the TTL expires, the cache item has expired.
Can be set lower to force the resolver to query for data often, and not
trust (very large) TTL values.
trust (very large) TTL values. Downstream clients also see the lower TTL.
.TP
.B cache\-min\-ttl: \fI<seconds>
Time to live minimum for RRsets and messages in the cache. Default is 0.
@ -436,20 +454,23 @@ TCP wireformat. The other server must support this (see
\fBtls\-service\-key\fR).
If you enable this, also configure a tls\-cert\-bundle or use tls\-win\-cert to
load CA certs, otherwise the connections cannot be authenticated.
This option enables TLS for all of them, but if you do not set this you can
configure TLS specifically for some forward zones with forward\-tls\-upstream. And also with stub\-tls\-upstream.
.TP
.B ssl\-upstream: \fI<yes or no>
Alternate syntax for \fBtls\-upstream\fR. If both are present in the config
file the last is used.
.TP
.B tls\-service\-key: \fI<file>
If enabled, the server provider TLS service on its TCP sockets. The clients
have to use tls\-upstream: yes. The file is the private key for the TLS
session. The public certificate is in the tls\-service\-pem file. Default
is "", turned off. Requires a restart (a reload is not enough) if changed,
because the private key is read while root permissions are held and before
chroot (if any). Normal DNS TCP service is not provided and gives errors,
this service is best run with a different \fBport:\fR config or \fI@port\fR
suffixes in the \fBinterface\fR config.
If enabled, the server provides TLS service on the TCP ports marked
implicitly or explicitly for TLS service with tls\-port. The file must
contain the private key for the TLS session, the public certificate is in
the tls\-service\-pem file and it must also be specified if tls\-service\-key
is specified. The default is "", turned off. Enabling or disabling
this service requires a restart (a reload is not enough), because the
key is read while root permissions are held and before chroot (if any).
The ports enabled implicitly or explicitly via \fBtls\-port:\fR do not provide
normal DNS TCP service.
.TP
.B ssl\-service\-key: \fI<file>
Alternate syntax for \fBtls\-service\-key\fR.
@ -488,6 +509,27 @@ List portnumbers as tls\-additional\-port, and when interfaces are defined,
eg. with the @port suffix, as this port number, they provide dns over TLS
service. Can list multiple, each on a new statement.
.TP
.B tls-session-ticket-keys: \fI<file>
If not "", lists files with 80 bytes of random contents that are used to
perform TLS session resumption for clients using the unbound server.
These files contain the secret key for the TLS session tickets.
First key use to encrypt and decrypt TLS session tickets.
Other keys use to decrypt only. With this you can roll over to new keys,
by generating a new first file and allowing decrypt of the old file by
listing it after the first file for some time, after the wait clients are not
using the old key any more and the old key can be removed.
One way to create the file is dd if=/dev/random bs=1 count=80 of=ticket.dat
The first 16 bytes should be different from the old one if you create a second key, that is the name used to identify the key. Then there is 32 bytes random
data for an AES key and then 32 bytes random data for the HMAC key.
.TP
.B tls\-ciphers: \fI<string with cipher list>
Set the list of ciphers to allow when serving TLS. Use "" for defaults,
and that is the default.
.TP
.B tls\-ciphersuites: \fI<string with ciphersuites list>
Set the list of ciphersuites to allow when serving TLS. This is for newer
TLS 1.3 connections. Use "" for defaults, and that is the default.
.TP
.B use\-systemd: \fI<yes or no>
Enable or disable systemd socket activation.
Default is no.
@ -508,6 +550,7 @@ classless network block. The action can be \fIdeny\fR, \fIrefuse\fR,
\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIdeny_non_local\fR or
\fIrefuse_non_local\fR.
The most specific netblock match is used, if none match \fIdeny\fR is used.
The order of the access\-control statements therefore does not matter.
.IP
The action \fIdeny\fR stops queries from hosts from that netblock.
.IP
@ -588,9 +631,11 @@ In the last case the path is adjusted to remove the unused portion.
The pidfile can be either a relative path to the working directory, or
an absolute path relative to the original root. It is written just prior
to chroot and dropping permissions. This allows the pidfile to be
/var/run/unbound.pid and the chroot to be /var/unbound, for example.
/var/run/unbound.pid and the chroot to be /var/unbound, for example. Note that
Unbound is not able to remove the pidfile after termination when it is located
outside of the chroot directory.
.IP
Additionally, unbound may need to access /dev/random (for entropy)
Additionally, unbound may need to access /dev/urandom (for entropy)
from inside the chroot.
.IP
If given a chroot is done to the given directory. By default chroot is
@ -655,6 +700,11 @@ Default is no. Note that it takes time to print these
lines which makes the server (significantly) slower. Odd (nonprintable)
characters in names are printed as '?'.
.TP
.B log\-tag\-queryreply: \fI<yes or no>
Prints the word 'query' and 'reply' with log\-queries and log\-replies.
This makes filtering logs easier. The default is off (for backwards
compatibility).
.TP
.B log\-local\-actions: \fI<yes or no>
Print log lines to inform about local zone actions. These lines are like the
local\-zone type inform prints out, but they are also printed for the other
@ -727,7 +777,7 @@ wise to send these, and could be necessary for operation if TSIG or EDNS
payload is very large.
.TP
.B harden\-glue: \fI<yes or no>
Will trust glue only if it is within the servers authority. Default is on.
Will trust glue only if it is within the servers authority. Default is yes.
.TP
.B harden\-dnssec\-stripped: \fI<yes or no>
Require DNSSEC data for trust\-anchored zones, if such data is absent,
@ -737,7 +787,7 @@ this behaves like there is no trust anchor. You could turn this off if
you are sometimes behind an intrusive firewall (of some sort) that
removes DNSSEC data from packets, or a zone changes from signed to
unsigned to badly signed often. If turned off you run the risk of a
downgrade attack that disables security for a zone. Default is on.
downgrade attack that disables security for a zone. Default is yes.
.TP
.B harden\-below\-nxdomain: \fI<yes or no>
From RFC 8020 (with title "NXDOMAIN: There Really Is Nothing Underneath"),
@ -747,7 +797,7 @@ noerror for empty nonterminals, hence this is possible. Very old software
might return nxdomain for empty nonterminals (that usually happen for reverse
IP address lookups), and thus may be incompatible with this. To try to avoid
this only DNSSEC-secure nxdomains are used, because the old software does not
have DNSSEC. Default is on.
have DNSSEC. Default is yes.
The nxdomain must be secure, this means nsec3 with optout is insufficient.
.TP
.B harden\-referral\-path: \fI<yes or no>
@ -784,7 +834,7 @@ Can be given multiple times, for different domains.
.TP
.B qname\-minimisation: \fI<yes or no>
Send minimum amount of information to upstream servers to enhance privacy.
Only sent minimum required labels of the QNAME and set QTYPE to A when
Only send minimum required labels of the QNAME and set QTYPE to A when
possible. Best effort approach; full QNAME and original QTYPE will be sent when
upstream replies with a RCODE other than NOERROR, except when receiving
NXDOMAIN from a DNSSEC signed zone. Default is yes.
@ -848,12 +898,18 @@ keep the cache up to date. Default is no. Turning it on gives about
10 percent more traffic and load on the machine, but popular items do
not expire from the cache.
.TP
.B prefetch-key: \fI<yes or no>
.B prefetch\-key: \fI<yes or no>
If yes, fetch the DNSKEYs earlier in the validation process, when a DS
record is encountered. This lowers the latency of requests. It does use
a little more CPU. Also if the cache is set to 0, it is no use. Default is no.
.TP
.B rrset-roundrobin: \fI<yes or no>
.B deny\-any: \fI<yes or no>
If yes, deny queries of type ANY with an empty response. Default is no.
If disabled, unbound responds with a short list of resource records if some
can be found in the cache and makes the upstream type ANY query if there
are none.
.TP
.B rrset\-roundrobin: \fI<yes or no>
If yes, Unbound rotates RRSet order in response (the random number is taken
from the query ID, for speed and thread safety). Default is no.
.TP
@ -881,6 +937,12 @@ Setting this to "iterator" will result in a non\-validating server.
Setting this to "validator iterator" will turn on DNSSEC validation.
The ordering of the modules is important.
You must also set trust\-anchors for validation to be useful.
The default is "validator iterator". When the server is built with
EDNS client subnet support the default is "subnetcache validator iterator".
Most modules that need to be listed here have to be listed at the beginning
of the line. The cachedb module has to be listed just before the iterator.
The python module can be listed in different places, it then processes the
output of the module it is just before.
.TP
.B trust\-anchor\-file: \fI<filename>
File with trusted keys for validation. Both DS and DNSKEY entries can appear
@ -889,7 +951,7 @@ Default is "", or no trust anchor file.
.TP
.B auto\-trust\-anchor\-file: \fI<filename>
File with trust anchor for one zone, which is tracked with RFC5011 probes.
The probes are several times per month, thus the machine must be online
The probes are run several times per month, thus the machine must be online
frequently. The initial file can be one with contents as described in
\fBtrust\-anchor\-file\fR. The file is written to when the anchor is updated,
so the unbound user must have write permission. Write permission to the file,
@ -914,10 +976,10 @@ It is possible to use wildcards with this statement, the wildcard is
expanded on start and on reload.
.TP
.B trust\-anchor\-signaling: \fI<yes or no>
Send RFC8145 key tag query after trust anchor priming. Default is on.
Send RFC8145 key tag query after trust anchor priming. Default is yes.
.TP
.B root\-key\-sentinel: \fI<yes or no>
Root key trust anchor sentinel. Default is on.
Root key trust anchor sentinel. Default is yes.
.TP
.B dlv\-anchor\-file: \fI<filename>
This option was used during early days DNSSEC deployment when no parent-side
@ -1010,20 +1072,35 @@ The default value is "no".
.TP
.B serve\-expired: \fI<yes or no>
If enabled, unbound attempts to serve old responses from cache with a
TTL of 0 in the response without waiting for the actual resolution to finish.
The actual resolution answer ends up in the cache later on. Default is "no".
TTL of \fBserve\-expired\-reply\-ttl\fR in the response without waiting for the
actual resolution to finish. The actual resolution answer ends up in the cache
later on. Default is "no".
.TP
.B serve\-expired\-ttl: \fI<seconds>
Limit serving of expired responses to configured seconds after expiration. 0
disables the limit. This option only applies when \fBserve\-expired\fR is
enabled. The default is 0.
disables the limit. This option only applies when \fBserve\-expired\fR is
enabled. A suggested value per draft-ietf-dnsop-serve-stale-10 is between
86400 (1 day) and 259200 (3 days). The default is 0.
.TP
.B serve\-expired\-ttl\-reset: \fI<yes or no>
Set the TTL of expired records to the \fBserve\-expired\-ttl\fR value after a
failed attempt to retrieve the record from upstream. This makes sure that the
expired records will be served as long as there are queries for it. Default is
failed attempt to retrieve the record from upstream. This makes sure that the
expired records will be served as long as there are queries for it. Default is
"no".
.TP
.B serve\-expired\-reply\-ttl: \fI<seconds>
TTL value to use when replying with expired data. If
\fBserve\-expired\-client\-timeout\fR is also used then it is RECOMMENDED to
use 30 as the value (draft-ietf-dnsop-serve-stale-10). The default is 30.
.TP
.B serve\-expired\-client\-timeout: \fI<msec>
Time in milliseconds before replying to the client with expired data. This
essentially enables the serve-stale behavior as specified in
draft-ietf-dnsop-serve-stale-10 that first tries to resolve before immediately
responding with expired data. A recommended value per
draft-ietf-dnsop-serve-stale-10 is 1800. Setting this to 0 will disable this
behavior. Default is 0.
.TP
.B val\-nsec3\-keysize\-iterations: \fI<"list of values">
List of keysize and iteration count values, separated by spaces, surrounded
by quotes. Default is "1024 150 2048 500 4096 2500". This determines the
@ -1092,7 +1169,7 @@ address space are not validated. This is usually required whenever
Configure a local zone. The type determines the answer to give if
there is no match from local\-data. The types are deny, refuse, static,
transparent, redirect, nodefault, typetransparent, inform, inform_deny,
always_transparent, always_refuse, always_nxdomain, noview,
inform_redirect, always_transparent, always_refuse, always_nxdomain, noview,
and are explained below. After that the default settings are listed. Use
local\-data: to enter data into the local zone. Answers for local zones
are authoritative DNS answers. By default the zones are class IN.
@ -1153,6 +1230,10 @@ looking up infected names are logged, eg. to run antivirus on them.
The query is dropped, like 'deny', and logged, like 'inform'. Ie. find
infected machines without answering the queries.
.TP 10
\h'5'\fIinform_redirect\fR
The query is redirected, like 'redirect', and logged, like 'inform'.
Ie. answer queries with fixed data and also log the machines that ask.
.TP 10
\h'5'\fIalways_transparent\fR
Like transparent, but ignores local data and resolves normally.
.TP 10
@ -1232,7 +1313,7 @@ local\-data: "onion. 10800 IN
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
.fi
.TP 10
\h'5'\fItest (RFC 2606)\fR
\h'5'\fItest (RFC 6761)\fR
Default content:
.nf
local\-zone: "test." static
@ -1241,7 +1322,7 @@ local\-data: "test. 10800 IN
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
.fi
.TP 10
\h'5'\fIinvalid (RFC 2606)\fR
\h'5'\fIinvalid (RFC 6761)\fR
Default content:
.nf
local\-zone: "invalid." static
@ -1308,7 +1389,8 @@ TTL can be inserted like this: "2001:DB8::4 7200 www.example.com"
Assign tags to localzones. Tagged localzones will only be applied when the
used access-control element has a matching tag. Tags must be defined in
\fIdefine\-tags\fR. Enclose list of tags in quotes ("") and put spaces between
tags.
tags. When there are multiple tags it checks if the intersection of the
list of tags for the query and local\-zone\-tag is non-empty.
.TP 5
.B local\-zone\-override: \fI<zone> <IP netblock> <type>
Override the localzone type for queries from addresses matching netblock.
@ -1391,22 +1473,20 @@ This can make ordinary queries complete (if repeatedly queried for),
and enter the cache, whilst also mitigating the traffic flow by the
factor given.
.TP 5
.B low\-rtt: \fI<msec time>
Set the time in millisecond that is considere a low ping time for fast
server selection with the low\-rtt\-permil option, that turns this on or off.
The default is 45 msec, a number from IPv6 quick response documents.
.B fast\-server\-permil: \fI<number>
Specify how many times out of 1000 to pick from the set of fastest servers.
0 turns the feature off. A value of 900 would pick from the fastest
servers 90 percent of the time, and would perform normal exploration of random
servers for the remaining time. When prefetch is enabled (or serve\-expired),
such prefetches are not sped up, because there is no one waiting for it, and it
presents a good moment to perform server exploration. The
\fBfast\-server\-num\fR option can be used to specify the size of the fastest
servers set. The default for fast\-server\-permil is 0.
.TP 5
.B low\-rtt\-permil: \fI<number>
Specify how many times out of 1000 to pick the fast server from the low
rtt band. 0 turns the feature off. A value of 900 would pick the fast
server when such fast servers are available 90 percent of the time, and
the remaining time perform normal exploration of random servers.
When prefetch is enabled (or serve\-expired), such prefetches are not
sped up, because there is no one waiting for it, and it presents a good
moment to perform server exploration. The low\-rtt option can be used
to specify which servers are picked for fast server selection, servers
with a ping roundtrip time below that value are considered.
The default for low\-rtt\-permil is 0.
.B fast\-server\-num: \fI<number>
Set the number of servers that should be used for fast server selection. Only
use the fastest specified number of servers with the fast\-server\-permil
option, that turns this on or off. The default is to use the fastest 3 servers.
.SS "Remote Control Options"
In the
.B remote\-control:
@ -1568,13 +1648,11 @@ the '@' and '#', the '@' comes first.
At high verbosity it logs the TLS certificate, with TLS enabled.
If you leave out the '#' and auth name from the forward\-addr, any
name is accepted. The cert must also match a CA from the tls\-cert\-bundle.
The cert name match code needs OpenSSL 1.1.0 or later to be enabled.
.TP
.B forward\-first: \fI<yes or no>
If enabled, a query is attempted without the forward clause if it fails.
The data could not be retrieved and would have caused SERVFAIL because
the servers are unreachable, instead it is tried without this clause.
The default is no.
If a forwarded query is met with a SERVFAIL error, and this option is
enabled, unbound will fall back to normal recursive resolution for this
query as if no query forwarding had been specified. The default is "no".
.TP
.B forward\-tls\-upstream: \fI<yes or no>
Enabled or disable whether the queries to this forwarder use TLS for transport.
@ -1604,6 +1682,13 @@ lookups of that data.
Authority zones can be read from zonefile. And can be kept updated via
AXFR and IXFR. After update the zonefile is rewritten. The update mechanism
uses the SOA timer values and performs SOA UDP queries to detect zone changes.
.LP
If the update fetch fails, the timers in the SOA record are used to time
another fetch attempt. Until the SOA expiry timer is reached. Then the
zone is expired. When a zone is expired, queries are SERVFAIL, and
any new serial number is accepted from the master (even if older), and if
fallback is enabled, the fallback activates to fetch from the upstream instead
of the SERVFAIL.
.TP
.B name: \fI<zone name>
Name of the authority zone.
@ -1611,6 +1696,13 @@ Name of the authority zone.
.B master: \fI<IP address or host name>
Where to download a copy of the zone from, with AXFR and IXFR. Multiple
masters can be specified. They are all tried if one fails.
With the "ip#name" notation a AXFR over TLS can be used.
If you point it at another Unbound instance, it would not work because
that does not support AXFR/IXFR for the zone, but if you used \fBurl:\fR to download
the zonefile as a text file from a webserver that would work.
If you specify the hostname, you cannot use the domain from the zonefile,
because it may not have that when retrieving that data, instead use a plain
IP address to avoid a circular dependency on retrieving that IP address.
.TP
.B url: \fI<url to zonefile>
Where to download a zonefile for the zone. With http or https. An example
@ -1622,6 +1714,10 @@ see if the SOA serial number has changed, reducing the number of downloads.
If none of the urls work, the masters are tried with IXFR and AXFR.
For https, the \fBtls\-cert\-bundle\fR and the hostname from the url are used
to authenticate the connection.
If you specify a hostname in the URL, you cannot use the domain from the
zonefile, because it may not have that when retrieving that data, instead
use a plain IP address to avoid a circular dependency on retrieving that IP
address. Avoid dependencies on name lookups by using a notation like "http://192.0.2.1/unbound-master/example.com.zone", with an explicit IP address.
.TP
.B allow\-notify: \fI<IP address or host name or netblockIP/prefix>
With allow\-notify you can specify additional sources of notifies.
@ -1662,7 +1758,9 @@ data (eg. from the master servers).
There may be multiple
.B view:
clauses. Each with a \fBname:\fR and zero or more \fBlocal\-zone\fR and
\fBlocal\-data\fR elements. View can be mapped to requests by specifying the
\fBlocal\-data\fR elements. Views can also contain view\-first,
response\-ip, response\-ip\-data and local\-data\-ptr elements.
View can be mapped to requests by specifying the
view name in an \fBaccess\-control\-view\fR element. Options from matching
views will override global options. Global options will be used if no matching
view is found, or when the matching view does not have the option specified.
@ -1699,7 +1797,8 @@ clause gives the settings for the \fIpython\fR(1) script module. This module
acts like the iterator and validator modules do, on queries and answers.
To enable the script module it has to be compiled into the daemon,
and the word "python" has to be put in the \fBmodule\-config:\fR option
(usually first, or between the validator and iterator).
(usually first, or between the validator and iterator). Multiple instances of
the python module are supported by adding the word "python" more than once.
.LP
If the \fBchroot:\fR option is enabled, you should make sure Python's
library directory structure is bind mounted in the new root environment, see
@ -1708,7 +1807,8 @@ absolute path relative to the new root, or as a relative path to the working
directory.
.TP
.B python\-script: \fI<python file>\fR
The script file to load.
The script file to load. Repeat this option for every python module instance
added to the \fBmodule\-config:\fR option.
.SS "DNS64 Module Options"
.LP
The dns64 module must be configured in the \fBmodule\-config:\fR "dns64
@ -1843,6 +1943,24 @@ to expose to third parties for IPv6. Defaults to 56.
.B max\-client\-subnet\-ipv4: \fI<number>\fR
Specifies the maximum prefix length of the client source address we are willing
to expose to third parties for IPv4. Defaults to 24.
.TP
.B min\-client\-subnet\-ipv6: \fI<number>\fR
Specifies the minimum prefix length of the IPv6 source mask we are willing to
accept in queries. Shorter source masks result in REFUSED answers. Source mask
of 0 is always accepted. Default is 0.
.TP
.B min\-client\-subnet\-ipv4: \fI<number>\fR
Specifies the minimum prefix length of the IPv4 source mask we are willing to
accept in queries. Shorter source masks result in REFUSED answers. Source mask
of 0 is always accepted. Default is 0.
.TP
.B max\-ecs\-tree\-size\-ipv4: \fI<number>\fR
Specifies the maximum number of subnets ECS answers kept in the ECS radix tree.
This number applies for each qname/qclass/qtype tuple. Defaults to 100.
.TP
.B max\-ecs\-tree\-size\-ipv6: \fI<number>\fR
Specifies the maximum number of subnets ECS answers kept in the ECS radix tree.
This number applies for each qname/qclass/qtype tuple. Defaults to 100.
.SS "Opportunistic IPsec Support Module Options"
.LP
The IPsec module must be configured in the \fBmodule\-config:\fR "ipsecmod
@ -1923,6 +2041,13 @@ to the query without performing iterative DNS resolution.
If Unbound cannot even find an answer in the backend, it resolves the
query as usual, and stores the answer in the backend.
.P
This module interacts with the \fBserve\-expired\-*\fR options and will reply
with expired data if unbound is configured for that. Currently the use
of \fBserve\-expired\-client\-timeout:\fR and
\fBserve\-expired\-reply\-ttl:\fR is not consistent for data originating from
the external cache as these will result in a reply with 0 TTL without trying to
update the data first, ignoring the configured values.
.P
If Unbound was built with
\fB\-\-with\-libhiredis\fR
on a system that has installed the hiredis C client library of Redis,
@ -1989,6 +2114,70 @@ If this timeout expires Unbound closes the connection, treats it as
if the Redis server does not have the requested data, and will try to
re-establish a new connection later.
This option defaults to 100 milliseconds.
.SS Response Policy Zone Options
.LP
Response Policy Zones are configured with \fBrpz:\fR, and each one must have a
\fBname:\fR. There can be multiple ones, by listing multiple rpz clauses, each
with a different name. RPZ clauses are applied in order of configuration. The
\fBrespip\fR module needs to be added to the \fBmodule-config\fR, e.g.:
\fBmodule-config: "respip validator iterator"\fR.
.P
Only the QNAME and Response IP Address triggers are supported. The supported RPZ
actions are: NXDOMAIN, NODATA, PASSTHRU, DROP and Local Data. RPZ QNAME triggers
are applied after
\fBlocal-zones\fR and before \fBauth-zones\fR.
.TP
.B name: \fI<zone name>
Name of the authority zone.
.TP
.B master: \fI<IP address or host name>
Where to download a copy of the zone from, with AXFR and IXFR. Multiple
masters can be specified. They are all tried if one fails.
.TP
.B url: \fI<url to zonefile>
Where to download a zonefile for the zone. With http or https. An example
for the url is "http://www.example.com/example.org.zone". Multiple url
statements can be given, they are tried in turn. If only urls are given
the SOA refresh timer is used to wait for making new downloads. If also
masters are listed, the masters are first probed with UDP SOA queries to
see if the SOA serial number has changed, reducing the number of downloads.
If none of the urls work, the masters are tried with IXFR and AXFR.
For https, the \fBtls\-cert\-bundle\fR and the hostname from the url are used
to authenticate the connection.
.TP
.B allow\-notify: \fI<IP address or host name or netblockIP/prefix>
With allow\-notify you can specify additional sources of notifies.
When notified, the server attempts to first probe and then zone transfer.
If the notify is from a master, it first attempts that master. Otherwise
other masters are attempted. If there are no masters, but only urls, the
file is downloaded when notified. The masters from master: statements are
allowed notify by default.
.TP
.B zonefile: \fI<filename>
The filename where the zone is stored. If not given then no zonefile is used.
If the file does not exist or is empty, unbound will attempt to fetch zone
data (eg. from the master servers).
.TP
.B rpz\-action\-override: \fI<action>
Always use this RPZ action for matching triggers from this zone. Possible action
are: nxdomain, nodata, passthru, drop, disabled and cname.
.TP
.B rpz\-cname\-override: \fI<domain>
The CNAME target domain to use if the cname action is configured for
\fBrpz\-action\-override\fR.
.TP
.B rpz\-log: \fI<yes or no>
Log all applied RPZ actions for this RPZ zone. Default is no.
.TP
.B rpz\-log\-name: \fI<name>
Specify a string to be part of the log line, for easy referencing.
.TP
.B tags: \fI<list of tags>
Limit the policies from this RPZ clause to clients with a matching tag. Tags
need to be defined in \fBdefine\-tag\fR and can be assigned to client addresses
using \fBaccess\-control\-tag\fR. Enclose list of tags in quotes ("") and put
spaces between tags. If no tags are specified the policies from this clause will
be applied for all clients.
.SH "MEMORY CONTROL EXAMPLE"
In the example config settings below memory usage is reduced. Some service
levels are lower, notable very large data and a high TCP load are no longer

View File

@ -612,18 +612,24 @@ RECURSIVE = YES
EXCLUDE = ./build \
./compat \
./contrib \
util/configparser.c \
util/configparser.h \
util/configlexer.c \
util/locks.h \
pythonmod/doc \
pythonmod/examples \
pythonmod/unboundmodule.py \
pythonmod/interface.h \
pythonmod/examples/resgen.py \
pythonmod/examples/resmod.py \
pythonmod/examples/resip.py \
pythonmod/ubmodule-msg.py \
pythonmod/ubmodule-tst.py \
unboundmodule.py \
libunbound/python/unbound.py \
libunbound/python/libunbound_wrap.c \
libunbound/python/doc \
libunbound/python/examples \
./ldns-src \
README.md \
doc/control_proto_spec.txt \
doc/requirements.txt

View File

@ -119,7 +119,7 @@ node_size(const struct addrtree *tree, const struct addrnode *n)
struct addrtree *
addrtree_create(addrlen_t max_depth, void (*delfunc)(void *, void *),
size_t (*sizefunc)(void *), void *env, unsigned int max_node_count)
size_t (*sizefunc)(void *), void *env, uint32_t max_node_count)
{
struct addrtree *tree;
log_assert(delfunc != NULL);

View File

@ -66,10 +66,10 @@ struct addrtree {
struct addrnode *root;
/** Number of elements in the tree (not always equal to number of
* nodes) */
unsigned int node_count;
uint32_t node_count;
/** Maximum number of allowed nodes, will be enforced by LRU list.
* Excluding the root node, 0 for unlimited */
unsigned int max_node_count;
uint32_t max_node_count;
/** Size of tree in bytes */
size_t size_bytes;
/** Maximum prefix length we are willing to cache. */
@ -137,7 +137,7 @@ size_t addrtree_size(const struct addrtree *tree);
*/
struct addrtree *
addrtree_create(addrlen_t max_depth, void (*delfunc)(void *, void *),
size_t (*sizefunc)(void *), void *env, unsigned int max_node_count);
size_t (*sizefunc)(void *), void *env, uint32_t max_node_count);
/**
* Free tree and all nodes below.

View File

@ -55,8 +55,7 @@
#include "util/config_file.h"
#include "util/data/msgreply.h"
#include "sldns/sbuffer.h"
#define ECS_MAX_TREESIZE 100
#include "iterator/iter_utils.h"
/** externally called */
void
@ -93,6 +92,7 @@ subnet_new_qstate(struct module_qstate *qstate, int id)
return 0;
qstate->minfo[id] = sq;
memset(sq, 0, sizeof(*sq));
sq->started_no_cache_store = qstate->no_cache_store;
return 1;
}
@ -150,7 +150,9 @@ int ecs_whitelist_check(struct query_info* qinfo,
/* Cache by default, might be disabled after parsing EDNS option
* received from nameserver. */
qstate->no_cache_store = 0;
if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo)) {
qstate->no_cache_store = 0;
}
if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream &&
qstate->env->cfg->client_subnet_always_forward) ||
@ -177,6 +179,14 @@ int ecs_whitelist_check(struct query_info* qinfo,
}
void
subnet_markdel(void* key)
{
struct msgreply_entry *e = (struct msgreply_entry*)key;
e->key.qtype = 0;
e->key.qclass = 0;
}
int
subnetmod_init(struct module_env *env, int id)
{
@ -193,6 +203,7 @@ 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("subnet: could not create cache");
free(sn_env);
@ -291,13 +302,13 @@ get_tree(struct subnet_msg_cache_data *data, struct ecs_data *edns,
if (!data->tree4)
data->tree4 = addrtree_create(
cfg->max_client_subnet_ipv4, &delfunc,
&sizefunc, env, ECS_MAX_TREESIZE);
&sizefunc, env, cfg->max_ecs_tree_size_ipv4);
tree = data->tree4;
} else {
if (!data->tree6)
data->tree6 = addrtree_create(
cfg->max_client_subnet_ipv6, &delfunc,
&sizefunc, env, ECS_MAX_TREESIZE);
&sizefunc, env, cfg->max_ecs_tree_size_ipv6);
tree = data->tree6;
}
return tree;
@ -323,33 +334,37 @@ update_cache(struct module_qstate *qstate, int id)
/* Step 1, general qinfo lookup */
struct lruhash_entry *lru_entry = slabhash_lookup(subnet_msg_cache, h,
&qstate->qinfo, 1);
int acquired_lock = (lru_entry != NULL);
int need_to_insert = (lru_entry == NULL);
if (!lru_entry) {
void* data = calloc(1,
sizeof(struct subnet_msg_cache_data));
if(!data) {
log_err("malloc failed");
return;
}
qinf = qstate->qinfo;
qinf.qname = memdup(qstate->qinfo.qname,
qstate->qinfo.qname_len);
if(!qinf.qname) {
free(data);
log_err("memdup failed");
return;
}
mrep_entry = query_info_entrysetup(&qinf, NULL, h);
mrep_entry = query_info_entrysetup(&qinf, data, h);
free(qinf.qname); /* if qname 'consumed', it is set to NULL */
if (!mrep_entry) {
free(data);
log_err("query_info_entrysetup failed");
return;
}
lru_entry = &mrep_entry->entry;
lock_rw_wrlock(&lru_entry->lock);
lru_entry->data = calloc(1,
sizeof(struct subnet_msg_cache_data));
if (!lru_entry->data) {
log_err("malloc failed");
return;
}
}
/* lru_entry->lock is locked regardless of how we got here,
* either from the slabhash_lookup, or above in the new allocated */
/* Step 2, find the correct tree */
if (!(tree = get_tree(lru_entry->data, edns, sne, qstate->env->cfg))) {
if (acquired_lock) lock_rw_unlock(&lru_entry->lock);
lock_rw_unlock(&lru_entry->lock);
log_err("Subnet cache insertion failed");
return;
}
@ -357,7 +372,7 @@ update_cache(struct module_qstate *qstate, int id)
rep = reply_info_copy(qstate->return_msg->rep, &sne->alloc, NULL);
lock_quick_unlock(&sne->alloc.lock);
if (!rep) {
if (acquired_lock) lock_rw_unlock(&lru_entry->lock);
lock_rw_unlock(&lru_entry->lock);
log_err("Subnet cache insertion failed");
return;
}
@ -374,10 +389,9 @@ update_cache(struct module_qstate *qstate, int id)
edns->subnet_source_mask,
sq->ecs_server_in.subnet_scope_mask, rep,
rep->ttl, *qstate->env->now);
if (acquired_lock) {
lock_rw_unlock(&lru_entry->lock);
} else {
lock_rw_unlock(&lru_entry->lock);
lock_rw_unlock(&lru_entry->lock);
if (need_to_insert) {
slabhash_insert(subnet_msg_cache, h, lru_entry, lru_entry->data,
NULL);
}
@ -417,7 +431,7 @@ lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
}
qstate->return_msg = tomsg(NULL, &qstate->qinfo,
(struct reply_info *)node->elem, qstate->region, *env->now,
(struct reply_info *)node->elem, qstate->region, *env->now, 0,
env->scratch);
scope = (uint8_t)node->scope;
lock_rw_unlock(&e->lock);
@ -487,9 +501,11 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
* is still usefull to put it in the edns subnet cache for
* when a client explicitly asks for subnet specific answer. */
verbose(VERB_QUERY, "subnet: Authority indicates no support");
lock_rw_wrlock(&sne->biglock);
update_cache(qstate, id);
lock_rw_unlock(&sne->biglock);
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;
@ -515,7 +531,9 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
}
lock_rw_wrlock(&sne->biglock);
update_cache(qstate, id);
if(!sq->started_no_cache_store) {
update_cache(qstate, id);
}
sne->num_msg_nocache++;
lock_rw_unlock(&sne->biglock);
@ -526,6 +544,19 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
c_out->subnet_source_mask = c_in->subnet_source_mask;
memcpy(&c_out->subnet_addr, &c_in->subnet_addr, INET6_SIZE);
c_out->subnet_scope_mask = s_in->subnet_scope_mask;
/* Limit scope returned to client to scope used for caching. */
if(c_out->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
if(c_out->subnet_scope_mask >
qstate->env->cfg->max_client_subnet_ipv4) {
c_out->subnet_scope_mask =
qstate->env->cfg->max_client_subnet_ipv4;
}
}
else if(c_out->subnet_scope_mask >
qstate->env->cfg->max_client_subnet_ipv6) {
c_out->subnet_scope_mask =
qstate->env->cfg->max_client_subnet_ipv6;
}
c_out->subnet_validdata = 1;
}
return module_finished;
@ -697,6 +728,17 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
return;
}
/* Limit to minimum allowed source mask */
if(sq->ecs_client_in.subnet_source_mask != 0 && (
(sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 &&
sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv4) ||
(sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6 &&
sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv6))) {
qstate->return_rcode = LDNS_RCODE_REFUSED;
qstate->ext_state[id] = module_finished;
return;
}
lock_rw_wrlock(&sne->biglock);
if (lookup_and_reply(qstate, id, sq)) {
sne->num_msg_cache++;
@ -753,6 +795,7 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
ecs_opt_list_append(&sq->ecs_client_out,
&qstate->edns_opts_front_out, qstate);
}
qstate->no_cache_store = sq->started_no_cache_store;
return;
}
if(sq && outbound) {

View File

@ -83,6 +83,8 @@ struct subnet_qstate {
struct ecs_data ecs_server_out;
int subnet_downstream;
int subnet_sent;
/** has the subnet module been started with no_cache_store? */
int started_no_cache_store;
};
void subnet_data_delete(void* d, void* ATTR_UNUSED(arg));
@ -131,4 +133,7 @@ int ecs_edns_back_parsed(struct module_qstate* qstate, int id, void* cbargs);
int ecs_query_response(struct module_qstate* qstate, struct dns_msg* response,
int id, void* cbargs);
/** mark subnet msg to be deleted */
void subnet_markdel(void* key);
#endif /* SUBNETMOD_H */

View File

@ -1,4 +1,4 @@
#!/bin/sh
#!/usr/bin/sh
# install - install a program, script, or datafile
scriptversion=2013-12-25.23; # UTC

View File

@ -103,11 +103,11 @@ ipsecmod_new(struct module_qstate* qstate, int id)
{
struct ipsecmod_qstate* iq = (struct ipsecmod_qstate*)regional_alloc(
qstate->region, sizeof(struct ipsecmod_qstate));
memset(iq, 0, sizeof(*iq));
qstate->minfo[id] = iq;
if(!iq)
return 0;
/* Initialise it. */
memset(iq, 0, sizeof(*iq));
iq->enabled = qstate->env->cfg->ipsecmod_enabled;
iq->is_whitelisted = ipsecmod_domain_is_whitelisted(
(struct ipsecmod_env*)qstate->env->modinfo[id], qstate->qinfo.qname,
@ -161,6 +161,71 @@ generate_request(struct module_qstate* qstate, int id, uint8_t* name,
return 1;
}
/**
* Check if the string passed is a valid domain name with safe characters to
* pass to a shell.
* This will only allow:
* - digits
* - alphas
* - hyphen (not at the start)
* - dot (not at the start, or the only character)
* - underscore
* @param s: pointer to the string.
* @param slen: string's length.
* @return true if s only contains safe characters; false otherwise.
*/
static int
domainname_has_safe_characters(char* s, size_t slen) {
size_t i;
for(i = 0; i < slen; i++) {
if(s[i] == '\0') return 1;
if((s[i] == '-' && i != 0)
|| (s[i] == '.' && (i != 0 || s[1] == '\0'))
|| (s[i] == '_') || (s[i] >= '0' && s[i] <= '9')
|| (s[i] >= 'A' && s[i] <= 'Z')
|| (s[i] >= 'a' && s[i] <= 'z')) {
continue;
}
return 0;
}
return 1;
}
/**
* Check if the stringified IPSECKEY RDATA contains safe characters to pass to
* a shell.
* This is only relevant for checking the gateway when the gateway type is 3
* (domainname).
* @param s: pointer to the string.
* @param slen: string's length.
* @return true if s contains only safe characters; false otherwise.
*/
static int
ipseckey_has_safe_characters(char* s, size_t slen) {
int precedence, gateway_type, algorithm;
char* gateway;
gateway = (char*)calloc(slen, sizeof(char));
if(!gateway) {
log_err("ipsecmod: out of memory when calling the hook");
return 0;
}
if(sscanf(s, "%d %d %d %s ",
&precedence, &gateway_type, &algorithm, gateway) != 4) {
free(gateway);
return 0;
}
if(gateway_type != 3) {
free(gateway);
return 1;
}
if(domainname_has_safe_characters(gateway, slen)) {
free(gateway);
return 1;
}
free(gateway);
return 0;
}
/**
* Prepare the data and call the hook.
*
@ -175,7 +240,7 @@ call_hook(struct module_qstate* qstate, struct ipsecmod_qstate* iq,
{
size_t slen, tempdata_len, tempstring_len, i;
char str[65535], *s, *tempstring;
int w;
int w = 0, w_temp, qtype;
struct ub_packed_rrset_key* rrset_key;
struct packed_rrset_data* rrset_data;
uint8_t *tempdata;
@ -192,9 +257,9 @@ call_hook(struct module_qstate* qstate, struct ipsecmod_qstate* iq,
memset(s, 0, slen);
/* Copy the hook into the buffer. */
sldns_str_print(&s, &slen, "%s", qstate->env->cfg->ipsecmod_hook);
w += sldns_str_print(&s, &slen, "%s", qstate->env->cfg->ipsecmod_hook);
/* Put space into the buffer. */
sldns_str_print(&s, &slen, " ");
w += sldns_str_print(&s, &slen, " ");
/* Copy the qname into the buffer. */
tempstring = sldns_wire2str_dname(qstate->qinfo.qname,
qstate->qinfo.qname_len);
@ -202,68 +267,96 @@ call_hook(struct module_qstate* qstate, struct ipsecmod_qstate* iq,
log_err("ipsecmod: out of memory when calling the hook");
return 0;
}
sldns_str_print(&s, &slen, "\"%s\"", tempstring);
if(!domainname_has_safe_characters(tempstring, strlen(tempstring))) {
log_err("ipsecmod: qname has unsafe characters");
free(tempstring);
return 0;
}
w += sldns_str_print(&s, &slen, "\"%s\"", tempstring);
free(tempstring);
/* Put space into the buffer. */
sldns_str_print(&s, &slen, " ");
w += sldns_str_print(&s, &slen, " ");
/* Copy the IPSECKEY TTL into the buffer. */
rrset_data = (struct packed_rrset_data*)iq->ipseckey_rrset->entry.data;
sldns_str_print(&s, &slen, "\"%ld\"", (long)rrset_data->ttl);
w += sldns_str_print(&s, &slen, "\"%ld\"", (long)rrset_data->ttl);
/* Put space into the buffer. */
sldns_str_print(&s, &slen, " ");
/* Copy the A/AAAA record(s) into the buffer. Start and end this section
* with a double quote. */
w += sldns_str_print(&s, &slen, " ");
rrset_key = reply_find_answer_rrset(&qstate->return_msg->qinfo,
qstate->return_msg->rep);
/* Double check that the records are indeed A/AAAA.
* This should never happen as this function is only executed for A/AAAA
* queries but make sure we don't pass anything other than A/AAAA to the
* shell. */
qtype = ntohs(rrset_key->rk.type);
if(qtype != LDNS_RR_TYPE_AAAA && qtype != LDNS_RR_TYPE_A) {
log_err("ipsecmod: Answer is not of A or AAAA type");
return 0;
}
rrset_data = (struct packed_rrset_data*)rrset_key->entry.data;
sldns_str_print(&s, &slen, "\"");
/* Copy the A/AAAA record(s) into the buffer. Start and end this section
* with a double quote. */
w += sldns_str_print(&s, &slen, "\"");
for(i=0; i<rrset_data->count; i++) {
if(i > 0) {
/* Put space into the buffer. */
sldns_str_print(&s, &slen, " ");
w += sldns_str_print(&s, &slen, " ");
}
/* Ignore the first two bytes, they are the rr_data len. */
w = sldns_wire2str_rdata_buf(rrset_data->rr_data[i] + 2,
w_temp = sldns_wire2str_rdata_buf(rrset_data->rr_data[i] + 2,
rrset_data->rr_len[i] - 2, s, slen, qstate->qinfo.qtype);
if(w < 0) {
if(w_temp < 0) {
/* Error in printout. */
return -1;
} else if((size_t)w >= slen) {
log_err("ipsecmod: Error in printing IP address");
return 0;
} else if((size_t)w_temp >= slen) {
s = NULL; /* We do not want str to point outside of buffer. */
slen = 0;
return -1;
log_err("ipsecmod: shell command too long");
return 0;
} else {
s += w;
slen -= w;
s += w_temp;
slen -= w_temp;
w += w_temp;
}
}
sldns_str_print(&s, &slen, "\"");
w += sldns_str_print(&s, &slen, "\"");
/* Put space into the buffer. */
sldns_str_print(&s, &slen, " ");
w += sldns_str_print(&s, &slen, " ");
/* Copy the IPSECKEY record(s) into the buffer. Start and end this section
* with a double quote. */
sldns_str_print(&s, &slen, "\"");
w += sldns_str_print(&s, &slen, "\"");
rrset_data = (struct packed_rrset_data*)iq->ipseckey_rrset->entry.data;
for(i=0; i<rrset_data->count; i++) {
if(i > 0) {
/* Put space into the buffer. */
sldns_str_print(&s, &slen, " ");
w += sldns_str_print(&s, &slen, " ");
}
/* Ignore the first two bytes, they are the rr_data len. */
tempdata = rrset_data->rr_data[i] + 2;
tempdata_len = rrset_data->rr_len[i] - 2;
/* Save the buffer pointers. */
tempstring = s; tempstring_len = slen;
w = sldns_wire2str_ipseckey_scan(&tempdata, &tempdata_len, &s, &slen,
NULL, 0);
w_temp = sldns_wire2str_ipseckey_scan(&tempdata, &tempdata_len, &s,
&slen, NULL, 0, NULL);
/* There was an error when parsing the IPSECKEY; reset the buffer
* pointers to their previous values. */
if(w == -1){
if(w_temp == -1) {
s = tempstring; slen = tempstring_len;
} else if(w_temp > 0) {
if(!ipseckey_has_safe_characters(
tempstring, tempstring_len - slen)) {
log_err("ipsecmod: ipseckey has unsafe characters");
return 0;
}
w += w_temp;
}
}
sldns_str_print(&s, &slen, "\"");
verbose(VERB_ALGO, "ipsecmod: hook command: '%s'", str);
w += sldns_str_print(&s, &slen, "\"");
if(w >= (int)sizeof(str)) {
log_err("ipsecmod: shell command too long");
return 0;
}
verbose(VERB_ALGO, "ipsecmod: shell command: '%s'", str);
/* ipsecmod-hook should return 0 on success. */
if(system(str) != 0)
return 0;

View File

@ -0,0 +1,383 @@
/**
* \file
* This file implements the ipset module. It can handle packets by putting
* the A and AAAA addresses that are configured in unbound.conf as type
* ipset (local-zone statements) into a firewall rule IPSet. For firewall
* blacklist and whitelist usage.
*/
#include "config.h"
#include "ipset/ipset.h"
#include "util/regional.h"
#include "util/net_help.h"
#include "util/config_file.h"
#include "services/cache/dns.h"
#include "sldns/sbuffer.h"
#include "sldns/wire2str.h"
#include "sldns/parseutil.h"
#include <libmnl/libmnl.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/ipset/ip_set.h>
#define BUFF_LEN 256
/**
* Return an error
* @param qstate: our query state
* @param id: module id
* @param rcode: error code (DNS errcode).
* @return: 0 for use by caller, to make notation easy, like:
* return error_response(..).
*/
static int error_response(struct module_qstate* qstate, int id, int rcode) {
verbose(VERB_QUERY, "return error response %s",
sldns_lookup_by_id(sldns_rcodes, rcode)?
sldns_lookup_by_id(sldns_rcodes, rcode)->name:"??");
qstate->return_rcode = rcode;
qstate->return_msg = NULL;
qstate->ext_state[id] = module_finished;
return 0;
}
static struct mnl_socket * open_mnl_socket() {
struct mnl_socket *mnl;
mnl = mnl_socket_open(NETLINK_NETFILTER);
if (!mnl) {
log_err("ipset: could not open netfilter.");
return NULL;
}
if (mnl_socket_bind(mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
mnl_socket_close(mnl);
log_err("ipset: could not bind netfilter.");
return NULL;
}
return mnl;
}
static int add_to_ipset(struct mnl_socket *mnl, const char *setname, const void *ipaddr, int af) {
struct nlmsghdr *nlh;
struct nfgenmsg *nfg;
struct nlattr *nested[2];
static char buffer[BUFF_LEN];
if (strlen(setname) >= IPSET_MAXNAMELEN) {
errno = ENAMETOOLONG;
return -1;
}
if (af != AF_INET && af != AF_INET6) {
errno = EAFNOSUPPORT;
return -1;
}
nlh = mnl_nlmsg_put_header(buffer);
nlh->nlmsg_type = IPSET_CMD_ADD | (NFNL_SUBSYS_IPSET << 8);
nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL;
nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
nfg->nfgen_family = af;
nfg->version = NFNETLINK_V0;
nfg->res_id = htons(0);
mnl_attr_put_u8(nlh, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL);
mnl_attr_put(nlh, IPSET_ATTR_SETNAME, strlen(setname) + 1, setname);
nested[0] = mnl_attr_nest_start(nlh, IPSET_ATTR_DATA);
nested[1] = mnl_attr_nest_start(nlh, IPSET_ATTR_IP);
mnl_attr_put(nlh, (af == AF_INET ? IPSET_ATTR_IPADDR_IPV4 : IPSET_ATTR_IPADDR_IPV6)
| NLA_F_NET_BYTEORDER, (af == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr)), ipaddr);
mnl_attr_nest_end(nlh, nested[1]);
mnl_attr_nest_end(nlh, nested[0]);
if (mnl_socket_sendto(mnl, nlh, nlh->nlmsg_len) < 0) {
return -1;
}
return 0;
}
static void
ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl,
struct packed_rrset_data *d, const char* setname, int af,
const char* dname)
{
int ret;
size_t j, rr_len, rd_len;
uint8_t *rr_data;
/* to d->count, not d->rrsig_count, because we do not want to add the RRSIGs, only the addresses */
for (j = 0; j < d->count; j++) {
rr_len = d->rr_len[j];
rr_data = d->rr_data[j];
rd_len = sldns_read_uint16(rr_data);
if(af == AF_INET && rd_len != INET_SIZE)
continue;
if(af == AF_INET6 && rd_len != INET6_SIZE)
continue;
if (rr_len - 2 >= rd_len) {
if(verbosity >= VERB_QUERY) {
char ip[128];
if(inet_ntop(af, rr_data+2, ip, (socklen_t)sizeof(ip)) == 0)
snprintf(ip, sizeof(ip), "(inet_ntop_error)");
verbose(VERB_QUERY, "ipset: add %s to %s for %s", ip, setname, dname);
}
ret = add_to_ipset(mnl, setname, rr_data + 2, af);
if (ret < 0) {
log_err("ipset: could not add %s into %s", dname, setname);
mnl_socket_close(mnl);
ie->mnl = NULL;
break;
}
}
}
}
static int
ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie,
struct mnl_socket *mnl, struct ub_packed_rrset_key *rrset,
const char *setname, int af)
{
static char dname[BUFF_LEN];
const char *s;
int dlen, plen;
struct config_strlist *p;
struct packed_rrset_data *d;
dlen = sldns_wire2str_dname_buf(rrset->rk.dname, rrset->rk.dname_len, dname, BUFF_LEN);
if (dlen == 0) {
log_err("bad domain name");
return -1;
}
if (dname[dlen - 1] == '.') {
dlen--;
}
for (p = env->cfg->local_zones_ipset; p; p = p->next) {
plen = strlen(p->str);
if (dlen >= plen) {
s = dname + (dlen - plen);
if (strncasecmp(p->str, s, plen) == 0) {
d = (struct packed_rrset_data*)rrset->entry.data;
ipset_add_rrset_data(ie, mnl, d, setname,
af, dname);
break;
}
}
}
return 0;
}
static int ipset_update(struct module_env *env, struct dns_msg *return_msg, struct ipset_env *ie) {
struct mnl_socket *mnl;
size_t i;
const char *setname;
struct ub_packed_rrset_key *rrset;
int af;
mnl = (struct mnl_socket *)ie->mnl;
if (!mnl) {
// retry to create mnl socket
mnl = open_mnl_socket();
if (!mnl) {
return -1;
}
ie->mnl = mnl;
}
for (i = 0; i < return_msg->rep->rrset_count; ++i) {
setname = NULL;
rrset = return_msg->rep->rrsets[i];
if (rrset->rk.type == htons(LDNS_RR_TYPE_A)) {
af = AF_INET;
if ((ie->v4_enabled == 1)) {
setname = ie->name_v4;
}
} else {
af = AF_INET6;
if ((ie->v6_enabled == 1)) {
setname = ie->name_v6;
}
}
if (setname) {
if(ipset_check_zones_for_rrset(env, ie, mnl, rrset,
setname, af) == -1)
return -1;
}
}
return 0;
}
int ipset_init(struct module_env* env, int id) {
struct ipset_env *ipset_env;
ipset_env = (struct ipset_env *)calloc(1, sizeof(struct ipset_env));
if (!ipset_env) {
log_err("malloc failure");
return 0;
}
env->modinfo[id] = (void *)ipset_env;
ipset_env->mnl = NULL;
ipset_env->name_v4 = env->cfg->ipset_name_v4;
ipset_env->name_v6 = env->cfg->ipset_name_v6;
ipset_env->v4_enabled = !ipset_env->name_v4 || (strlen(ipset_env->name_v4) == 0) ? 0 : 1;
ipset_env->v6_enabled = !ipset_env->name_v6 || (strlen(ipset_env->name_v6) == 0) ? 0 : 1;
if ((ipset_env->v4_enabled < 1) && (ipset_env->v6_enabled < 1)) {
log_err("ipset: set name no configuration?");
return 0;
}
return 1;
}
void ipset_deinit(struct module_env *env, int id) {
struct mnl_socket *mnl;
struct ipset_env *ipset_env;
if (!env || !env->modinfo[id]) {
return;
}
ipset_env = (struct ipset_env *)env->modinfo[id];
mnl = (struct mnl_socket *)ipset_env->mnl;
if (mnl) {
mnl_socket_close(mnl);
ipset_env->mnl = NULL;
}
free(ipset_env);
env->modinfo[id] = NULL;
}
static int ipset_new(struct module_qstate* qstate, int id) {
struct ipset_qstate *iq = (struct ipset_qstate *)regional_alloc(
qstate->region, sizeof(struct ipset_qstate));
qstate->minfo[id] = iq;
if (!iq) {
return 0;
}
memset(iq, 0, sizeof(*iq));
/* initialise it */
/* TODO */
return 1;
}
void ipset_operate(struct module_qstate *qstate, enum module_ev event, int id,
struct outbound_entry *outbound) {
struct ipset_env *ie = (struct ipset_env *)qstate->env->modinfo[id];
struct ipset_qstate *iq = (struct ipset_qstate *)qstate->minfo[id];
verbose(VERB_QUERY, "ipset[module %d] operate: extstate:%s event:%s",
id, strextstate(qstate->ext_state[id]), strmodulevent(event));
if (iq) {
log_query_info(VERB_QUERY, "ipset operate: query", &qstate->qinfo);
}
/* perform ipset state machine */
if ((event == module_event_new || event == module_event_pass) && !iq) {
if (!ipset_new(qstate, id)) {
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
iq = (struct ipset_qstate*)qstate->minfo[id];
}
if (iq && (event == module_event_pass || event == module_event_new)) {
qstate->ext_state[id] = module_wait_module;
return;
}
if (iq && (event == module_event_moddone)) {
if (qstate->return_msg && qstate->return_msg->rep) {
ipset_update(qstate->env, qstate->return_msg, ie);
}
qstate->ext_state[id] = module_finished;
return;
}
if (iq && outbound) {
/* ipset does not need to process responses at this time
* ignore it.
ipset_process_response(qstate, iq, ie, id, outbound, event);
*/
return;
}
if (event == module_event_error) {
verbose(VERB_ALGO, "got called with event error, giving up");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
if (!iq && (event == module_event_moddone)) {
/* during priming, module done but we never started */
qstate->ext_state[id] = module_finished;
return;
}
log_err("bad event for ipset");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
void ipset_inform_super(struct module_qstate *ATTR_UNUSED(qstate),
int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super)) {
/* ipset does not use subordinate requests at this time */
verbose(VERB_ALGO, "ipset inform_super was called");
}
void ipset_clear(struct module_qstate *qstate, int id) {
struct cachedb_qstate *iq;
if (!qstate) {
return;
}
iq = (struct cachedb_qstate *)qstate->minfo[id];
if (iq) {
/* free contents of iq */
/* TODO */
}
qstate->minfo[id] = NULL;
}
size_t ipset_get_mem(struct module_env *env, int id) {
struct ipset_env *ie = (struct ipset_env *)env->modinfo[id];
if (!ie) {
return 0;
}
return sizeof(*ie);
}
/**
* The ipset function block
*/
static struct module_func_block ipset_block = {
"ipset",
&ipset_init, &ipset_deinit, &ipset_operate,
&ipset_inform_super, &ipset_clear, &ipset_get_mem
};
struct module_func_block * ipset_get_funcblock(void) {
return &ipset_block;
}

View File

@ -0,0 +1,79 @@
/**
* ipset.h
*
* Author: Kevin Chou
* Email: k9982874@gmail.com
*/
#ifndef IPSET_H
#define IPSET_H
/** \file
*
* This file implements the ipset module. It can handle packets by putting
* the A and AAAA addresses that are configured in unbound.conf as type
* ipset (local-zone statements) into a firewall rule IPSet. For firewall
* blacklist and whitelist usage.
*
* To use the IPset module, install the libmnl-dev (or libmnl-devel) package
* and configure with --enable-ipset. And compile. Then enable the ipset
* module in unbound.conf with module-config: "ipset validator iterator"
* then create it with ipset -N blacklist iphash and then add
* local-zone: "example.com." ipset
* statements for the zones where you want the addresses of the names
* looked up added to the set.
*
* Set the name of the set with
* ipset:
* name-v4: "blacklist"
* name-v6: "blacklist6"
* in unbound.conf. The set can be used in this way:
* iptables -A INPUT -m set --set blacklist src -j DROP
* ip6tables -A INPUT -m set --set blacklist6 src -j DROP
*/
#include "util/module.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ipset_env {
void* mnl;
int v4_enabled;
int v6_enabled;
const char *name_v4;
const char *name_v6;
};
struct ipset_qstate {
int dummy;
};
/** Init the ipset module */
int ipset_init(struct module_env* env, int id);
/** Deinit the ipset module */
void ipset_deinit(struct module_env* env, int id);
/** Operate on an event on a query (in qstate). */
void ipset_operate(struct module_qstate* qstate, enum module_ev event,
int id, struct outbound_entry* outbound);
/** Subordinate query done, inform this super request of its conclusion */
void ipset_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super);
/** clear the ipset query-specific contents out of qstate */
void ipset_clear(struct module_qstate* qstate, int id);
/** return memory estimate for ipset module */
size_t ipset_get_mem(struct module_env* env, int id);
/**
* Get the function block with pointers to the ipset functions
* @return the function block for "ipset".
*/
struct module_func_block* ipset_get_funcblock(void);
#ifdef __cplusplus
}
#endif
#endif /* IPSET_H */

View File

@ -84,7 +84,7 @@ struct delegpt* delegpt_copy(struct delegpt* dp, struct regional* region)
}
for(a = dp->target_list; a; a = a->next_target) {
if(!delegpt_add_addr(copy, region, &a->addr, a->addrlen,
a->bogus, a->lame, a->tls_auth_name))
a->bogus, a->lame, a->tls_auth_name, NULL))
return NULL;
}
return copy;
@ -161,7 +161,7 @@ delegpt_find_addr(struct delegpt* dp, struct sockaddr_storage* addr,
int
delegpt_add_target(struct delegpt* dp, struct regional* region,
uint8_t* name, size_t namelen, struct sockaddr_storage* addr,
socklen_t addrlen, uint8_t bogus, uint8_t lame)
socklen_t addrlen, uint8_t bogus, uint8_t lame, int* additions)
{
struct delegpt_ns* ns = delegpt_find_ns(dp, name, namelen);
log_assert(!dp->dp_type_mlc);
@ -176,13 +176,14 @@ delegpt_add_target(struct delegpt* dp, struct regional* region,
if(ns->got4 && ns->got6)
ns->resolved = 1;
}
return delegpt_add_addr(dp, region, addr, addrlen, bogus, lame, NULL);
return delegpt_add_addr(dp, region, addr, addrlen, bogus, lame, NULL,
additions);
}
int
delegpt_add_addr(struct delegpt* dp, struct regional* region,
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t bogus,
uint8_t lame, char* tls_auth_name)
uint8_t lame, char* tls_auth_name, int* additions)
{
struct delegpt_addr* a;
log_assert(!dp->dp_type_mlc);
@ -194,6 +195,8 @@ delegpt_add_addr(struct delegpt* dp, struct regional* region,
a->lame = 0;
return 1;
}
if(additions)
*additions = 1;
a = (struct delegpt_addr*)regional_alloc(region,
sizeof(struct delegpt_addr));
@ -382,10 +385,10 @@ delegpt_from_message(struct dns_msg* msg, struct regional* region)
continue;
if(ntohs(s->rk.type) == LDNS_RR_TYPE_A) {
if(!delegpt_add_rrset_A(dp, region, s, 0))
if(!delegpt_add_rrset_A(dp, region, s, 0, NULL))
return NULL;
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_AAAA) {
if(!delegpt_add_rrset_AAAA(dp, region, s, 0))
if(!delegpt_add_rrset_AAAA(dp, region, s, 0, NULL))
return NULL;
}
}
@ -416,7 +419,7 @@ delegpt_rrset_add_ns(struct delegpt* dp, struct regional* region,
int
delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
struct ub_packed_rrset_key* ak, uint8_t lame)
struct ub_packed_rrset_key* ak, uint8_t lame, int* additions)
{
struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
size_t i;
@ -432,7 +435,7 @@ delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
memmove(&sa.sin_addr, d->rr_data[i]+2, INET_SIZE);
if(!delegpt_add_target(dp, region, ak->rk.dname,
ak->rk.dname_len, (struct sockaddr_storage*)&sa,
len, (d->security==sec_status_bogus), lame))
len, (d->security==sec_status_bogus), lame, additions))
return 0;
}
return 1;
@ -440,7 +443,7 @@ delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
int
delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region,
struct ub_packed_rrset_key* ak, uint8_t lame)
struct ub_packed_rrset_key* ak, uint8_t lame, int* additions)
{
struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
size_t i;
@ -456,7 +459,7 @@ delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region,
memmove(&sa.sin6_addr, d->rr_data[i]+2, INET6_SIZE);
if(!delegpt_add_target(dp, region, ak->rk.dname,
ak->rk.dname_len, (struct sockaddr_storage*)&sa,
len, (d->security==sec_status_bogus), lame))
len, (d->security==sec_status_bogus), lame, additions))
return 0;
}
return 1;
@ -464,20 +467,33 @@ delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region,
int
delegpt_add_rrset(struct delegpt* dp, struct regional* region,
struct ub_packed_rrset_key* rrset, uint8_t lame)
struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions)
{
if(!rrset)
return 1;
if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_NS)
return delegpt_rrset_add_ns(dp, region, rrset, lame);
else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_A)
return delegpt_add_rrset_A(dp, region, rrset, lame);
return delegpt_add_rrset_A(dp, region, rrset, lame, additions);
else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA)
return delegpt_add_rrset_AAAA(dp, region, rrset, lame);
return delegpt_add_rrset_AAAA(dp, region, rrset, lame,
additions);
log_warn("Unknown rrset type added to delegpt");
return 1;
}
void delegpt_mark_neg(struct delegpt_ns* ns, uint16_t qtype)
{
if(ns) {
if(qtype == LDNS_RR_TYPE_A)
ns->got4 = 2;
else if(qtype == LDNS_RR_TYPE_AAAA)
ns->got6 = 2;
if(ns->got4 && ns->got6)
ns->resolved = 1;
}
}
void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg)
{
struct reply_info* rep = (struct reply_info*)msg->entry.data;
@ -487,14 +503,7 @@ void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg)
if(FLAGS_GET_RCODE(rep->flags) != 0 || rep->an_numrrsets == 0) {
struct delegpt_ns* ns = delegpt_find_ns(dp, msg->key.qname,
msg->key.qname_len);
if(ns) {
if(msg->key.qtype == LDNS_RR_TYPE_A)
ns->got4 = 1;
else if(msg->key.qtype == LDNS_RR_TYPE_AAAA)
ns->got6 = 1;
if(ns->got4 && ns->got6)
ns->resolved = 1;
}
delegpt_mark_neg(ns, msg->key.qtype);
}
}

View File

@ -106,9 +106,10 @@ struct delegpt_ns {
* and marked true if got4 and got6 are both true.
*/
int resolved;
/** if the ipv4 address is in the delegpt */
/** if the ipv4 address is in the delegpt, 0=not, 1=yes 2=negative,
* negative means it was done, but no content. */
uint8_t got4;
/** if the ipv6 address is in the delegpt */
/** if the ipv6 address is in the delegpt, 0=not, 1=yes 2=negative */
uint8_t got6;
/**
* If the name is parent-side only and thus dispreferred.
@ -215,11 +216,12 @@ int delegpt_rrset_add_ns(struct delegpt* dp, struct regional* regional,
* @param addrlen: the length of addr.
* @param bogus: security status for the address, pass true if bogus.
* @param lame: address is lame.
* @param additions: will be set to 1 if a new address is added
* @return false on error.
*/
int delegpt_add_target(struct delegpt* dp, struct regional* regional,
uint8_t* name, size_t namelen, struct sockaddr_storage* addr,
socklen_t addrlen, uint8_t bogus, uint8_t lame);
socklen_t addrlen, uint8_t bogus, uint8_t lame, int* additions);
/**
* Add A RRset to delegpt.
@ -227,10 +229,11 @@ int delegpt_add_target(struct delegpt* dp, struct regional* regional,
* @param regional: where to allocate the info.
* @param rrset: RRset A to add.
* @param lame: rrset is lame, disprefer it.
* @param additions: will be set to 1 if a new address is added
* @return 0 on alloc error.
*/
int delegpt_add_rrset_A(struct delegpt* dp, struct regional* regional,
struct ub_packed_rrset_key* rrset, uint8_t lame);
struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions);
/**
* Add AAAA RRset to delegpt.
@ -238,10 +241,11 @@ int delegpt_add_rrset_A(struct delegpt* dp, struct regional* regional,
* @param regional: where to allocate the info.
* @param rrset: RRset AAAA to add.
* @param lame: rrset is lame, disprefer it.
* @param additions: will be set to 1 if a new address is added
* @return 0 on alloc error.
*/
int delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* regional,
struct ub_packed_rrset_key* rrset, uint8_t lame);
struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions);
/**
* Add any RRset to delegpt.
@ -250,10 +254,11 @@ int delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* regional,
* @param regional: where to allocate the info.
* @param rrset: RRset to add, NS, A, AAAA.
* @param lame: rrset is lame, disprefer it.
* @param additions: will be set to 1 if a new address is added
* @return 0 on alloc error.
*/
int delegpt_add_rrset(struct delegpt* dp, struct regional* regional,
struct ub_packed_rrset_key* rrset, uint8_t lame);
struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions);
/**
* Add address to the delegation point. No servername is associated or checked.
@ -264,11 +269,12 @@ int delegpt_add_rrset(struct delegpt* dp, struct regional* regional,
* @param bogus: if address is bogus.
* @param lame: if address is lame.
* @param tls_auth_name: TLS authentication name (or NULL).
* @param additions: will be set to 1 if a new address is added
* @return false on error.
*/
int delegpt_add_addr(struct delegpt* dp, struct regional* regional,
struct sockaddr_storage* addr, socklen_t addrlen,
uint8_t bogus, uint8_t lame, char* tls_auth_name);
uint8_t bogus, uint8_t lame, char* tls_auth_name, int* additions);
/**
* Find NS record in name list of delegation point.
@ -341,6 +347,14 @@ size_t delegpt_count_targets(struct delegpt* dp);
struct delegpt* delegpt_from_message(struct dns_msg* msg,
struct regional* regional);
/**
* Mark negative return in delegation point for specific nameserver.
* sets the got4 or got6 to negative, updates the ns->resolved.
* @param ns: the nameserver in the delegpt.
* @param qtype: A or AAAA (host order).
*/
void delegpt_mark_neg(struct delegpt_ns* ns, uint16_t qtype);
/**
* Add negative message to delegation point.
* @param dp: delegation point.

View File

@ -239,7 +239,7 @@ read_fwds_addr(struct config_stub* s, struct delegpt* dp)
s->name, p->str);
return 0;
}
#ifndef HAVE_SSL_SET1_HOST
#if ! defined(HAVE_SSL_SET1_HOST) && ! defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
if(tls_auth_name)
log_err("no name verification functionality in "
"ssl library, ignored name for %s", p->str);

View File

@ -252,7 +252,7 @@ read_stubs_addr(struct config_stub* s, struct delegpt* dp)
s->name, p->str);
return 0;
}
#ifndef HAVE_SSL_SET1_HOST
#if ! defined(HAVE_SSL_SET1_HOST) && ! defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
if(auth_name)
log_err("no name verification functionality in "
"ssl library, ignored name for %s", p->str);

View File

@ -185,8 +185,9 @@ mark_additional_rrset(sldns_buffer* pkt, struct msg_parse* msg,
/** Get target name of a CNAME */
static int
parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname,
size_t* snamelen)
size_t* snamelen, sldns_buffer* pkt)
{
size_t oldpos, dlen;
if(rrset->rr_count != 1) {
struct rr_parse* sig;
verbose(VERB_ALGO, "Found CNAME rrset with "
@ -204,6 +205,19 @@ parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname,
*sname = rrset->rr_first->ttl_data + sizeof(uint32_t)
+ sizeof(uint16_t); /* skip ttl, rdatalen */
*snamelen = rrset->rr_first->size - sizeof(uint16_t);
if(rrset->rr_first->outside_packet) {
if(!dname_valid(*sname, *snamelen))
return 0;
return 1;
}
oldpos = sldns_buffer_position(pkt);
sldns_buffer_set_position(pkt, (size_t)(*sname - sldns_buffer_begin(pkt)));
dlen = pkt_dname_len(pkt);
sldns_buffer_set_position(pkt, oldpos);
if(dlen == 0)
return 0; /* parse fail on the rdata name */
*snamelen = dlen;
return 1;
}
@ -215,8 +229,12 @@ synth_cname(uint8_t* qname, size_t qnamelen, struct rrset_parse* dname_rrset,
/* we already know that sname is a strict subdomain of DNAME owner */
uint8_t* dtarg = NULL;
size_t dtarglen;
if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen))
if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen, pkt))
return 0;
if(qnamelen <= dname_rrset->dname_len)
return 0;
if(qnamelen == 0)
return 0;
log_assert(qnamelen > dname_rrset->dname_len);
/* DNAME from com. to net. with qname example.com. -> example.net. */
/* so: \3com\0 to \3net\0 and qname \7example\3com\0 */
@ -316,6 +334,18 @@ sub_of_pkt(sldns_buffer* pkt, uint8_t* zone, uint8_t* comprname)
return dname_subdomain_c(zone, buf);
}
/** Check if there are SOA records in the authority section (negative) */
static int
soa_in_auth(struct msg_parse* msg)
{
struct rrset_parse* rrset;
for(rrset = msg->rrset_first; rrset; rrset = rrset->rrset_all_next)
if(rrset->type == LDNS_RR_TYPE_SOA &&
rrset->section == LDNS_SECTION_AUTHORITY)
return 1;
return 0;
}
/**
* This routine normalizes a response. This includes removing "irrelevant"
* records from the answer and additional sections and (re)synthesizing
@ -372,7 +402,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
/* check next cname */
uint8_t* t = NULL;
size_t tlen = 0;
if(!parse_get_cname_target(nx, &t, &tlen))
if(!parse_get_cname_target(nx, &t, &tlen, pkt))
return 0;
if(dname_pkt_compare(pkt, alias, t) == 0) {
/* it's OK and better capitalized */
@ -423,7 +453,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
size_t tlen = 0;
if(synth_cname(sname, snamelen, nx, alias,
&aliaslen, pkt) &&
parse_get_cname_target(rrset, &t, &tlen) &&
parse_get_cname_target(rrset, &t, &tlen, pkt) &&
dname_pkt_compare(pkt, alias, t) == 0) {
/* the synthesized CNAME equals the
* current CNAME. This CNAME is the
@ -444,7 +474,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
}
/* move to next name in CNAME chain */
if(!parse_get_cname_target(rrset, &sname, &snamelen))
if(!parse_get_cname_target(rrset, &sname, &snamelen, pkt))
return 0;
prev = rrset;
rrset = rrset->rrset_all_next;
@ -497,6 +527,19 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
"RRset:", pkt, msg, prev, &rrset);
continue;
}
/* we don't want NS sets for NXDOMAIN answers,
* because they could contain poisonous contents,
* from. eg. fragmentation attacks, inserted after
* long RRSIGs in the packet get to the packet
* border and such */
/* also for NODATA answers */
if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN ||
(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR
&& soa_in_auth(msg) && msg->an_rrsets == 0)) {
remove_rrset("normalize: removing irrelevant "
"RRset:", pkt, msg, prev, &rrset);
continue;
}
if(nsset == NULL) {
nsset = rrset;
} else {
@ -595,18 +638,6 @@ store_rrset(sldns_buffer* pkt, struct msg_parse* msg, struct module_env* env,
(void)rrset_cache_update(env->rrset_cache, &ref, env->alloc, now);
}
/** Check if there are SOA records in the authority section (negative) */
static int
soa_in_auth(struct msg_parse* msg)
{
struct rrset_parse* rrset;
for(rrset = msg->rrset_first; rrset; rrset = rrset->rrset_all_next)
if(rrset->type == LDNS_RR_TYPE_SOA &&
rrset->section == LDNS_SECTION_AUTHORITY)
return 1;
return 0;
}
/**
* Check if right hand name in NSEC is within zone
* @param rrset: the NSEC rrset

View File

@ -282,10 +282,13 @@ iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env,
static int
iter_fill_rtt(struct iter_env* iter_env, struct module_env* env,
uint8_t* name, size_t namelen, uint16_t qtype, time_t now,
struct delegpt* dp, int* best_rtt, struct sock_list* blacklist)
struct delegpt* dp, int* best_rtt, struct sock_list* blacklist,
size_t* num_suitable_results)
{
int got_it = 0;
struct delegpt_addr* a;
*num_suitable_results = 0;
if(dp->bogus)
return 0; /* NS bogus, all bogus, nothing found */
for(a=dp->result_list; a; a = a->next_result) {
@ -301,11 +304,58 @@ iter_fill_rtt(struct iter_env* iter_env, struct module_env* env,
} else if(a->sel_rtt < *best_rtt) {
*best_rtt = a->sel_rtt;
}
(*num_suitable_results)++;
}
}
return got_it;
}
/** compare two rtts, return -1, 0 or 1 */
static int
rtt_compare(const void* x, const void* y)
{
if(*(int*)x == *(int*)y)
return 0;
if(*(int*)x > *(int*)y)
return 1;
return -1;
}
/** get RTT for the Nth fastest server */
static int
nth_rtt(struct delegpt_addr* result_list, size_t num_results, size_t n)
{
int rtt_band;
size_t i;
int* rtt_list, *rtt_index;
if(num_results < 1 || n >= num_results) {
return -1;
}
rtt_list = calloc(num_results, sizeof(int));
if(!rtt_list) {
log_err("malloc failure: allocating rtt_list");
return -1;
}
rtt_index = rtt_list;
for(i=0; i<num_results && result_list; i++) {
if(result_list->sel_rtt != -1) {
*rtt_index = result_list->sel_rtt;
rtt_index++;
}
result_list=result_list->next_result;
}
qsort(rtt_list, num_results, sizeof(*rtt_list), rtt_compare);
log_assert(n > 0);
rtt_band = rtt_list[n-1];
free(rtt_list);
return rtt_band;
}
/** filter the address list, putting best targets at front,
* returns number of best targets (or 0, no suitable targets) */
static int
@ -314,12 +364,13 @@ iter_filter_order(struct iter_env* iter_env, struct module_env* env,
struct delegpt* dp, int* selected_rtt, int open_target,
struct sock_list* blacklist, time_t prefetch)
{
int got_num = 0, low_rtt = 0, swap_to_front, rtt_band = RTT_BAND;
int got_num = 0, low_rtt = 0, swap_to_front, rtt_band = RTT_BAND, nth;
size_t num_results;
struct delegpt_addr* a, *n, *prev=NULL;
/* fillup sel_rtt and find best rtt in the bunch */
got_num = iter_fill_rtt(iter_env, env, name, namelen, qtype, now, dp,
&low_rtt, blacklist);
&low_rtt, blacklist, &num_results);
if(got_num == 0)
return 0;
if(low_rtt >= USEFUL_SERVER_TOP_TIMEOUT &&
@ -329,14 +380,19 @@ iter_filter_order(struct iter_env* iter_env, struct module_env* env,
return 0 to force the caller to fetch more */
}
if(env->cfg->low_rtt_permil != 0 && prefetch == 0 &&
low_rtt < env->cfg->low_rtt &&
ub_random_max(env->rnd, 1000) < env->cfg->low_rtt_permil) {
if(env->cfg->fast_server_permil != 0 && prefetch == 0 &&
num_results > env->cfg->fast_server_num &&
ub_random_max(env->rnd, 1000) < env->cfg->fast_server_permil) {
/* the query is not prefetch, but for a downstream client,
* there is a low_rtt (fast) server. We choose that x% of the
* time */
/* pick rtt numbers from 0..LOWBAND_RTT */
rtt_band = env->cfg->low_rtt - low_rtt;
* there are more servers available then the fastest N we want
* to choose from. Limit our choice to the fastest servers. */
nth = nth_rtt(dp->result_list, num_results,
env->cfg->fast_server_num);
if(nth > 0) {
rtt_band = nth - low_rtt;
if(rtt_band > RTT_BAND)
rtt_band = RTT_BAND;
}
}
got_num = 0;
@ -826,10 +882,35 @@ rrset_equal(struct ub_packed_rrset_key* k1, struct ub_packed_rrset_key* k2)
return 1;
}
/** compare rrsets and sort canonically. Compares rrset name, type, class.
* return 0 if equal, +1 if x > y, and -1 if x < y.
*/
static int
rrset_canonical_sort_cmp(const void* x, const void* y)
{
struct ub_packed_rrset_key* rrx = *(struct ub_packed_rrset_key**)x;
struct ub_packed_rrset_key* rry = *(struct ub_packed_rrset_key**)y;
int r = dname_canonical_compare(rrx->rk.dname, rry->rk.dname);
if(r != 0)
return r;
if(rrx->rk.type != rry->rk.type) {
if(ntohs(rrx->rk.type) > ntohs(rry->rk.type))
return 1;
else return -1;
}
if(rrx->rk.rrset_class != rry->rk.rrset_class) {
if(ntohs(rrx->rk.rrset_class) > ntohs(rry->rk.rrset_class))
return 1;
else return -1;
}
return 0;
}
int
reply_equal(struct reply_info* p, struct reply_info* q, struct regional* region)
{
size_t i;
struct ub_packed_rrset_key** sorted_p, **sorted_q;
if(p->flags != q->flags ||
p->qdcount != q->qdcount ||
/* do not check TTL, this may differ */
@ -843,16 +924,43 @@ reply_equal(struct reply_info* p, struct reply_info* q, struct regional* region)
p->ar_numrrsets != q->ar_numrrsets ||
p->rrset_count != q->rrset_count)
return 0;
/* sort the rrsets in the authority and additional sections before
* compare, the query and answer sections are ordered in the sequence
* they should have (eg. one after the other for aliases). */
sorted_p = (struct ub_packed_rrset_key**)regional_alloc_init(
region, p->rrsets, sizeof(*sorted_p)*p->rrset_count);
if(!sorted_p) return 0;
log_assert(p->an_numrrsets + p->ns_numrrsets + p->ar_numrrsets <=
p->rrset_count);
qsort(sorted_p + p->an_numrrsets, p->ns_numrrsets,
sizeof(*sorted_p), rrset_canonical_sort_cmp);
qsort(sorted_p + p->an_numrrsets + p->ns_numrrsets, p->ar_numrrsets,
sizeof(*sorted_p), rrset_canonical_sort_cmp);
sorted_q = (struct ub_packed_rrset_key**)regional_alloc_init(
region, q->rrsets, sizeof(*sorted_q)*q->rrset_count);
if(!sorted_q) {
regional_free_all(region);
return 0;
}
log_assert(q->an_numrrsets + q->ns_numrrsets + q->ar_numrrsets <=
q->rrset_count);
qsort(sorted_q + q->an_numrrsets, q->ns_numrrsets,
sizeof(*sorted_q), rrset_canonical_sort_cmp);
qsort(sorted_q + q->an_numrrsets + q->ns_numrrsets, q->ar_numrrsets,
sizeof(*sorted_q), rrset_canonical_sort_cmp);
/* compare the rrsets */
for(i=0; i<p->rrset_count; i++) {
if(!rrset_equal(p->rrsets[i], q->rrsets[i])) {
if(!rrset_canonical_equal(region, p->rrsets[i],
q->rrsets[i])) {
if(!rrset_equal(sorted_p[i], sorted_q[i])) {
if(!rrset_canonical_equal(region, sorted_p[i],
sorted_q[i])) {
regional_free_all(region);
return 0;
}
regional_free_all(region);
}
}
regional_free_all(region);
return 1;
}
@ -1034,7 +1142,7 @@ int iter_lookup_parent_glue_from_cache(struct module_env* env,
log_rrset_key(VERB_ALGO, "found parent-side", akey);
ns->done_pside4 = 1;
/* a negative-cache-element has no addresses it adds */
if(!delegpt_add_rrset_A(dp, region, akey, 1))
if(!delegpt_add_rrset_A(dp, region, akey, 1, NULL))
log_err("malloc failure in lookup_parent_glue");
lock_rw_unlock(&akey->entry.lock);
}
@ -1046,7 +1154,7 @@ int iter_lookup_parent_glue_from_cache(struct module_env* env,
log_rrset_key(VERB_ALGO, "found parent-side", akey);
ns->done_pside6 = 1;
/* a negative-cache-element has no addresses it adds */
if(!delegpt_add_rrset_AAAA(dp, region, akey, 1))
if(!delegpt_add_rrset_AAAA(dp, region, akey, 1, NULL))
log_err("malloc failure in lookup_parent_glue");
lock_rw_unlock(&akey->entry.lock);
}
@ -1103,6 +1211,19 @@ iter_scrub_ds(struct dns_msg* msg, struct ub_packed_rrset_key* ns, uint8_t* z)
}
}
void
iter_scrub_nxdomain(struct dns_msg* msg)
{
if(msg->rep->an_numrrsets == 0)
return;
memmove(msg->rep->rrsets, msg->rep->rrsets+msg->rep->an_numrrsets,
sizeof(struct ub_packed_rrset_key*) *
(msg->rep->rrset_count-msg->rep->an_numrrsets));
msg->rep->rrset_count -= msg->rep->an_numrrsets;
msg->rep->an_numrrsets = 0;
}
void iter_dec_attempts(struct delegpt* dp, int d)
{
struct delegpt_addr* a;
@ -1210,3 +1331,50 @@ int iter_dp_cangodown(struct query_info* qinfo, struct delegpt* dp)
return 0;
return 1;
}
int
iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf)
{
struct iter_hints_stub *stub;
struct delegpt *dp;
/* Check for stub. */
stub = hints_lookup_stub(qstate->env->hints, qinf->qname,
qinf->qclass, NULL);
dp = forwards_lookup(qstate->env->fwds, qinf->qname, qinf->qclass);
/* see if forward or stub is more pertinent */
if(stub && stub->dp && dp) {
if(dname_strict_subdomain(dp->name, dp->namelabs,
stub->dp->name, stub->dp->namelabs)) {
stub = NULL; /* ignore stub, forward is lower */
} else {
dp = NULL; /* ignore forward, stub is lower */
}
}
/* check stub */
if (stub != NULL && stub->dp != NULL) {
if(stub->dp->no_cache) {
char qname[255+1];
char dpname[255+1];
dname_str(qinf->qname, qname);
dname_str(stub->dp->name, dpname);
verbose(VERB_ALGO, "stub for %s %s has no_cache", qname, dpname);
}
return (stub->dp->no_cache);
}
/* Check for forward. */
if (dp) {
if(dp->no_cache) {
char qname[255+1];
char dpname[255+1];
dname_str(qinf->qname, qname);
dname_str(dp->name, dpname);
verbose(VERB_ALGO, "forward for %s %s has no_cache", qname, dpname);
}
return (dp->no_cache);
}
return 0;
}

View File

@ -334,6 +334,13 @@ int iter_get_next_root(struct iter_hints* hints, struct iter_forwards* fwd,
void iter_scrub_ds(struct dns_msg* msg, struct ub_packed_rrset_key* ns,
uint8_t* z);
/**
* Prepare an NXDOMAIN message to be used for a subdomain answer by removing all
* RRs from the ANSWER section.
* @param msg: the response to scrub.
*/
void iter_scrub_nxdomain(struct dns_msg* msg);
/**
* Remove query attempts from all available ips. For 0x20.
* @param dp: delegpt.
@ -369,4 +376,13 @@ int iter_ds_toolow(struct dns_msg* msg, struct delegpt* dp);
*/
int iter_dp_cangodown(struct query_info* qinfo, struct delegpt* dp);
/**
* Lookup if no_cache is set in stub or fwd.
* @param qstate: query state with env with hints and fwds.
* @param qinf: query name to lookup for.
* @return true if no_cache is set in stub or fwd.
*/
int iter_stub_fwd_no_cache(struct module_qstate *qstate,
struct query_info *qinf);
#endif /* ITERATOR_ITER_UTILS_H */

View File

@ -69,6 +69,11 @@
#include "sldns/parseutil.h"
#include "sldns/sbuffer.h"
/* in msec */
int UNKNOWN_SERVER_NICENESS = 376;
static void target_count_increase_nx(struct iter_qstate* iq, int num);
int
iter_init(struct module_env* env, int id)
{
@ -147,6 +152,7 @@ iter_new(struct module_qstate* qstate, int id)
iq->sent_count = 0;
iq->ratelimit_ok = 0;
iq->target_count = NULL;
iq->dp_target_count = 0;
iq->wait_priming_stub = 0;
iq->refetch_glue = 0;
iq->dnssec_expected = 0;
@ -218,6 +224,7 @@ final_state(struct iter_qstate* iq)
static void
error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
{
struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
struct iter_qstate* super_iq = (struct iter_qstate*)super->minfo[id];
if(qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
@ -243,7 +250,11 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
super->region, super_iq->dp))
log_err("out of memory adding missing");
}
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
dpns->resolved = 1; /* mark as failed */
if((dpns->got4 == 2 || !ie->supports_ipv4) &&
(dpns->got6 == 2 || !ie->supports_ipv6))
target_count_increase_nx(super_iq, 1);
}
if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) {
/* prime failed to get delegation */
@ -324,6 +335,29 @@ error_response_cache(struct module_qstate* qstate, int id, int rcode)
/* serving expired contents, but nothing is cached
* at all, so the servfail cache entry is useful
* (stops waste of time on this servfail NORR_TTL) */
} else {
/* don't overwrite existing (non-expired) data in
* cache with a servfail */
struct msgreply_entry* msg;
if((msg=msg_cache_lookup(qstate->env,
qstate->qinfo.qname, qstate->qinfo.qname_len,
qstate->qinfo.qtype, qstate->qinfo.qclass,
qstate->query_flags, *qstate->env->now, 0))
!= NULL) {
struct reply_info* rep = (struct reply_info*)
msg->entry.data;
if(FLAGS_GET_RCODE(rep->flags) ==
LDNS_RCODE_NOERROR ||
FLAGS_GET_RCODE(rep->flags) ==
LDNS_RCODE_NXDOMAIN) {
/* we have a good entry,
* don't overwrite */
lock_rw_unlock(&msg->entry.lock);
return error_response(qstate, id, rcode);
}
lock_rw_unlock(&msg->entry.lock);
}
}
memset(&err, 0, sizeof(err));
err.flags = (uint16_t)(BIT_QR | BIT_RA);
@ -383,6 +417,8 @@ iter_prepend(struct iter_qstate* iq, struct dns_msg* msg,
num_an = 0;
for(p = iq->an_prepend_list; p; p = p->next) {
sets[num_an++] = p->rrset;
if(ub_packed_rrset_ttl(p->rrset) < msg->rep->ttl)
msg->rep->ttl = ub_packed_rrset_ttl(p->rrset);
}
memcpy(sets+num_an, msg->rep->rrsets, msg->rep->an_numrrsets *
sizeof(struct ub_packed_rrset_key*));
@ -395,6 +431,8 @@ iter_prepend(struct iter_qstate* iq, struct dns_msg* msg,
msg->rep->ns_numrrsets, p->rrset))
continue;
sets[msg->rep->an_numrrsets + num_an + num_ns++] = p->rrset;
if(ub_packed_rrset_ttl(p->rrset) < msg->rep->ttl)
msg->rep->ttl = ub_packed_rrset_ttl(p->rrset);
}
memcpy(sets + num_an + msg->rep->an_numrrsets + num_ns,
msg->rep->rrsets + msg->rep->an_numrrsets,
@ -591,7 +629,7 @@ static void
target_count_create(struct iter_qstate* iq)
{
if(!iq->target_count) {
iq->target_count = (int*)calloc(2, sizeof(int));
iq->target_count = (int*)calloc(3, sizeof(int));
/* if calloc fails we simply do not track this number */
if(iq->target_count)
iq->target_count[0] = 1;
@ -604,6 +642,15 @@ target_count_increase(struct iter_qstate* iq, int num)
target_count_create(iq);
if(iq->target_count)
iq->target_count[1] += num;
iq->dp_target_count++;
}
static void
target_count_increase_nx(struct iter_qstate* iq, int num)
{
target_count_create(iq);
if(iq->target_count)
iq->target_count[2] += num;
}
/**
@ -626,13 +673,15 @@ target_count_increase(struct iter_qstate* iq, int num)
* @param subq_ret: if newly allocated, the subquerystate, or NULL if it does
* not need initialisation.
* @param v: if true, validation is done on the subquery.
* @param detached: true if this qstate should not attach to the subquery
* @return false on error (malloc).
*/
static int
generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
uint16_t qclass, struct module_qstate* qstate, int id,
struct iter_qstate* iq, enum iter_state initial_state,
enum iter_state finalstate, struct module_qstate** subq_ret, int v)
enum iter_state finalstate, struct module_qstate** subq_ret, int v,
int detached)
{
struct module_qstate* subq = NULL;
struct iter_qstate* subiq = NULL;
@ -659,11 +708,23 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
valrec = 1;
}
/* attach subquery, lookup existing or make a new one */
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(detached) {
struct mesh_state* sub = NULL;
fptr_ok(fptr_whitelist_modenv_add_sub(
qstate->env->add_sub));
if(!(*qstate->env->add_sub)(qstate, &qinf,
qflags, prime, valrec, &subq, &sub)){
return 0;
}
}
else {
/* attach subquery, lookup existing or make a new one */
fptr_ok(fptr_whitelist_modenv_attach_sub(
qstate->env->attach_sub));
if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime,
valrec, &subq)) {
return 0;
}
}
*subq_ret = subq;
if(subq) {
@ -686,6 +747,7 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
subiq->target_count = iq->target_count;
if(iq->target_count)
iq->target_count[0] ++; /* extra reference */
subiq->dp_target_count = 0;
subiq->num_current_queries = 0;
subiq->depth = iq->depth+1;
outbound_list_init(&subiq->outlist);
@ -729,7 +791,7 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id,
* the normal INIT state logic (which would cause an infloop). */
if(!generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS,
qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE,
&subq, 0)) {
&subq, 0, 0)) {
verbose(VERB_ALGO, "could not prime root");
return 0;
}
@ -820,7 +882,7 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id,
* redundant INIT state processing. */
if(!generate_sub_request(stub_dp->name, stub_dp->namelen,
LDNS_RR_TYPE_NS, qclass, qstate, id, iq,
QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0)) {
QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0, 0)) {
verbose(VERB_ALGO, "could not prime stub");
errinf(qstate, "could not generate lookup for stub prime");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
@ -995,7 +1057,7 @@ generate_a_aaaa_check(struct module_qstate* qstate, struct iter_qstate* iq,
if(!generate_sub_request(s->rk.dname, s->rk.dname_len,
ntohs(s->rk.type), ntohs(s->rk.rrset_class),
qstate, id, iq,
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) {
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) {
verbose(VERB_ALGO, "could not generate addr check");
return;
}
@ -1039,7 +1101,7 @@ generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id)
iq->dp->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);
if(!generate_sub_request(iq->dp->name, iq->dp->namelen,
LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq,
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) {
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) {
verbose(VERB_ALGO, "could not generate ns check");
return;
}
@ -1096,7 +1158,7 @@ generate_dnskey_prefetch(struct module_qstate* qstate,
iq->dp->name, LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass);
if(!generate_sub_request(iq->dp->name, iq->dp->namelen,
LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass, qstate, id, iq,
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) {
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) {
/* we'll be slower, but it'll work */
verbose(VERB_ALGO, "could not generate dnskey prefetch");
return;
@ -1144,53 +1206,6 @@ forward_request(struct module_qstate* qstate, struct iter_qstate* iq)
return 1;
}
static int
iter_stub_fwd_no_cache(struct module_qstate *qstate, struct iter_qstate *iq)
{
struct iter_hints_stub *stub;
struct delegpt *dp;
/* Check for stub. */
stub = hints_lookup_stub(qstate->env->hints, iq->qchase.qname,
iq->qchase.qclass, iq->dp);
dp = forwards_lookup(qstate->env->fwds, iq->qchase.qname, iq->qchase.qclass);
/* see if forward or stub is more pertinent */
if(stub && stub->dp && dp) {
if(dname_strict_subdomain(dp->name, dp->namelabs,
stub->dp->name, stub->dp->namelabs)) {
stub = NULL; /* ignore stub, forward is lower */
} else {
dp = NULL; /* ignore forward, stub is lower */
}
}
/* check stub */
if (stub != NULL && stub->dp != NULL) {
if(stub->dp->no_cache) {
char qname[255+1];
char dpname[255+1];
dname_str(iq->qchase.qname, qname);
dname_str(stub->dp->name, dpname);
verbose(VERB_ALGO, "stub for %s %s has no_cache", qname, dpname);
}
return (stub->dp->no_cache);
}
/* Check for forward. */
if (dp) {
if(dp->no_cache) {
char qname[255+1];
char dpname[255+1];
dname_str(iq->qchase.qname, qname);
dname_str(dp->name, dpname);
verbose(VERB_ALGO, "forward for %s %s has no_cache", qname, dpname);
}
return (dp->no_cache);
}
return 0;
}
/**
* Process the initial part of the request handling. This state roughly
* corresponds to resolver algorithms steps 1 (find answer in cache) and 2
@ -1268,7 +1283,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
/* This either results in a query restart (CNAME cache response), a
* terminating response (ANSWER), or a cache miss (null). */
if (iter_stub_fwd_no_cache(qstate, iq)) {
if (iter_stub_fwd_no_cache(qstate, &iq->qchase)) {
/* Asked to not query cache. */
verbose(VERB_ALGO, "no-cache set, going to the network");
qstate->no_cache_lookup = 1;
@ -1332,6 +1347,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
iq->refetch_glue = 0;
iq->query_restart_count++;
iq->sent_count = 0;
iq->dp_target_count = 0;
sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
if(qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
@ -1469,7 +1485,8 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
* now will also exceed the rate, keeping cache fresh */
(void)infra_ratelimit_inc(qstate->env->infra_cache,
iq->dp->name, iq->dp->namelen,
*qstate->env->now);
*qstate->env->now, &qstate->qinfo,
qstate->reply);
/* see if we are passed through with slip factor */
if(qstate->env->cfg->ratelimit_factor != 0 &&
ub_random_max(qstate->env->rnd,
@ -1709,7 +1726,7 @@ generate_parentside_target_query(struct module_qstate* qstate,
{
struct module_qstate* subq;
if(!generate_sub_request(name, namelen, qtype, qclass, qstate,
id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0))
id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0))
return 0;
if(subq) {
struct iter_qstate* subiq =
@ -1760,7 +1777,7 @@ generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq,
{
struct module_qstate* subq;
if(!generate_sub_request(name, namelen, qtype, qclass, qstate,
id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0))
id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0))
return 0;
log_nametypeclass(VERB_QUERY, "new target", name, qtype, qclass);
return 1;
@ -1799,6 +1816,14 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
"number of glue fetches %d", s, iq->target_count[1]);
return 0;
}
if(iq->dp_target_count > MAX_DP_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
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",
s, iq->dp_target_count);
return 0;
}
iter_mark_cycle_targets(qstate, iq->dp);
missing = (int)delegpt_count_missing_targets(iq->dp);
@ -1903,7 +1928,6 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
struct delegpt* p = hints_lookup_root(qstate->env->hints,
iq->qchase.qclass);
if(p) {
struct delegpt_ns* ns;
struct delegpt_addr* a;
iq->chase_flags &= ~BIT_RD; /* go to authorities */
for(ns = p->nslist; ns; ns=ns->next) {
@ -1913,7 +1937,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
for(a = p->target_list; a; a=a->next_target) {
(void)delegpt_add_addr(iq->dp, qstate->region,
&a->addr, a->addrlen, a->bogus,
a->lame, a->tls_auth_name);
a->lame, a->tls_auth_name, NULL);
}
}
iq->dp->has_parent_side_NS = 1;
@ -1930,6 +1954,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
iq->refetch_glue = 1;
iq->query_restart_count++;
iq->sent_count = 0;
iq->dp_target_count = 0;
if(qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
return next_state(iq, INIT_REQUEST_STATE);
@ -2095,7 +2120,7 @@ processDSNSFind(struct module_qstate* qstate, struct iter_qstate* iq, int id)
iq->dsns_point, LDNS_RR_TYPE_NS, iq->qchase.qclass);
if(!generate_sub_request(iq->dsns_point, iq->dsns_point_len,
LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq,
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) {
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) {
errinf_dname(qstate, "for DS query parent-child nameserver search, could not generate NS lookup for", iq->dsns_point);
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
@ -2127,6 +2152,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
struct delegpt_addr* target;
struct outbound_entry* outq;
int auth_fallback = 0;
uint8_t* qout_orig = NULL;
size_t qout_orig_len = 0;
/* NOTE: a request will encounter this state for each target it
* needs to send a query to. That is, at least one per referral,
@ -2151,6 +2178,13 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
errinf(qstate, "exceeded the maximum number of sends");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->target_count && iq->target_count[2] > MAX_TARGET_NX) {
verbose(VERB_QUERY, "request has exceeded the maximum "
" number of nxdomain nameserver lookups with %d",
iq->target_count[2]);
errinf(qstate, "exceeded the maximum nameserver nxdomains");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* Make sure we have a delegation point, otherwise priming failed
* or another failure occurred */
@ -2200,6 +2234,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
int labdiff = qchaselabs -
dname_count_labels(iq->qinfo_out.qname);
qout_orig = iq->qinfo_out.qname;
qout_orig_len = iq->qinfo_out.qname_len;
iq->qinfo_out.qname = iq->qchase.qname;
iq->qinfo_out.qname_len = iq->qchase.qname_len;
iq->minimise_count++;
@ -2253,12 +2289,41 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
iq->qinfo_out.qtype, iq->qinfo_out.qclass,
qstate->query_flags, qstate->region,
qstate->env->scratch, 0);
if(msg && msg->rep->an_numrrsets == 0
&& FLAGS_GET_RCODE(msg->rep->flags) ==
if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
LDNS_RCODE_NOERROR)
/* no need to send query if it is already
* cached as NOERROR/NODATA */
* cached as NOERROR */
return 1;
if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
LDNS_RCODE_NXDOMAIN &&
qstate->env->need_to_validate &&
qstate->env->cfg->harden_below_nxdomain) {
if(msg->rep->security == sec_status_secure) {
iq->response = msg;
return final_state(iq);
}
if(msg->rep->security == sec_status_unchecked) {
struct module_qstate* subq = NULL;
if(!generate_sub_request(
iq->qinfo_out.qname,
iq->qinfo_out.qname_len,
iq->qinfo_out.qtype,
iq->qinfo_out.qclass,
qstate, id, iq,
INIT_REQUEST_STATE,
FINISHED_STATE, &subq, 1, 1))
verbose(VERB_ALGO,
"could not validate NXDOMAIN "
"response");
}
}
if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
LDNS_RCODE_NXDOMAIN) {
/* return and add a label in the next
* minimisation iteration.
*/
return 1;
}
}
}
if(iq->minimisation_state == SKIP_MINIMISE_STATE) {
@ -2320,7 +2385,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
errinf(qstate, "auth zone lookup failed, fallback is off");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->dp && iq->dp->auth_dp) {
if(iq->dp->auth_dp) {
/* we wanted to fallback, but had no delegpt, only the
* auth zone generated delegpt, create an actual one */
iq->auth_zone_avoid = 1;
@ -2334,6 +2399,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
* generated query will immediately be discarded due to depth and
* that servfail is cached, which is not good as opportunism goes. */
if(iq->depth < ie->max_dependency_depth
&& iq->num_target_queries == 0
&& (!iq->target_count || iq->target_count[2]==0)
&& iq->sent_count < TARGET_FETCH_STOP) {
tf_policy = ie->target_fetch_policy[iq->depth];
}
@ -2352,6 +2419,13 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
/* wait to get all targets, we want to try em */
verbose(VERB_ALGO, "wait for all targets for fallback");
qstate->ext_state[id] = module_wait_reply;
/* undo qname minimise step because we'll get back here
* to do it again */
if(qout_orig && iq->minimise_count > 0) {
iq->minimise_count--;
iq->qinfo_out.qname = qout_orig;
iq->qinfo_out.qname_len = qout_orig_len;
}
return 0;
}
/* did we do enough fallback queries already? */
@ -2372,6 +2446,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
iq->num_current_queries++; /* RespState decrements it*/
iq->referral_count++; /* make sure we don't loop */
iq->sent_count = 0;
iq->dp_target_count = 0;
iq->state = QUERY_RESP_STATE;
return 1;
}
@ -2459,6 +2534,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
iq->num_current_queries++; /* RespState decrements it*/
iq->referral_count++; /* make sure we don't loop */
iq->sent_count = 0;
iq->dp_target_count = 0;
iq->state = QUERY_RESP_STATE;
return 1;
}
@ -2485,13 +2561,21 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
iq->num_current_queries);
qstate->ext_state[id] = module_wait_reply;
}
/* undo qname minimise step because we'll get back here
* to do it again */
if(qout_orig && iq->minimise_count > 0) {
iq->minimise_count--;
iq->qinfo_out.qname = qout_orig;
iq->qinfo_out.qname_len = qout_orig_len;
}
return 0;
}
/* if not forwarding, check ratelimits per delegationpoint name */
if(!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok) {
if(!infra_ratelimit_inc(qstate->env->infra_cache, iq->dp->name,
iq->dp->namelen, *qstate->env->now)) {
iq->dp->namelen, *qstate->env->now, &qstate->qinfo,
qstate->reply)) {
lock_basic_lock(&ie->queries_ratelimit_lock);
ie->num_queries_ratelimited++;
lock_basic_unlock(&ie->queries_ratelimit_lock);
@ -2720,8 +2804,15 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
&& !(iq->chase_flags & BIT_RD)) {
if(FLAGS_GET_RCODE(iq->response->rep->flags) !=
LDNS_RCODE_NOERROR) {
if(qstate->env->cfg->qname_minimisation_strict)
return final_state(iq);
if(qstate->env->cfg->qname_minimisation_strict) {
if(FLAGS_GET_RCODE(iq->response->rep->flags) ==
LDNS_RCODE_NXDOMAIN) {
iter_scrub_nxdomain(iq->response);
return final_state(iq);
}
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
/* Best effort qname-minimisation.
* Stop minimising and send full query when
* RCODE is not NOERROR. */
@ -2738,7 +2829,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
/* Make subrequest to validate intermediate
* NXDOMAIN if harden-below-nxdomain is
* enabled. */
if(qstate->env->cfg->harden_below_nxdomain) {
if(qstate->env->cfg->harden_below_nxdomain &&
qstate->env->need_to_validate) {
struct module_qstate* subq = NULL;
log_query_info(VERB_QUERY,
"schedule NXDOMAIN validation:",
@ -2750,16 +2842,10 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
iq->response->qinfo.qclass,
qstate, id, iq,
INIT_REQUEST_STATE,
FINISHED_STATE, &subq, 1))
FINISHED_STATE, &subq, 1, 1))
verbose(VERB_ALGO,
"could not validate NXDOMAIN "
"response");
outbound_list_clear(&iq->outlist);
iq->num_current_queries = 0;
fptr_ok(fptr_whitelist_modenv_detach_subs(
qstate->env->detach_subs));
(*qstate->env->detach_subs)(qstate);
iq->num_target_queries = 0;
}
}
return next_state(iq, QUERYTARGETS_STATE);
@ -2843,6 +2929,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
/* Count this as a referral. */
iq->referral_count++;
iq->sent_count = 0;
iq->dp_target_count = 0;
/* see if the next dp is a trust anchor, or a DS was sent
* along, indicating dnssec is expected for next zone */
iq->dnssec_expected = iter_indicates_dnssec(qstate->env,
@ -2919,6 +3006,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
iq->dsns_point = NULL;
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. */
@ -3111,7 +3199,7 @@ processPrimeResponse(struct module_qstate* qstate, int id)
if(!generate_sub_request(qstate->qinfo.qname,
qstate->qinfo.qname_len, qstate->qinfo.qtype,
qstate->qinfo.qclass, qstate, id, iq,
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) {
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) {
verbose(VERB_ALGO, "could not generate prime check");
}
generate_a_aaaa_check(qstate, iq, id);
@ -3139,6 +3227,7 @@ static void
processTargetResponse(struct module_qstate* qstate, int id,
struct module_qstate* forq)
{
struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
struct ub_packed_rrset_key* rrset;
@ -3176,7 +3265,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
log_rrset_key(VERB_ALGO, "add parentside glue to dp",
iq->pside_glue);
if(!delegpt_add_rrset(foriq->dp, forq->region,
iq->pside_glue, 1))
iq->pside_glue, 1, NULL))
log_err("out of memory adding pside glue");
}
@ -3187,6 +3276,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
* response type was ANSWER. */
rrset = reply_find_answer_rrset(&iq->qchase, qstate->return_msg->rep);
if(rrset) {
int additions = 0;
/* if CNAMEs have been followed - add new NS to delegpt. */
/* BTW. RFC 1918 says NS should not have got CNAMEs. Robust. */
if(!delegpt_find_ns(foriq->dp, rrset->rk.dname,
@ -3198,13 +3288,23 @@ processTargetResponse(struct module_qstate* qstate, int id,
}
/* if dpns->lame then set the address(es) lame too */
if(!delegpt_add_rrset(foriq->dp, forq->region, rrset,
dpns->lame))
dpns->lame, &additions))
log_err("out of memory adding targets");
if(!additions) {
/* no new addresses, increase the nxns counter, like
* this could be a list of wildcards with no new
* addresses */
target_count_increase_nx(foriq, 1);
}
verbose(VERB_ALGO, "added target response");
delegpt_log(VERB_ALGO, foriq->dp);
} else {
verbose(VERB_ALGO, "iterator TargetResponse failed");
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
dpns->resolved = 1; /* fail the target */
if((dpns->got4 == 2 || !ie->supports_ipv4) &&
(dpns->got6 == 2 || !ie->supports_ipv6))
target_count_increase_nx(foriq, 1);
}
}
@ -3378,7 +3478,7 @@ processCollectClass(struct module_qstate* qstate, int id)
qstate->qinfo.qname_len, qstate->qinfo.qtype,
c, qstate, id, iq, INIT_REQUEST_STATE,
FINISHED_STATE, &subq,
(int)!(qstate->query_flags&BIT_CD))) {
(int)!(qstate->query_flags&BIT_CD), 0)) {
errinf(qstate, "could not generate class ANY"
" lookup query");
return error_response(qstate, id,
@ -3592,7 +3692,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
if(event == module_event_noreply || event == module_event_error) {
if(event == module_event_noreply && iq->sent_count >= 3 &&
qstate->env->cfg->use_caps_bits_for_id &&
!iq->caps_fallback) {
!iq->caps_fallback && !is_caps_whitelisted(ie, iq)) {
/* start fallback */
iq->caps_fallback = 1;
iq->caps_server = 0;

View File

@ -55,6 +55,11 @@ struct rbtree_type;
/** max number of targets spawned for a query and its subqueries */
#define MAX_TARGET_COUNT 64
/** max number of target lookups per qstate, per delegation point */
#define MAX_DP_TARGET_COUNT 16
/** max number of nxdomains allowed for target lookups for a query and
* its subqueries */
#define MAX_TARGET_NX 5
/** max number of query restarts. Determines max number of CNAME chain. */
#define MAX_RESTART_COUNT 8
/** max number of referrals. Makes sure resolver does not run away */
@ -83,7 +88,7 @@ struct rbtree_type;
/** how nice is a server without further information, in msec
* Equals rtt initial timeout value.
*/
#define UNKNOWN_SERVER_NICENESS 376
extern int UNKNOWN_SERVER_NICENESS;
/** maximum timeout before a host is deemed unsuitable, in msec.
* After host_ttl this will be timed out and the host will be tried again.
* Equals RTT_MAX_TIMEOUT
@ -305,9 +310,14 @@ struct iter_qstate {
int sent_count;
/** number of target queries spawned in [1], for this query and its
* subqueries, the malloced-array is shared, [0] refcount. */
* subqueries, the malloced-array is shared, [0] refcount.
* in [2] the number of nxdomains is counted. */
int* target_count;
/** number of target lookups per delegation point. Reset to 0 after
* receiving referral answer. Not shared with subqueries. */
int dp_target_count;
/** if true, already tested for ratelimiting and passed the test */
int ratelimit_ok;

View File

@ -55,11 +55,19 @@
int
context_finalize(struct ub_ctx* ctx)
{
int is_rpz = 0;
struct config_file* cfg = ctx->env->cfg;
verbosity = cfg->verbosity;
if(ctx->logfile_override)
if(ctx_logfile_overridden && !ctx->logfile_override) {
log_file(NULL); /* clear that override */
ctx_logfile_overridden = 0;
}
if(ctx->logfile_override) {
ctx_logfile_overridden = 1;
log_file(ctx->log_out);
else log_init(cfg->logfile, cfg->use_syslog, NULL);
} else {
log_init(cfg->logfile, cfg->use_syslog, NULL);
}
config_apply(cfg);
if(!modstack_setup(&ctx->mods, cfg->module_conf, ctx->env))
return UB_INITFAIL;
@ -69,7 +77,7 @@ context_finalize(struct ub_ctx* ctx)
return UB_NOMEM;
if(!local_zones_apply_cfg(ctx->local_zones, cfg))
return UB_INITFAIL;
if(!auth_zones_apply_cfg(ctx->env->auth_zones, cfg, 1))
if(!auth_zones_apply_cfg(ctx->env->auth_zones, cfg, 1, &is_rpz))
return UB_INITFAIL;
if(!slabhash_is_size(ctx->env->msg_cache, cfg->msg_cache_size,
cfg->msg_cache_slabs)) {

View File

@ -52,6 +52,9 @@ struct tube;
struct sldns_buffer;
struct ub_event_base;
/** store that the logfile has a debug override */
extern int ctx_logfile_overridden;
/**
* The context structure
*
@ -116,6 +119,9 @@ struct ub_ctx {
/** event base for event oriented interface */
struct ub_event_base* event_base;
/** true if the event_base is a pluggable base that is malloced
* with a user event base inside, if so, clean up the pluggable alloc*/
int event_base_malloced;
/** libworker for event based interface */
struct libworker* event_worker;

View File

@ -79,18 +79,21 @@
#include <iphlpapi.h>
#endif /* UB_ON_WINDOWS */
/** store that the logfile has a debug override */
int ctx_logfile_overridden = 0;
/** create context functionality, but no pipes */
static struct ub_ctx* ub_ctx_create_nopipe(void)
{
struct ub_ctx* ctx;
unsigned int seed;
#ifdef USE_WINSOCK
int r;
WSADATA wsa_data;
#endif
checklock_start();
log_init(NULL, 0, NULL); /* logs to stderr */
if(!ctx_logfile_overridden)
log_init(NULL, 0, NULL); /* logs to stderr */
log_ident_set("libunbound");
#ifdef USE_WINSOCK
if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) {
@ -99,7 +102,7 @@ static struct ub_ctx* ub_ctx_create_nopipe(void)
return NULL;
}
#endif
verbosity = 0; /* errors only */
verbosity = NO_VERBOSE; /* errors only */
checklock_start();
ctx = (struct ub_ctx*)calloc(1, sizeof(*ctx));
if(!ctx) {
@ -107,15 +110,12 @@ static struct ub_ctx* ub_ctx_create_nopipe(void)
return NULL;
}
alloc_init(&ctx->superalloc, NULL, 0);
seed = (unsigned int)time(NULL) ^ (unsigned int)getpid();
if(!(ctx->seed_rnd = ub_initstate(seed, NULL))) {
explicit_bzero(&seed, sizeof(seed));
if(!(ctx->seed_rnd = ub_initstate(NULL))) {
ub_randfree(ctx->seed_rnd);
free(ctx);
errno = ENOMEM;
return NULL;
}
explicit_bzero(&seed, sizeof(seed));
lock_basic_init(&ctx->qqpipe_lock);
lock_basic_init(&ctx->rrpipe_lock);
lock_basic_init(&ctx->cfglock);
@ -222,6 +222,7 @@ ub_ctx_create_event(struct event_base* eb)
ub_ctx_delete(ctx);
return NULL;
}
ctx->event_base_malloced = 1;
return ctx;
}
@ -328,6 +329,12 @@ ub_ctx_delete(struct ub_ctx* ctx)
ub_randfree(ctx->seed_rnd);
alloc_clear(&ctx->superalloc);
traverse_postorder(&ctx->queries, delq, NULL);
if(ctx_logfile_overridden) {
log_file(NULL);
ctx_logfile_overridden = 0;
}
if(ctx->event_base_malloced)
free(ctx->event_base);
free(ctx);
#ifdef USE_WINSOCK
WSACleanup();
@ -469,6 +476,7 @@ int ub_ctx_debugout(struct ub_ctx* ctx, void* out)
{
lock_basic_lock(&ctx->cfglock);
log_file((FILE*)out);
ctx_logfile_overridden = 1;
ctx->logfile_override = 1;
ctx->log_out = out;
lock_basic_unlock(&ctx->cfglock);
@ -724,7 +732,7 @@ ub_resolve_event(struct ub_ctx* ctx, const char* name, int rrtype,
*async_id = 0;
lock_basic_lock(&ctx->cfglock);
if(!ctx->finalized) {
int r = context_finalize(ctx);
r = context_finalize(ctx);
if(r) {
lock_basic_unlock(&ctx->cfglock);
return r;
@ -966,6 +974,19 @@ ub_ctx_set_fwd(struct ub_ctx* ctx, const char* addr)
return UB_NOERROR;
}
int ub_ctx_set_tls(struct ub_ctx* ctx, int tls)
{
lock_basic_lock(&ctx->cfglock);
if(ctx->finalized) {
lock_basic_unlock(&ctx->cfglock);
errno=EINVAL;
return UB_AFTERFINAL;
}
ctx->env->cfg->ssl_upstream = tls;
lock_basic_unlock(&ctx->cfglock);
return UB_NOERROR;
}
int ub_ctx_set_stub(struct ub_ctx* ctx, const char* zone, const char* addr,
int isprime)
{
@ -1137,7 +1158,7 @@ int
ub_ctx_hosts(struct ub_ctx* ctx, const char* fname)
{
FILE* in;
char buf[1024], ldata[1024];
char buf[1024], ldata[2048];
char* parse, *addr, *name, *ins;
lock_basic_lock(&ctx->cfglock);
if(ctx->finalized) {

View File

@ -122,7 +122,6 @@ libworker_delete_event(struct libworker* w)
static struct libworker*
libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb)
{
unsigned int seed;
struct libworker* w = (struct libworker*)calloc(1, sizeof(*w));
struct config_file* cfg = ctx->env->cfg;
int* ports;
@ -177,17 +176,13 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb)
}
w->env->worker = (struct worker*)w;
w->env->probe_timer = NULL;
seed = (unsigned int)time(NULL) ^ (unsigned int)getpid() ^
(((unsigned int)w->thread_num)<<17);
seed ^= (unsigned int)w->env->alloc->next_id;
if(!w->is_bg || w->is_bg_thread) {
lock_basic_lock(&ctx->cfglock);
}
if(!(w->env->rnd = ub_initstate(seed, ctx->seed_rnd))) {
if(!(w->env->rnd = ub_initstate(ctx->seed_rnd))) {
if(!w->is_bg || w->is_bg_thread) {
lock_basic_unlock(&ctx->cfglock);
}
explicit_bzero(&seed, sizeof(seed));
libworker_delete(w);
return NULL;
}
@ -207,7 +202,6 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb)
hash_set_raninit((uint32_t)ub_random(w->env->rnd));
}
}
explicit_bzero(&seed, sizeof(seed));
if(eb)
w->base = comm_base_create_event(eb);
@ -222,11 +216,10 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb)
}
numports = cfg_condense_ports(cfg, &ports);
if(numports == 0) {
int locked = !w->is_bg || w->is_bg_thread;
libworker_delete(w);
if(locked) {
if(!w->is_bg || w->is_bg_thread) {
lock_basic_unlock(&ctx->cfglock);
}
libworker_delete(w);
return NULL;
}
w->back = outside_network_create(w->base, cfg->msg_buffer_size,
@ -533,7 +526,7 @@ libworker_fillup_fg(struct ctx_query* q, int rcode, sldns_buffer* buf,
}
q->res->rcode = LDNS_RCODE_SERVFAIL;
q->msg_security = 0;
q->msg_security = sec_status_unchecked;
q->msg = memdup(sldns_buffer_begin(buf), sldns_buffer_limit(buf));
q->msg_len = sldns_buffer_limit(buf);
if(!q->msg) {
@ -568,7 +561,6 @@ setup_qinfo_edns(struct libworker* w, struct ctx_query* q,
if(!qinfo->qname) {
return 0;
}
qinfo->local_alias = NULL;
edns->edns_present = 1;
edns->ext_rcode = 0;
edns->edns_version = 0;
@ -657,8 +649,8 @@ libworker_event_done_cb(void* arg, int rcode, sldns_buffer* buf,
sec = 1;
else if(s == sec_status_secure)
sec = 2;
(*cb)(cb_arg, rcode, (void*)sldns_buffer_begin(buf),
(int)sldns_buffer_limit(buf), sec, why_bogus, was_ratelimited);
(*cb)(cb_arg, rcode, (buf?(void*)sldns_buffer_begin(buf):NULL),
(buf?(int)sldns_buffer_limit(buf):0), sec, why_bogus, was_ratelimited);
}
}

View File

@ -20,6 +20,7 @@ ub_ctx_set_event
ub_ctx_set_fwd
ub_ctx_set_option
ub_ctx_set_stub
ub_ctx_set_tls
ub_ctx_trustedkeys
ub_ctx_zone_add
ub_ctx_zone_remove

View File

@ -204,8 +204,9 @@ struct ub_result {
char* why_bogus;
/**
* If the query or one of its subqueries was ratelimited. Useful if
* ratelimiting is enabled and answer is SERVFAIL.
* If the query or one of its subqueries was ratelimited. Useful if
* ratelimiting is enabled and answer to the client is SERVFAIL as a
* result.
*/
int was_ratelimited;
@ -309,6 +310,17 @@ int ub_ctx_config(struct ub_ctx* ctx, const char* fname);
*/
int ub_ctx_set_fwd(struct ub_ctx* ctx, const char* addr);
/**
* Use DNS over TLS to send queries to machines set with ub_ctx_set_fwd().
*
* @param ctx: context.
* At this time it is only possible to set configuration before the
* first resolve is done.
* @param tls: enable or disable DNS over TLS
* @return 0 if OK, else error.
*/
int ub_ctx_set_tls(struct ub_ctx* ctx, int tls);
/**
* Add a stub zone, with given address to send to. This is for custom
* root hints or pointing to a local authoritative dns server.
@ -643,6 +655,8 @@ struct ub_shm_stat_info {
#define UB_STATS_OPCODE_NUM 16
/** number of histogram buckets */
#define UB_STATS_BUCKET_NUM 40
/** number of RPZ actions */
#define UB_STATS_RPZ_ACTION_NUM 10
/** per worker statistics. */
struct ub_server_stats {
@ -722,8 +736,8 @@ struct ub_server_stats {
long long unwanted_queries;
/** usage of tcp accept list */
long long tcp_accept_usage;
/** answers served from expired cache */
long long zero_ttl_responses;
/** expired answers served from cache */
long long ans_expired;
/** histogram data exported to array
* if the array is the same size, no data is lost, and
* if all histograms are same size (is so by default) then
@ -770,6 +784,12 @@ struct ub_server_stats {
/** number of queries answered from edns-subnet specific data, and
* the answer was from the edns-subnet cache. */
long long num_query_subnet_cache;
/** number of bytes in the stream wait buffers */
long long mem_stream_wait;
/** number of TLS connection resume */
long long qtls_resume;
/** RPZ action stats */
long long rpz_action[UB_STATS_RPZ_ACTION_NUM];
};
/**

View File

@ -2124,7 +2124,7 @@ fi
# a configuration failure hint, and exit.
func_fatal_configuration ()
{
func__fatal_error ${1+"$@"} \
func_fatal_error ${1+"$@"} \
"See the $PACKAGE documentation for more information." \
"Fatal configuration error."
}
@ -7272,10 +7272,12 @@ func_mode_link ()
# -tp=* Portland pgcc target processor selection
# --sysroot=* for sysroot support
# -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
# -specs=* GCC specs files
# -stdlib=* select c++ std lib with clang
-64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
-t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
-O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*)
-O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \
-specs=*)
func_quote_for_eval "$arg"
arg=$func_quote_for_eval_result
func_append compile_command " $arg"

View File

@ -12,6 +12,7 @@
#include "config.h"
#include "services/localzone.h"
#include "services/authzone.h"
#include "services/cache/dns.h"
#include "sldns/str2wire.h"
#include "util/config_file.h"
@ -25,30 +26,6 @@
#include "services/view.h"
#include "sldns/rrdef.h"
/**
* Conceptual set of IP addresses for response AAAA or A records that should
* trigger special actions.
*/
struct respip_set {
struct regional* region;
struct rbtree_type ip_tree;
char* const* tagname; /* shallow copy of tag names, for logging */
int num_tags; /* number of tagname entries */
};
/** An address span with response control information */
struct resp_addr {
/** node in address tree */
struct addr_tree_node node;
/** tag bitlist */
uint8_t* taglist;
/** length of the taglist (in bytes) */
size_t taglen;
/** action for this address span */
enum respip_action action;
/** "local data" for this node */
struct ub_packed_rrset_key* data;
};
/** Subset of resp_addr.node, used for inform-variant logging */
struct respip_addr_info {
@ -88,14 +65,28 @@ respip_set_create(void)
return NULL;
}
addr_tree_init(&set->ip_tree);
lock_rw_init(&set->lock);
return set;
}
/** helper traverse to delete resp_addr nodes */
static void
resp_addr_del(rbnode_type* n, void* ATTR_UNUSED(arg))
{
struct resp_addr* r = (struct resp_addr*)n->key;
lock_rw_destroy(&r->lock);
#ifdef THREADS_DISABLED
(void)r;
#endif
}
void
respip_set_delete(struct respip_set* set)
{
if(!set)
return;
lock_rw_destroy(&set->lock);
traverse_postorder(&set->ip_tree, resp_addr_del, NULL);
regional_destroy(set->region);
free(set);
}
@ -108,12 +99,49 @@ respip_set_get_tree(struct respip_set* set)
return &set->ip_tree;
}
struct resp_addr*
respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage* addr,
socklen_t addrlen, int net, int create, const char* ipstr)
{
struct resp_addr* node;
node = (struct resp_addr*)addr_tree_find(&set->ip_tree, addr, addrlen, net);
if(!node && create) {
node = regional_alloc_zero(set->region, sizeof(*node));
if(!node) {
log_err("out of memory");
return NULL;
}
lock_rw_init(&node->lock);
node->action = respip_none;
if(!addr_tree_insert(&set->ip_tree, &node->node, addr,
addrlen, net)) {
/* We know we didn't find it, so this should be
* impossible. */
log_warn("unexpected: duplicate address: %s", ipstr);
}
}
return node;
}
void
respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node)
{
struct resp_addr* prev;
prev = (struct resp_addr*)rbtree_previous((struct rbnode_type*)node);
lock_rw_destroy(&node->lock);
rbtree_delete(&set->ip_tree, node);
/* no free'ing, all allocated in region */
if(!prev)
addr_tree_init_parents((rbtree_type*)set);
else
addr_tree_init_parents_node(&prev->node);
}
/** returns the node in the address tree for the specified netblock string;
* non-existent node will be created if 'create' is true */
static struct resp_addr*
respip_find_or_create(struct respip_set* set, const char* ipstr, int create)
{
struct resp_addr* node;
struct sockaddr_storage addr;
int net;
socklen_t addrlen;
@ -122,22 +150,8 @@ respip_find_or_create(struct respip_set* set, const char* ipstr, int create)
log_err("cannot parse netblock: '%s'", ipstr);
return NULL;
}
node = (struct resp_addr*)addr_tree_find(&set->ip_tree, &addr, addrlen, net);
if(!node && create) {
node = regional_alloc_zero(set->region, sizeof(*node));
if(!node) {
log_err("out of memory");
return NULL;
}
node->action = respip_none;
if(!addr_tree_insert(&set->ip_tree, &node->node, &addr,
addrlen, net)) {
/* We know we didn't find it, so this should be
* impossible. */
log_warn("unexpected: duplicate address: %s", ipstr);
}
}
return node;
return respip_sockaddr_find_or_create(set, &addr, addrlen, net, create,
ipstr);
}
static int
@ -183,12 +197,18 @@ respip_action_cfg(struct respip_set* set, const char* ipstr,
action = respip_inform;
else if(strcmp(actnstr, "inform_deny") == 0)
action = respip_inform_deny;
else if(strcmp(actnstr, "inform_redirect") == 0)
action = respip_inform_redirect;
else if(strcmp(actnstr, "always_transparent") == 0)
action = respip_always_transparent;
else if(strcmp(actnstr, "always_refuse") == 0)
action = respip_always_refuse;
else if(strcmp(actnstr, "always_nxdomain") == 0)
action = respip_always_nxdomain;
else if(strcmp(actnstr, "always_nodata") == 0)
action = respip_always_nodata;
else if(strcmp(actnstr, "always_deny") == 0)
action = respip_always_deny;
else {
log_err("unknown response-ip action %s", actnstr);
return 0;
@ -230,8 +250,43 @@ new_rrset(struct regional* region, uint16_t rrtype, uint16_t rrclass)
}
/** enter local data as resource records into a response-ip node */
static int
int
respip_enter_rr(struct regional* region, struct resp_addr* raddr,
uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata,
size_t rdata_len, const char* rrstr, const char* netblockstr)
{
struct packed_rrset_data* pd;
struct sockaddr* sa;
sa = (struct sockaddr*)&raddr->node.addr;
if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data) {
log_err("CNAME response-ip data (%s) can not co-exist with other "
"response-ip data for netblock %s", rrstr, netblockstr);
return 0;
} else if (raddr->data &&
raddr->data->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
log_err("response-ip data (%s) can not be added; CNAME response-ip "
"data already in place for netblock %s", rrstr, netblockstr);
return 0;
} else if((rrtype != LDNS_RR_TYPE_CNAME) &&
((sa->sa_family == AF_INET && rrtype != LDNS_RR_TYPE_A) ||
(sa->sa_family == AF_INET6 && rrtype != LDNS_RR_TYPE_AAAA))) {
log_err("response-ip data %s record type does not correspond "
"to netblock %s address family", rrstr, netblockstr);
return 0;
}
if(!raddr->data) {
raddr->data = new_rrset(region, rrtype, rrclass);
if(!raddr->data)
return 0;
}
pd = raddr->data->entry.data;
return rrset_insert_rr(region, pd, rdata, rdata_len, ttl, rrstr);
}
static int
respip_enter_rrstr(struct regional* region, struct resp_addr* raddr,
const char* rrstr, const char* netblock)
{
uint8_t* nm;
@ -242,10 +297,9 @@ respip_enter_rr(struct regional* region, struct resp_addr* raddr,
size_t rdata_len = 0;
char buf[65536];
char bufshort[64];
struct packed_rrset_data* pd;
struct sockaddr* sa;
int ret;
if(raddr->action != respip_redirect) {
if(raddr->action != respip_redirect
&& raddr->action != respip_inform_redirect) {
log_err("cannot parse response-ip-data %s: response-ip "
"action for %s is not redirect", rrstr, netblock);
return 0;
@ -262,31 +316,8 @@ respip_enter_rr(struct regional* region, struct resp_addr* raddr,
return 0;
}
free(nm);
sa = (struct sockaddr*)&raddr->node.addr;
if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data) {
log_err("CNAME response-ip data (%s) can not co-exist with other "
"response-ip data for netblock %s", rrstr, netblock);
return 0;
} else if (raddr->data &&
raddr->data->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
log_err("response-ip data (%s) can not be added; CNAME response-ip "
"data already in place for netblock %s", rrstr, netblock);
return 0;
} else if((rrtype != LDNS_RR_TYPE_CNAME) &&
((sa->sa_family == AF_INET && rrtype != LDNS_RR_TYPE_A) ||
(sa->sa_family == AF_INET6 && rrtype != LDNS_RR_TYPE_AAAA))) {
log_err("response-ip data %s record type does not correspond "
"to netblock %s address family", rrstr, netblock);
return 0;
}
if(!raddr->data) {
raddr->data = new_rrset(region, rrtype, rrclass);
if(!raddr->data)
return 0;
}
pd = raddr->data->entry.data;
return rrset_insert_rr(region, pd, rdata, rdata_len, ttl, rrstr);
return respip_enter_rr(region, raddr, rrtype, rrclass, ttl, rdata,
rdata_len, rrstr, netblock);
}
static int
@ -300,7 +331,7 @@ respip_data_cfg(struct respip_set* set, const char* ipstr, const char* rrstr)
"response-ip node for %s not found", rrstr, ipstr);
return 0;
}
return respip_enter_rr(set->region, node, rrstr, ipstr);
return respip_enter_rrstr(set->region, node, rrstr, ipstr);
}
static int
@ -358,6 +389,7 @@ respip_set_apply_cfg(struct respip_set* set, char* const* tagname, int num_tags,
free(pd);
pd = np;
}
addr_tree_init_parents(&set->ip_tree);
return 1;
}
@ -475,10 +507,16 @@ copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region)
if(!ck->rk.dname)
return NULL;
if((unsigned)data->count >= 0xffff00U)
return NULL; /* guard against integer overflow in dsize */
dsize = sizeof(struct packed_rrset_data) + data->count *
(sizeof(size_t)+sizeof(uint8_t*)+sizeof(time_t));
for(i=0; i<data->count; i++)
for(i=0; i<data->count; i++) {
if((unsigned)dsize >= 0x0fffffffU ||
(unsigned)data->rr_len[i] >= 0x0fffffffU)
return NULL; /* guard against integer overflow */
dsize += data->rr_len[i];
}
d = regional_alloc(region, dsize);
if(!d)
return NULL;
@ -554,9 +592,10 @@ rdata2sockaddr(const struct packed_rrset_data* rd, uint16_t rtype, size_t i,
* rep->rrsets for the RRset that contains the matching IP address record
* (the index is normally 0, but can be larger than that if this is a CNAME
* chain or type-ANY response).
* Returns resp_addr holding read lock.
*/
static const struct resp_addr*
respip_addr_lookup(const struct reply_info *rep, struct rbtree_type* iptree,
static struct resp_addr*
respip_addr_lookup(const struct reply_info *rep, struct respip_set* rs,
size_t* rrset_id)
{
size_t i;
@ -564,6 +603,7 @@ respip_addr_lookup(const struct reply_info *rep, struct rbtree_type* iptree,
struct sockaddr_storage ss;
socklen_t addrlen;
lock_rw_rdlock(&rs->lock);
for(i=0; i<rep->an_numrrsets; i++) {
size_t j;
const struct packed_rrset_data* rd;
@ -575,15 +615,17 @@ respip_addr_lookup(const struct reply_info *rep, struct rbtree_type* iptree,
for(j = 0; j < rd->count; j++) {
if(!rdata2sockaddr(rd, rtype, j, &ss, &addrlen))
continue;
ra = (struct resp_addr*)addr_tree_lookup(iptree, &ss,
addrlen);
ra = (struct resp_addr*)addr_tree_lookup(&rs->ip_tree,
&ss, addrlen);
if(ra) {
*rrset_id = i;
lock_rw_rdlock(&ra->lock);
lock_rw_unlock(&rs->lock);
return ra;
}
}
}
lock_rw_unlock(&rs->lock);
return NULL;
}
@ -632,8 +674,8 @@ make_new_reply_info(const struct reply_info* rep, struct regional* region,
* Note that this function distinguishes error conditions from "success but
* not overridden". This is because we want to avoid accidentally applying
* the "no data" action in case of error.
* @param raddr: address span that requires an action
* @param action: action to apply
* @param data: RRset to use for override
* @param qtype: original query type
* @param rep: original reply message
* @param rrset_id: the rrset ID in 'rep' to which the action should apply
@ -648,14 +690,15 @@ make_new_reply_info(const struct reply_info* rep, struct regional* region,
* @return 1 if overridden, 0 if not overridden, -1 on error.
*/
static int
respip_data_answer(const struct resp_addr* raddr, enum respip_action action,
respip_data_answer(enum respip_action action,
struct ub_packed_rrset_key* data,
uint16_t qtype, const struct reply_info* rep,
size_t rrset_id, struct reply_info** new_repp, int tag,
struct config_strlist** tag_datas, size_t tag_datas_size,
char* const* tagname, int num_tags,
struct ub_packed_rrset_key** redirect_rrsetp, struct regional* region)
{
struct ub_packed_rrset_key* rp = raddr->data;
struct ub_packed_rrset_key* rp = data;
struct reply_info* new_rep;
*redirect_rrsetp = NULL;
@ -693,7 +736,7 @@ respip_data_answer(const struct resp_addr* raddr, enum respip_action action,
* to replace the rrset's dname. Note that, unlike local data, we
* rename the dname for other actions than redirect. This is because
* response-ip-data isn't associated to any specific name. */
if(rp == raddr->data) {
if(rp == data) {
rp = copy_rrset(rp, region);
if(!rp)
return -1;
@ -750,7 +793,9 @@ respip_nodata_answer(uint16_t qtype, enum respip_action action,
*new_repp = new_rep;
return 1;
} else if(action == respip_static || action == respip_redirect ||
action == respip_always_nxdomain) {
action == respip_always_nxdomain ||
action == respip_always_nodata ||
action == respip_inform_redirect) {
/* Since we don't know about other types of the owner name,
* we generally return NOERROR/NODATA unless an NXDOMAIN action
* is explicitly specified. */
@ -783,16 +828,22 @@ populate_action_info(struct respip_action_info* actinfo,
enum respip_action action, const struct resp_addr* raddr,
const struct ub_packed_rrset_key* ATTR_UNUSED(rrset),
int ATTR_UNUSED(tag), const struct respip_set* ATTR_UNUSED(ipset),
int ATTR_UNUSED(action_only), struct regional* region)
int ATTR_UNUSED(action_only), struct regional* region, int rpz_used,
int rpz_log, char* log_name, int rpz_cname_override)
{
if(action == respip_none || !raddr)
return 1;
actinfo->action = action;
actinfo->rpz_used = rpz_used;
actinfo->rpz_log = rpz_log;
actinfo->log_name = log_name;
actinfo->rpz_cname_override = rpz_cname_override;
/* for inform variants, make a copy of the matched address block for
* later logging. We make a copy to proactively avoid disruption if
* and when we allow a dynamic update to the respip tree. */
if(action == respip_inform || action == respip_inform_deny) {
if(action == respip_inform || action == respip_inform_deny ||
rpz_used) {
struct respip_addr_info* a =
regional_alloc_zero(region, sizeof(*a));
if(!a) {
@ -808,12 +859,39 @@ populate_action_info(struct respip_action_info* actinfo,
return 1;
}
static int
respip_use_rpz(struct resp_addr* raddr, struct rpz* r,
enum respip_action* action,
struct ub_packed_rrset_key** data, int* rpz_log, char** log_name,
int* rpz_cname_override, struct regional* region, int* is_rpz)
{
if(r->action_override == RPZ_DISABLED_ACTION) {
*is_rpz = 0;
return 1;
}
else if(r->action_override == RPZ_NO_OVERRIDE_ACTION)
*action = raddr->action;
else
*action = rpz_action_to_respip_action(r->action_override);
if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION &&
r->cname_override) {
*data = r->cname_override;
*rpz_cname_override = 1;
}
*rpz_log = r->log;
if(r->log_name)
if(!(*log_name = regional_strdup(region, r->log_name)))
return 0;
*is_rpz = 1;
return 1;
}
int
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 regional* region, struct auth_zones* az)
{
const uint8_t* ctaglist;
size_t ctaglen;
@ -826,9 +904,15 @@ respip_rewrite_reply(const struct query_info* qinfo,
size_t rrset_id = 0;
enum respip_action action = respip_none;
int tag = -1;
const struct resp_addr* raddr = NULL;
struct resp_addr* raddr = NULL;
int ret = 1;
struct ub_packed_rrset_key* redirect_rrset = NULL;
struct rpz* r;
struct ub_packed_rrset_key* data = NULL;
int rpz_used = 0;
int rpz_log = 0;
int rpz_cname_override = 0;
char* log_name = NULL;
if(!cinfo)
goto done;
@ -841,6 +925,8 @@ respip_rewrite_reply(const struct query_info* qinfo,
view = cinfo->view;
ipset = cinfo->respip_set;
log_assert(ipset);
/** Try to use response-ip config from the view first; use
* global response-ip config if we don't have the view or we don't
* have the matching per-view config (and the view allows the use
@ -855,7 +941,7 @@ respip_rewrite_reply(const struct query_info* qinfo,
lock_rw_rdlock(&view->lock);
if(view->respip_set) {
if((raddr = respip_addr_lookup(rep,
&view->respip_set->ip_tree, &rrset_id))) {
view->respip_set, &rrset_id))) {
/** for per-view respip directives the action
* can only be direct (i.e. not tag-based) */
action = raddr->action;
@ -864,7 +950,7 @@ respip_rewrite_reply(const struct query_info* qinfo,
if(!raddr && !view->isfirst)
goto done;
}
if(!raddr && ipset && (raddr = respip_addr_lookup(rep, &ipset->ip_tree,
if(!raddr && (raddr = respip_addr_lookup(rep, ipset,
&rrset_id))) {
action = (enum respip_action)local_data_find_tag_action(
raddr->taglist, raddr->taglen, ctaglist, ctaglen,
@ -872,6 +958,29 @@ respip_rewrite_reply(const struct query_info* qinfo,
(enum localzone_type)raddr->action, &tag,
ipset->tagname, ipset->num_tags);
}
lock_rw_rdlock(&az->rpz_lock);
for(r = az->rpz_first; r && !raddr; r = r->next) {
if(!r->taglist || taglist_intersect(r->taglist,
r->taglistlen, ctaglist, ctaglen)) {
if((raddr = respip_addr_lookup(rep,
r->respip_set, &rrset_id))) {
if(!respip_use_rpz(raddr, r, &action, &data,
&rpz_log, &log_name, &rpz_cname_override,
region, &rpz_used)) {
log_err("out of memory");
lock_rw_unlock(&raddr->lock);
lock_rw_unlock(&az->rpz_lock);
return 0;
}
if(!rpz_used) {
lock_rw_unlock(&raddr->lock);
raddr = NULL;
actinfo->rpz_disabled++;
}
}
}
}
lock_rw_unlock(&az->rpz_lock);
if(raddr && !search_only) {
int result = 0;
@ -880,10 +989,13 @@ respip_rewrite_reply(const struct query_info* qinfo,
if(action != respip_always_refuse
&& action != respip_always_transparent
&& action != respip_always_nxdomain
&& (result = respip_data_answer(raddr, action,
qinfo->qtype, rep, rrset_id, new_repp, tag, tag_datas,
tag_datas_size, ipset->tagname, ipset->num_tags,
&redirect_rrset, region)) < 0) {
&& action != respip_always_nodata
&& action != respip_always_deny
&& (result = respip_data_answer(action,
(data) ? data : raddr->data, qinfo->qtype, rep,
rrset_id, new_repp, tag, tag_datas, tag_datas_size,
ipset->tagname, ipset->num_tags, &redirect_rrset,
region)) < 0) {
ret = 0;
goto done;
}
@ -914,7 +1026,11 @@ respip_rewrite_reply(const struct query_info* qinfo,
*alias_rrset = redirect_rrset;
/* on success, populate respip result structure */
ret = populate_action_info(actinfo, action, raddr,
redirect_rrset, tag, ipset, search_only, region);
redirect_rrset, tag, ipset, search_only, region,
rpz_used, rpz_log, log_name, rpz_cname_override);
}
if(raddr) {
lock_rw_unlock(&raddr->lock);
}
return ret;
}
@ -970,14 +1086,15 @@ respip_operate(struct module_qstate* qstate, enum module_ev event, int id,
qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA ||
qstate->qinfo.qtype == LDNS_RR_TYPE_ANY) &&
qstate->return_msg && qstate->return_msg->rep) {
struct respip_action_info actinfo = {respip_none, NULL};
struct reply_info* new_rep = qstate->return_msg->rep;
struct ub_packed_rrset_key* alias_rrset = NULL;
struct respip_action_info actinfo = {0};
actinfo.action = respip_none;
if(!respip_rewrite_reply(&qstate->qinfo,
qstate->client_info, qstate->return_msg->rep,
&new_rep, &actinfo, &alias_rrset, 0,
qstate->region)) {
qstate->region, qstate->env->auth_zones)) {
goto servfail;
}
if(actinfo.action != respip_none) {
@ -993,9 +1110,10 @@ respip_operate(struct module_qstate* qstate, enum module_ev event, int id,
} else {
qstate->respip_action_info = NULL;
}
if (new_rep == qstate->return_msg->rep &&
if (actinfo.action == respip_always_deny ||
(new_rep == qstate->return_msg->rep &&
(actinfo.action == respip_deny ||
actinfo.action == respip_inform_deny)) {
actinfo.action == respip_inform_deny))) {
/* for deny-variant actions (unless response-ip
* data is applied), mark the query state so
* the response will be dropped for all
@ -1023,14 +1141,16 @@ 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 reply_info** new_repp, struct regional* region,
struct auth_zones* az)
{
struct reply_info* new_rep;
struct reply_info* tmp_rep = NULL; /* just a placeholder */
struct ub_packed_rrset_key* alias_rrset = NULL; /* ditto */
uint16_t tgt_rcode;
size_t i, j;
struct respip_action_info actinfo = {respip_none, NULL};
struct respip_action_info actinfo = {0};
actinfo.action = respip_none;
/* If the query for the CNAME target would result in an unusual rcode,
* we generally translate it as a failure for the base query
@ -1049,7 +1169,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))
&alias_rrset, 1, region, az))
return 0;
if(actinfo.action != respip_none) {
log_info("CNAME target of redirect response-ip action would "
@ -1101,7 +1221,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))
super->env->need_to_validate, &new_rep, super->region,
qstate->env->auth_zones))
goto fail;
super->return_msg->rep = new_rep;
return;
@ -1160,12 +1281,15 @@ respip_set_is_empty(const struct respip_set* set)
}
void
respip_inform_print(struct respip_addr_info* respip_addr, uint8_t* qname,
respip_inform_print(struct respip_action_info* respip_actinfo, uint8_t* qname,
uint16_t qtype, uint16_t qclass, struct local_rrset* local_alias,
struct comm_reply* repinfo)
{
char srcip[128], respip[128], txt[512];
unsigned port;
struct respip_addr_info* respip_addr = respip_actinfo->addrinfo;
size_t txtlen = 0;
const char* actionstr = NULL;
if(local_alias)
qname = local_alias->rrset->rk.dname;
@ -1175,7 +1299,23 @@ respip_inform_print(struct respip_addr_info* respip_addr, uint8_t* qname,
addr_to_str(&repinfo->addr, repinfo->addrlen, srcip, sizeof(srcip));
addr_to_str(&respip_addr->addr, respip_addr->addrlen,
respip, sizeof(respip));
snprintf(txt, sizeof(txt), "%s/%d inform %s@%u", respip,
respip_addr->net, srcip, port);
log_nametypeclass(0, txt, qname, qtype, qclass);
if(respip_actinfo->rpz_log) {
txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen, "%s",
"RPZ applied ");
if(respip_actinfo->rpz_cname_override)
actionstr = rpz_action_to_string(
RPZ_CNAME_OVERRIDE_ACTION);
else
actionstr = rpz_action_to_string(
respip_action_to_rpz_action(
respip_actinfo->action));
}
if(respip_actinfo->log_name) {
txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen,
"[%s] ", respip_actinfo->log_name);
}
snprintf(txt+txtlen, sizeof(txt)-txtlen,
"%s/%d %s %s@%u", respip, respip_addr->net,
(actionstr) ? actionstr : "inform", srcip, port);
log_nametypeclass(NO_VERBOSE, txt, qname, qtype, qclass);
}

View File

@ -14,23 +14,42 @@
#include "util/module.h"
#include "services/localzone.h"
#include "util/locks.h"
/**
* Set of response IP addresses with associated actions and tags.
* Forward declaration only here. Actual definition is hidden within the
* module.
* Conceptual set of IP addresses for response AAAA or A records that should
* trigger special actions.
*/
struct respip_set;
struct respip_set {
struct regional* region;
struct rbtree_type ip_tree;
lock_rw_type lock; /* lock on the respip tree */
char* const* tagname; /* shallow copy of tag names, for logging */
int num_tags; /* number of tagname entries */
};
/** An address span with response control information */
struct resp_addr {
/** node in address tree */
struct addr_tree_node node;
/** lock on the node item */
lock_rw_type lock;
/** tag bitlist */
uint8_t* taglist;
/** length of the taglist (in bytes) */
size_t taglen;
/** action for this address span */
enum respip_action action;
/** "local data" for this node */
struct ub_packed_rrset_key* data;
};
/**
* Forward declaration for the structure that represents a node in the
* respip_set address tree
*/
struct resp_addr;
/**
* Forward declaration for the structure that represents a tree of view data.
*/
struct views;
struct respip_addr_info;
@ -60,6 +79,11 @@ struct respip_client_info {
*/
struct respip_action_info {
enum respip_action action;
int rpz_used;
int rpz_log;
int rpz_disabled;
char* log_name;
int rpz_cname_override;
struct respip_addr_info* addrinfo; /* set only for inform variants */
};
@ -124,12 +148,14 @@ int respip_views_apply_cfg(struct views* vs, struct config_file* cfg,
* @param new_repp: pointer placeholder for the merged reply. will be intact
* on error.
* @param region: allocator to build *new_repp.
* @param az: auth zones containing RPZ information.
* @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 reply_info** new_repp, struct regional* region,
struct auth_zones* az);
/**
* See if any IP-based action should apply to any IP address of AAAA/A answer
@ -148,6 +174,7 @@ int respip_merge_cname(struct reply_info* base_rep,
* @param alias_rrset: must not be NULL.
* @param search_only: if true, only check if an action would apply. actionp
* will be set (or intact) accordingly but the modified reply won't be built.
* @param az: auth zones containing RPZ information.
* @param region: allocator to build *new_repp.
* @return 1 on success, 0 on error.
*/
@ -156,7 +183,7 @@ int respip_rewrite_reply(const struct query_info* qinfo,
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);
int search_only, struct regional* region, struct auth_zones* az);
/**
* Get the response-ip function block.
@ -213,7 +240,7 @@ int respip_set_is_empty(const struct respip_set* set);
/**
* print log information for a query subject to an inform or inform-deny
* response-ip action.
* @param respip_addr: response-ip information that causes the action
* @param respip_actinfo: response-ip information that causes the action
* @param qname: query name in the context, will be ignored if local_alias is
* non-NULL.
* @param qtype: query type, in host byte order.
@ -223,8 +250,48 @@ int respip_set_is_empty(const struct respip_set* set);
* query name.
* @param repinfo: reply info containing the client's source address and port.
*/
void respip_inform_print(struct respip_addr_info* respip_addr, uint8_t* qname,
uint16_t qtype, uint16_t qclass, struct local_rrset* local_alias,
struct comm_reply* repinfo);
void respip_inform_print(struct respip_action_info* respip_actinfo,
uint8_t* qname, uint16_t qtype, uint16_t qclass,
struct local_rrset* local_alias, struct comm_reply* repinfo);
/**
* Find resp_addr in tree, create and add to tree if it does not exist.
* @param set: struct containing the tree and region to alloc new node on.
* should hold write lock.
* @param addr: address to look up.
* @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.
* @return newly created of found node, not holding lock.
*/
struct resp_addr*
respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage* addr,
socklen_t addrlen, int net, int create, const char* ipstr);
/**
* Add RR to resp_addr's RRset. Create RRset if not existing.
* @param region: region to alloc RR(set).
* @param raddr: resp_addr containing RRset. Must hold write lock.
* @param rrtype: RR type.
* @param rrclass: RR class.
* @param ttl: TTL.
* @param rdata: RDATA.
* @param rdata_len: length of rdata.
* @param rrstr: RR as string, for logging
* @param netblockstr: netblock as string, for logging
* @return 0 on error
*/
int
respip_enter_rr(struct regional* region, struct resp_addr* raddr,
uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata,
size_t rdata_len, const char* rrstr, const char* netblockstr);
/**
* Delete resp_addr node from tree.
* @param set: struct containing tree. Must hold write lock.
* @param node: node to delete. Not locked.
*/
void
respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node);
#endif /* RESPIP_RESPIP_H */

File diff suppressed because it is too large Load Diff

View File

@ -46,6 +46,7 @@
#include "util/rbtree.h"
#include "util/locks.h"
#include "services/mesh.h"
#include "services/rpz.h"
struct ub_packed_rrset_key;
struct regional;
struct config_file;
@ -81,6 +82,11 @@ struct auth_zones {
size_t num_query_up;
/** number of queries downstream */
size_t num_query_down;
/** first rpz item in linked list */
struct rpz* rpz_first;
/** rw lock for rpz linked list, needed when iterating or editing linked
* list. */
lock_rw_type rpz_lock;
};
/**
@ -126,6 +132,8 @@ struct auth_zone {
/** for upstream: this zone answers queries that unbound intends to
* send upstream. */
int for_upstream;
/** RPZ zones */
struct rpz* rpz;
/** zone has been deleted */
int zone_deleted;
/** deletelist pointer, unused normally except during delete */
@ -327,6 +335,8 @@ struct auth_probe {
/** the SOA probe udp event.
* on the workers event base. */
struct comm_point* cp;
/** is the cp for ip6 or ip4 */
int cp_is_ip6;
/** timeout for packets.
* on the workers event base. */
struct comm_timer* timer;
@ -378,6 +388,8 @@ struct auth_transfer {
* data or add of duplicate data). Flag is cleared once the retry
* with axfr is done. */
int ixfr_fail;
/** we saw an ixfr-indicating timeout, count of them */
int ixfr_possible_timeout_count;
/** we are doing IXFR right now */
int on_ixfr;
/** did we detect the current AXFR/IXFR serial number yet, 0 not yet,
@ -396,6 +408,9 @@ struct auth_transfer {
/** the transfer (TCP) to the master.
* on the workers event base. */
struct comm_point* cp;
/** timeout for the transfer.
* on the workers event base. */
struct comm_timer* timer;
};
/** list of addresses */
@ -453,10 +468,11 @@ struct auth_zones* auth_zones_create(void);
* @param az: auth zones structure
* @param cfg: config to apply.
* @param setup: if true, also sets up values in the auth zones structure
* @param is_rpz: set to 1 if at least one RPZ zone is configured.
* @return false on failure.
*/
int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg,
int setup);
int setup, int* is_rpz);
/** initial pick up of worker timeouts, ties events to worker event loop
* @param az: auth zones structure
@ -599,7 +615,7 @@ int auth_zones_startprobesequence(struct auth_zones* az,
struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t dclass);
/** read auth zone from zonefile. caller must lock zone. false on failure */
int auth_zone_read_zonefile(struct auth_zone* z);
int auth_zone_read_zonefile(struct auth_zone* z, struct config_file* cfg);
/** find serial number of zone or false if none (no SOA record) */
int auth_zone_get_serial(struct auth_zone* z, uint32_t* serial);
@ -645,6 +661,8 @@ int auth_xfer_transfer_http_callback(struct comm_point* c, void* arg, int err,
struct comm_reply* repinfo);
/** xfer probe timeout callback, part of task_probe */
void auth_xfer_probe_timer_callback(void* arg);
/** xfer transfer timeout callback, part of task_transfer */
void auth_xfer_transfer_timer_callback(void* arg);
/** mesh callback for task_probe on lookup of host names */
void auth_xfer_probe_lookup_callback(void* arg, int rcode,
struct sldns_buffer* buf, enum sec_status sec, char* why_bogus,

View File

@ -40,10 +40,12 @@
*/
#include "config.h"
#include "iterator/iter_delegpt.h"
#include "iterator/iter_utils.h"
#include "validator/val_nsec.h"
#include "validator/val_utils.h"
#include "services/cache/dns.h"
#include "services/cache/rrset.h"
#include "util/data/msgparse.h"
#include "util/data/msgreply.h"
#include "util/data/packed_rrset.h"
#include "util/data/dname.h"
@ -72,15 +74,15 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now,
time_t leeway, int pside, struct reply_info* qrep,
struct regional* region)
{
size_t i;
/* see if rrset already exists in cache, if not insert it. */
for(i=0; i<rep->rrset_count; i++) {
rep->ref[i].key = rep->rrsets[i];
rep->ref[i].id = rep->rrsets[i]->id;
/* update ref if it was in the cache */
size_t i;
/* see if rrset already exists in cache, if not insert it. */
for(i=0; i<rep->rrset_count; i++) {
rep->ref[i].key = rep->rrsets[i];
rep->ref[i].id = rep->rrsets[i]->id;
/* update ref if it was in the cache */
switch(rrset_cache_update(env->rrset_cache, &rep->ref[i],
env->alloc, now + ((ntohs(rep->ref[i].key->rk.type)==
LDNS_RR_TYPE_NS && !pside)?0:leeway))) {
env->alloc, now + ((ntohs(rep->ref[i].key->rk.type)==
LDNS_RR_TYPE_NS && !pside)?0:leeway))) {
case 0: /* ref unchanged, item inserted */
break;
case 2: /* ref updated, cache is superior */
@ -103,9 +105,9 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now,
* the fallthrough warning */
/* fallthrough */
case 1: /* ref updated, item inserted */
rep->rrsets[i] = rep->ref[i].key;
rep->rrsets[i] = rep->ref[i].key;
}
}
}
}
/** delete message from message cache */
@ -271,7 +273,7 @@ find_add_addrs(struct module_env* env, uint16_t qclass,
akey = rrset_cache_lookup(env->rrset_cache, ns->name,
ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0);
if(akey) {
if(!delegpt_add_rrset_A(dp, region, akey, 0)) {
if(!delegpt_add_rrset_A(dp, region, akey, 0, NULL)) {
lock_rw_unlock(&akey->entry.lock);
return 0;
}
@ -291,7 +293,7 @@ find_add_addrs(struct module_env* env, uint16_t qclass,
akey = rrset_cache_lookup(env->rrset_cache, ns->name,
ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
if(akey) {
if(!delegpt_add_rrset_AAAA(dp, region, akey, 0)) {
if(!delegpt_add_rrset_AAAA(dp, region, akey, 0, NULL)) {
lock_rw_unlock(&akey->entry.lock);
return 0;
}
@ -325,7 +327,8 @@ cache_fill_missing(struct module_env* env, uint16_t qclass,
akey = rrset_cache_lookup(env->rrset_cache, ns->name,
ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0);
if(akey) {
if(!delegpt_add_rrset_A(dp, region, akey, ns->lame)) {
if(!delegpt_add_rrset_A(dp, region, akey, ns->lame,
NULL)) {
lock_rw_unlock(&akey->entry.lock);
return 0;
}
@ -345,7 +348,8 @@ cache_fill_missing(struct module_env* env, uint16_t qclass,
akey = rrset_cache_lookup(env->rrset_cache, ns->name,
ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
if(akey) {
if(!delegpt_add_rrset_AAAA(dp, region, akey, ns->lame)) {
if(!delegpt_add_rrset_AAAA(dp, region, akey, ns->lame,
NULL)) {
lock_rw_unlock(&akey->entry.lock);
return 0;
}
@ -531,31 +535,51 @@ gen_dns_msg(struct regional* region, struct query_info* q, size_t num)
}
struct dns_msg*
tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
struct regional* region, time_t now, struct regional* scratch)
tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
struct regional* region, time_t now, int allow_expired,
struct regional* scratch)
{
struct dns_msg* msg;
size_t i;
if(now > r->ttl)
return NULL;
int is_expired = 0;
time_t now_control = now;
if(now > r->ttl) {
/* Check if we are allowed to serve expired */
if(allow_expired) {
if(env->cfg->serve_expired_ttl &&
r->serve_expired_ttl < now) {
return NULL;
}
} else {
return NULL;
}
/* Change the current time so we can pass the below TTL checks when
* serving expired data. */
now_control = r->ttl - env->cfg->serve_expired_reply_ttl;
is_expired = 1;
}
msg = gen_dns_msg(region, q, r->rrset_count);
if(!msg)
return NULL;
if(!msg) return NULL;
msg->rep->flags = r->flags;
msg->rep->qdcount = r->qdcount;
msg->rep->ttl = r->ttl - now;
msg->rep->ttl = is_expired
?SERVE_EXPIRED_REPLY_TTL
:r->ttl - now;
if(r->prefetch_ttl > now)
msg->rep->prefetch_ttl = r->prefetch_ttl - now;
else msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
else
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
msg->rep->security = r->security;
msg->rep->an_numrrsets = r->an_numrrsets;
msg->rep->ns_numrrsets = r->ns_numrrsets;
msg->rep->ar_numrrsets = r->ar_numrrsets;
msg->rep->rrset_count = r->rrset_count;
msg->rep->authoritative = r->authoritative;
if(!rrset_array_lock(r->ref, r->rrset_count, now))
msg->rep->authoritative = r->authoritative;
if(!rrset_array_lock(r->ref, r->rrset_count, now_control)) {
return NULL;
}
if(r->an_numrrsets > 0 && (r->rrsets[0]->rk.type == htons(
LDNS_RR_TYPE_CNAME) || r->rrsets[0]->rk.type == htons(
LDNS_RR_TYPE_DNAME)) && !reply_check_cname_chain(q, r)) {
@ -569,7 +593,7 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
return NULL;
}
for(i=0; i<msg->rep->rrset_count; i++) {
msg->rep->rrsets[i] = packed_rrset_copy_region(r->rrsets[i],
msg->rep->rrsets[i] = packed_rrset_copy_region(r->rrsets[i],
region, now);
if(!msg->rep->rrsets[i]) {
rrset_array_unlock(r->ref, r->rrset_count);
@ -721,6 +745,19 @@ fill_any(struct module_env* env,
int i, num=6; /* number of RR types to look up */
log_assert(lookup[num] == 0);
if(env->cfg->deny_any) {
/* return empty message */
msg = dns_msg_create(qname, qnamelen, qtype, qclass,
region, 0);
if(!msg) {
return NULL;
}
/* set NOTIMPL for RFC 8482 */
msg->rep->flags |= LDNS_RCODE_NOTIMPL;
msg->rep->security = sec_status_indeterminate;
return msg;
}
for(i=0; i<num; i++) {
/* look up this RR for inclusion in type ANY response */
struct ub_packed_rrset_key* rrset = rrset_cache_lookup(
@ -783,7 +820,7 @@ dns_cache_lookup(struct module_env* env,
if(e) {
struct msgreply_entry* key = (struct msgreply_entry*)e->key;
struct reply_info* data = (struct reply_info*)e->data;
struct dns_msg* msg = tomsg(env, &key->key, data, region, now,
struct dns_msg* msg = tomsg(env, &key->key, data, region, now, 0,
scratch);
if(msg) {
lock_rw_unlock(&e->lock);
@ -885,34 +922,38 @@ dns_cache_lookup(struct module_env* env,
* Empty nonterminals are NOERROR, so an NXDOMAIN for foo
* means bla.foo also does not exist. The DNSSEC proofs are
* the same. We search upwards for NXDOMAINs. */
if(env->cfg->harden_below_nxdomain)
while(!dname_is_root(k.qname)) {
dname_remove_label(&k.qname, &k.qname_len);
h = query_info_hash(&k, flags);
e = slabhash_lookup(env->msg_cache, h, &k, 0);
if(!e && k.qtype != LDNS_RR_TYPE_A &&
env->cfg->qname_minimisation) {
k.qtype = LDNS_RR_TYPE_A;
if(env->cfg->harden_below_nxdomain) {
while(!dname_is_root(k.qname)) {
dname_remove_label(&k.qname, &k.qname_len);
h = query_info_hash(&k, flags);
e = slabhash_lookup(env->msg_cache, h, &k, 0);
}
if(e) {
struct reply_info* data = (struct reply_info*)e->data;
struct dns_msg* msg;
if(FLAGS_GET_RCODE(data->flags) == LDNS_RCODE_NXDOMAIN
&& data->security == sec_status_secure
&& (msg=tomsg(env, &k, data, region, now, scratch))){
lock_rw_unlock(&e->lock);
msg->qinfo.qname=qname;
msg->qinfo.qname_len=qnamelen;
/* check that DNSSEC really works out */
msg->rep->security = sec_status_unchecked;
return msg;
if(!e && k.qtype != LDNS_RR_TYPE_A &&
env->cfg->qname_minimisation) {
k.qtype = LDNS_RR_TYPE_A;
h = query_info_hash(&k, flags);
e = slabhash_lookup(env->msg_cache, h, &k, 0);
}
lock_rw_unlock(&e->lock);
if(e) {
struct reply_info* data = (struct reply_info*)e->data;
struct dns_msg* msg;
if(FLAGS_GET_RCODE(data->flags) == LDNS_RCODE_NXDOMAIN
&& data->security == sec_status_secure
&& (data->an_numrrsets == 0 ||
ntohs(data->rrsets[0]->rk.type) != LDNS_RR_TYPE_CNAME)
&& (msg=tomsg(env, &k, data, region, now, 0, scratch))) {
lock_rw_unlock(&e->lock);
msg->qinfo.qname=qname;
msg->qinfo.qname_len=qnamelen;
/* check that DNSSEC really works out */
msg->rep->security = sec_status_unchecked;
iter_scrub_nxdomain(msg);
return msg;
}
lock_rw_unlock(&e->lock);
}
k.qtype = qtype;
}
k.qtype = qtype;
}
}
/* fill common RR types for ANY response to avoid requery */
if(qtype == LDNS_RR_TYPE_ANY) {

View File

@ -143,11 +143,14 @@ struct delegpt* dns_cache_find_delegation(struct module_env* env,
* @param r: reply info that, together with qname, will make up the dns message.
* @param region: where to allocate dns message.
* @param now: the time now, for check if TTL on cache entry is ok.
* @param allow_expired: if true and serve-expired is enabled, it will allow
* for expired dns_msg to be generated based on the configured serve-expired
* logic.
* @param scratch: where to allocate temporary data.
* */
struct dns_msg* tomsg(struct module_env* env, struct query_info* q,
struct reply_info* r, struct regional* region, time_t now,
struct regional* scratch);
int allow_expired, struct regional* scratch);
/**
* Find cached message
@ -160,7 +163,7 @@ struct dns_msg* tomsg(struct module_env* env, struct query_info* q,
* @param region: where to allocate result.
* @param scratch: where to allocate temporary data.
* @param no_partial: if true, only complete messages and not a partial
* one (with only the start of the CNAME chain and not the rest).
* one (with only the start of the CNAME chain and not the rest).
* @return new response message (alloced in region, rrsets do not have IDs).
* or NULL on error or if not found in cache.
* TTLs are made relative to the current time.

View File

@ -41,6 +41,8 @@
#include "config.h"
#include "sldns/rrdef.h"
#include "sldns/str2wire.h"
#include "sldns/sbuffer.h"
#include "sldns/wire2str.h"
#include "services/cache/infra.h"
#include "util/storage/slabhash.h"
#include "util/storage/lookup3.h"
@ -907,7 +909,8 @@ int infra_rate_max(void* data, time_t now)
}
int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name,
size_t namelen, time_t timenow)
size_t namelen, time_t timenow, struct query_info* qinfo,
struct comm_reply* replylist)
{
int lim, max;
struct lruhash_entry* entry;
@ -930,9 +933,19 @@ int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name,
lock_rw_unlock(&entry->lock);
if(premax < lim && max >= lim) {
char buf[257];
char buf[257], qnm[257], ts[12], cs[12], ip[128];
dname_str(name, buf);
verbose(VERB_OPS, "ratelimit exceeded %s %d", buf, lim);
dname_str(qinfo->qname, qnm);
sldns_wire2str_type_buf(qinfo->qtype, ts, sizeof(ts));
sldns_wire2str_class_buf(qinfo->qclass, cs, sizeof(cs));
ip[0]=0;
if(replylist) {
addr_to_str((struct sockaddr_storage *)&replylist->addr,
replylist->addrlen, ip, sizeof(ip));
verbose(VERB_OPS, "ratelimit exceeded %s %d query %s %s %s from %s", buf, lim, qnm, cs, ts, ip);
} else {
verbose(VERB_OPS, "ratelimit exceeded %s %d query %s %s %s", buf, lim, qnm, cs, ts);
}
}
return (max < lim);
}
@ -991,7 +1004,7 @@ infra_get_mem(struct infra_cache* infra)
}
int infra_ip_ratelimit_inc(struct infra_cache* infra,
struct comm_reply* repinfo, time_t timenow)
struct comm_reply* repinfo, time_t timenow, struct sldns_buffer* buffer)
{
int max;
struct lruhash_entry* entry;
@ -1010,11 +1023,28 @@ int infra_ip_ratelimit_inc(struct infra_cache* infra,
lock_rw_unlock(&entry->lock);
if(premax < infra_ip_ratelimit && max >= infra_ip_ratelimit) {
char client_ip[128];
char client_ip[128], qnm[LDNS_MAX_DOMAINLEN+1+12+12];
addr_to_str((struct sockaddr_storage *)&repinfo->addr,
repinfo->addrlen, client_ip, sizeof(client_ip));
verbose(VERB_OPS, "ip_ratelimit exceeded %s %d",
client_ip, infra_ip_ratelimit);
qnm[0]=0;
if(sldns_buffer_limit(buffer)>LDNS_HEADER_SIZE &&
LDNS_QDCOUNT(sldns_buffer_begin(buffer))!=0) {
(void)sldns_wire2str_rrquestion_buf(
sldns_buffer_at(buffer, LDNS_HEADER_SIZE),
sldns_buffer_limit(buffer)-LDNS_HEADER_SIZE,
qnm, sizeof(qnm));
if(strlen(qnm)>0 && qnm[strlen(qnm)-1]=='\n')
qnm[strlen(qnm)-1] = 0; /*remove newline*/
if(strchr(qnm, '\t'))
*strchr(qnm, '\t') = ' ';
if(strchr(qnm, '\t'))
*strchr(qnm, '\t') = ' ';
verbose(VERB_OPS, "ip_ratelimit exceeded %s %d %s",
client_ip, infra_ip_ratelimit, qnm);
} else {
verbose(VERB_OPS, "ip_ratelimit exceeded %s %d (no query name)",
client_ip, infra_ip_ratelimit);
}
}
return (max <= infra_ip_ratelimit);
}

View File

@ -366,12 +366,15 @@ long long infra_get_host_rto(struct infra_cache* infra,
* @param name: zone name
* @param namelen: zone name length
* @param timenow: what time it is now.
* @param qinfo: for logging, query name.
* @param replylist: for logging, querier's address (if any).
* @return 1 if it could be incremented. 0 if the increment overshot the
* ratelimit or if in the previous second the ratelimit was exceeded.
* Failures like alloc failures are not returned (probably as 1).
*/
int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name,
size_t namelen, time_t timenow);
size_t namelen, time_t timenow, struct query_info* qinfo,
struct comm_reply* replylist);
/**
* Decrement the query rate counter for a delegation point.
@ -410,10 +413,12 @@ int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name,
* @param infra: infra cache
* @param repinfo: information about client
* @param timenow: what time it is now.
* @param buffer: with query for logging.
* @return 1 if it could be incremented. 0 if the increment overshot the
* ratelimit and the query should be dropped. */
int infra_ip_ratelimit_inc(struct infra_cache* infra,
struct comm_reply* repinfo, time_t timenow);
struct comm_reply* repinfo, time_t timenow,
struct sldns_buffer* buffer);
/**
* Get memory used by the infra cache.

View File

@ -53,6 +53,9 @@
#include "util/config_file.h"
#include "util/net_help.h"
#include "sldns/sbuffer.h"
#include "services/mesh.h"
#include "util/fptr_wlist.h"
#include "util/locks.h"
#ifdef HAVE_NETDB_H
#include <netdb.h>
@ -70,6 +73,18 @@
/** number of queued TCP connections for listen() */
#define TCP_BACKLOG 256
/** number of simultaneous requests a client can have */
#define TCP_MAX_REQ_SIMULTANEOUS 32
#ifndef THREADS_DISABLED
/** lock on the counter of stream buffer memory */
static lock_basic_type stream_wait_count_lock;
#endif
/** size (in bytes) of stream wait buffers */
static size_t stream_wait_count = 0;
/** is the lock initialised for stream wait buffers */
static int stream_wait_lock_inited = 0;
/**
* Debug print of the getaddrinfo returned address.
* @param addr: the address returned.
@ -247,6 +262,26 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
}
#endif /* SO_REUSEADDR */
#ifdef SO_REUSEPORT
# ifdef SO_REUSEPORT_LB
/* on FreeBSD 12 we have SO_REUSEPORT_LB that does loadbalance
* like SO_REUSEPORT on Linux. This is what the users want
* with the config option in unbound.conf; if we actually
* need local address and port reuse they'll also need to
* have SO_REUSEPORT set for them, assume it was _LB they want.
*/
if (reuseport && *reuseport &&
setsockopt(s, SOL_SOCKET, SO_REUSEPORT_LB, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
#ifdef ENOPROTOOPT
if(errno != ENOPROTOOPT || verbosity >= 3)
log_warn("setsockopt(.. SO_REUSEPORT_LB ..) failed: %s",
strerror(errno));
#endif
/* this option is not essential, we can continue */
*reuseport = 0;
}
# else /* no SO_REUSEPORT_LB */
/* try to set SO_REUSEPORT so that incoming
* queries are distributed evenly among the receiving threads.
* Each thread must have its own socket bound to the same port,
@ -263,6 +298,7 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
/* this option is not essential, we can continue */
*reuseport = 0;
}
# endif /* SO_REUSEPORT_LB */
#else
(void)reuseport;
#endif /* defined(SO_REUSEPORT) */
@ -565,7 +601,11 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
if(family==AF_INET6 && errno==EINVAL)
*noproto = 1;
else if(errno != EADDRINUSE &&
!(errno == EACCES && verbosity < 4 && !listen)) {
!(errno == EACCES && verbosity < 4 && !listen)
#ifdef EADDRNOTAVAIL
&& !(errno == EADDRNOTAVAIL && verbosity < 4 && !listen)
#endif
) {
log_err_addr("can't bind socket", strerror(errno),
(struct sockaddr_storage*)addr, addrlen);
}
@ -811,9 +851,16 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
#ifdef ENOPROTOOPT
/* squelch ENOPROTOOPT: freebsd server mode with kernel support
disabled, except when verbosity enabled for debugging */
if(errno != ENOPROTOOPT || verbosity >= 3)
if(errno != ENOPROTOOPT || verbosity >= 3) {
#endif
if(errno == EPERM) {
log_warn("Setting TCP Fast Open as server failed: %s ; this could likely be because sysctl net.inet.tcp.fastopen.enabled, net.inet.tcp.fastopen.server_enable, or net.ipv4.tcp_fastopen is disabled", strerror(errno));
} else {
log_err("Setting TCP Fast Open as server failed: %s", strerror(errno));
}
#ifdef ENOPROTOOPT
}
#endif
log_err("Setting TCP Fast Open as server failed: %s", strerror(errno));
}
#endif
return s;
@ -1235,6 +1282,10 @@ listen_create(struct comm_base* base, struct listen_port* ports,
free(front);
return NULL;
}
if(!stream_wait_lock_inited) {
lock_basic_init(&stream_wait_count_lock);
stream_wait_lock_inited = 1;
}
/* create comm points as needed */
while(ports) {
@ -1247,11 +1298,13 @@ listen_create(struct comm_base* base, struct listen_port* ports,
ports->ftype == listen_type_tcp_dnscrypt)
cp = comm_point_create_tcp(base, ports->fd,
tcp_accept_count, tcp_idle_timeout,
tcp_conn_limit, bufsize, cb, cb_arg);
tcp_conn_limit, bufsize, front->udp_buff,
cb, cb_arg);
else if(ports->ftype == listen_type_ssl) {
cp = comm_point_create_tcp(base, ports->fd,
tcp_accept_count, tcp_idle_timeout,
tcp_conn_limit, bufsize, cb, cb_arg);
tcp_conn_limit, bufsize, front->udp_buff,
cb, cb_arg);
cp->ssl = sslctx;
} else if(ports->ftype == listen_type_udpancil ||
ports->ftype == listen_type_udpancil_dnscrypt)
@ -1322,6 +1375,10 @@ listen_delete(struct listen_dnsport* front)
#endif
sldns_buffer_free(front->udp_buff);
free(front);
if(stream_wait_lock_inited) {
stream_wait_lock_inited = 0;
lock_basic_destroy(&stream_wait_count_lock);
}
}
struct listen_port*
@ -1479,3 +1536,373 @@ void listen_start_accept(struct listen_dnsport* listen)
}
}
struct tcp_req_info*
tcp_req_info_create(struct sldns_buffer* spoolbuf)
{
struct tcp_req_info* req = (struct tcp_req_info*)malloc(sizeof(*req));
if(!req) {
log_err("malloc failure for new stream outoforder processing structure");
return NULL;
}
memset(req, 0, sizeof(*req));
req->spool_buffer = spoolbuf;
return req;
}
void
tcp_req_info_delete(struct tcp_req_info* req)
{
if(!req) return;
tcp_req_info_clear(req);
/* cp is pointer back to commpoint that owns this struct and
* called delete on us */
/* spool_buffer is shared udp buffer, not deleted here */
free(req);
}
void tcp_req_info_clear(struct tcp_req_info* req)
{
struct tcp_req_open_item* open, *nopen;
struct tcp_req_done_item* item, *nitem;
if(!req) return;
/* free outstanding request mesh reply entries */
open = req->open_req_list;
while(open) {
nopen = open->next;
mesh_state_remove_reply(open->mesh, open->mesh_state, req->cp);
free(open);
open = nopen;
}
req->open_req_list = NULL;
req->num_open_req = 0;
/* free pending writable result packets */
item = req->done_req_list;
while(item) {
nitem = item->next;
lock_basic_lock(&stream_wait_count_lock);
stream_wait_count -= (sizeof(struct tcp_req_done_item)
+item->len);
lock_basic_unlock(&stream_wait_count_lock);
free(item->buf);
free(item);
item = nitem;
}
req->done_req_list = NULL;
req->num_done_req = 0;
req->read_is_closed = 0;
}
void
tcp_req_info_remove_mesh_state(struct tcp_req_info* req, struct mesh_state* m)
{
struct tcp_req_open_item* open, *prev = NULL;
if(!req || !m) return;
open = req->open_req_list;
while(open) {
if(open->mesh_state == m) {
struct tcp_req_open_item* next;
if(prev) prev->next = open->next;
else req->open_req_list = open->next;
/* caller has to manage the mesh state reply entry */
next = open->next;
free(open);
req->num_open_req --;
/* prev = prev; */
open = next;
continue;
}
prev = open;
open = open->next;
}
}
/** setup listening for read or write */
static void
tcp_req_info_setup_listen(struct tcp_req_info* req)
{
int wr = 0;
int rd = 0;
if(req->cp->tcp_byte_count != 0) {
/* cannot change, halfway through */
return;
}
if(!req->cp->tcp_is_reading)
wr = 1;
if(req->num_open_req + req->num_done_req < TCP_MAX_REQ_SIMULTANEOUS &&
!req->read_is_closed)
rd = 1;
if(wr) {
req->cp->tcp_is_reading = 0;
comm_point_stop_listening(req->cp);
comm_point_start_listening(req->cp, -1,
req->cp->tcp_timeout_msec);
} else if(rd) {
req->cp->tcp_is_reading = 1;
comm_point_stop_listening(req->cp);
comm_point_start_listening(req->cp, -1,
req->cp->tcp_timeout_msec);
/* and also read it (from SSL stack buffers), so
* no event read event is expected since the remainder of
* the TLS frame is sitting in the buffers. */
req->read_again = 1;
} else {
comm_point_stop_listening(req->cp);
comm_point_start_listening(req->cp, -1,
req->cp->tcp_timeout_msec);
comm_point_listen_for_rw(req->cp, 0, 0);
}
}
/** remove first item from list of pending results */
static struct tcp_req_done_item*
tcp_req_info_pop_done(struct tcp_req_info* req)
{
struct tcp_req_done_item* item;
log_assert(req->num_done_req > 0 && req->done_req_list);
item = req->done_req_list;
lock_basic_lock(&stream_wait_count_lock);
stream_wait_count -= (sizeof(struct tcp_req_done_item)+item->len);
lock_basic_unlock(&stream_wait_count_lock);
req->done_req_list = req->done_req_list->next;
req->num_done_req --;
return item;
}
/** Send given buffer and setup to write */
static void
tcp_req_info_start_write_buf(struct tcp_req_info* req, uint8_t* buf,
size_t len)
{
sldns_buffer_clear(req->cp->buffer);
sldns_buffer_write(req->cp->buffer, buf, len);
sldns_buffer_flip(req->cp->buffer);
req->cp->tcp_is_reading = 0; /* we are now writing */
}
/** pick up the next result and start writing it to the channel */
static void
tcp_req_pickup_next_result(struct tcp_req_info* req)
{
if(req->num_done_req > 0) {
/* unlist the done item from the list of pending results */
struct tcp_req_done_item* item = tcp_req_info_pop_done(req);
tcp_req_info_start_write_buf(req, item->buf, item->len);
free(item->buf);
free(item);
}
}
/** the read channel has closed */
int
tcp_req_info_handle_read_close(struct tcp_req_info* req)
{
verbose(VERB_ALGO, "tcp channel read side closed %d", req->cp->fd);
/* reset byte count for (potential) partial read */
req->cp->tcp_byte_count = 0;
/* if we still have results to write, pick up next and write it */
if(req->num_done_req != 0) {
tcp_req_pickup_next_result(req);
tcp_req_info_setup_listen(req);
return 1;
}
/* if nothing to do, this closes the connection */
if(req->num_open_req == 0 && req->num_done_req == 0)
return 0;
/* otherwise, we must be waiting for dns resolve, wait with timeout */
req->read_is_closed = 1;
tcp_req_info_setup_listen(req);
return 1;
}
void
tcp_req_info_handle_writedone(struct tcp_req_info* req)
{
/* back to reading state, we finished this write event */
sldns_buffer_clear(req->cp->buffer);
if(req->num_done_req == 0 && req->read_is_closed) {
/* no more to write and nothing to read, close it */
comm_point_drop_reply(&req->cp->repinfo);
return;
}
req->cp->tcp_is_reading = 1;
/* see if another result needs writing */
tcp_req_pickup_next_result(req);
/* see if there is more to write, if not stop_listening for writing */
/* see if new requests are allowed, if so, start_listening
* for reading */
tcp_req_info_setup_listen(req);
}
void
tcp_req_info_handle_readdone(struct tcp_req_info* req)
{
struct comm_point* c = req->cp;
/* we want to read up several requests, unless there are
* pending answers */
req->is_drop = 0;
req->is_reply = 0;
req->in_worker_handle = 1;
sldns_buffer_set_limit(req->spool_buffer, 0);
/* handle the current request */
/* this calls the worker handle request routine that could give
* a cache response, or localdata response, or drop the reply,
* or schedule a mesh entry for later */
fptr_ok(fptr_whitelist_comm_point(c->callback));
if( (*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, &c->repinfo) ) {
req->in_worker_handle = 0;
/* there is an answer, put it up. It is already in the
* c->buffer, just send it. */
/* since we were just reading a query, the channel is
* clear to write to */
send_it:
c->tcp_is_reading = 0;
comm_point_stop_listening(c);
comm_point_start_listening(c, -1, c->tcp_timeout_msec);
return;
}
req->in_worker_handle = 0;
/* it should be waiting in the mesh for recursion.
* If mesh failed to add a new entry and called commpoint_drop_reply.
* Then the mesh state has been cleared. */
if(req->is_drop) {
/* the reply has been dropped, stream has been closed. */
return;
}
/* If mesh failed(mallocfail) and called commpoint_send_reply with
* something like servfail then we pick up that reply below. */
if(req->is_reply) {
goto send_it;
}
sldns_buffer_clear(c->buffer);
/* if pending answers, pick up an answer and start sending it */
tcp_req_pickup_next_result(req);
/* if answers pending, start sending answers */
/* read more requests if we can have more requests */
tcp_req_info_setup_listen(req);
}
int
tcp_req_info_add_meshstate(struct tcp_req_info* req,
struct mesh_area* mesh, struct mesh_state* m)
{
struct tcp_req_open_item* item;
log_assert(req && mesh && m);
item = (struct tcp_req_open_item*)malloc(sizeof(*item));
if(!item) return 0;
item->next = req->open_req_list;
item->mesh = mesh;
item->mesh_state = m;
req->open_req_list = item;
req->num_open_req++;
return 1;
}
/** Add a result to the result list. At the end. */
static int
tcp_req_info_add_result(struct tcp_req_info* req, uint8_t* buf, size_t len)
{
struct tcp_req_done_item* last = NULL;
struct tcp_req_done_item* item;
size_t space;
/* see if we have space */
space = sizeof(struct tcp_req_done_item) + len;
lock_basic_lock(&stream_wait_count_lock);
if(stream_wait_count + space > stream_wait_max) {
lock_basic_unlock(&stream_wait_count_lock);
verbose(VERB_ALGO, "drop stream reply, no space left, in stream-wait-size");
return 0;
}
stream_wait_count += space;
lock_basic_unlock(&stream_wait_count_lock);
/* find last element */
last = req->done_req_list;
while(last && last->next)
last = last->next;
/* create new element */
item = (struct tcp_req_done_item*)malloc(sizeof(*item));
if(!item) {
log_err("malloc failure, for stream result list");
return 0;
}
item->next = NULL;
item->len = len;
item->buf = memdup(buf, len);
if(!item->buf) {
free(item);
log_err("malloc failure, adding reply to stream result list");
return 0;
}
/* link in */
if(last) last->next = item;
else req->done_req_list = item;
req->num_done_req++;
return 1;
}
void
tcp_req_info_send_reply(struct tcp_req_info* req)
{
if(req->in_worker_handle) {
/* reply from mesh is in the spool_buffer */
/* copy now, so that the spool buffer is free for other tasks
* before the callback is done */
sldns_buffer_clear(req->cp->buffer);
sldns_buffer_write(req->cp->buffer,
sldns_buffer_begin(req->spool_buffer),
sldns_buffer_limit(req->spool_buffer));
sldns_buffer_flip(req->cp->buffer);
req->is_reply = 1;
return;
}
/* now that the query has been handled, that mesh_reply entry
* should be removed, from the tcp_req_info list,
* the mesh state cleanup removes then with region_cleanup and
* replies_sent true. */
/* see if we can send it straight away (we are not doing
* anything else). If so, copy to buffer and start */
if(req->cp->tcp_is_reading && req->cp->tcp_byte_count == 0) {
/* buffer is free, and was ready to read new query into,
* but we are now going to use it to send this answer */
tcp_req_info_start_write_buf(req,
sldns_buffer_begin(req->spool_buffer),
sldns_buffer_limit(req->spool_buffer));
/* switch to listen to write events */
comm_point_stop_listening(req->cp);
comm_point_start_listening(req->cp, -1,
req->cp->tcp_timeout_msec);
return;
}
/* queue up the answer behind the others already pending */
if(!tcp_req_info_add_result(req, sldns_buffer_begin(req->spool_buffer),
sldns_buffer_limit(req->spool_buffer))) {
/* drop the connection, we are out of resources */
comm_point_drop_reply(&req->cp->repinfo);
}
}
size_t tcp_req_info_get_stream_buffer_size(void)
{
size_t s;
if(!stream_wait_lock_inited)
return stream_wait_count;
lock_basic_lock(&stream_wait_count_lock);
s = stream_wait_count;
lock_basic_unlock(&stream_wait_count_lock);
return s;
}

View File

@ -237,4 +237,134 @@ int create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
*/
int create_local_accept_sock(const char* path, int* noproto, int use_systemd);
/**
* TCP request info. List of requests outstanding on the channel, that
* are asked for but not yet answered back.
*/
struct tcp_req_info {
/** the TCP comm point for this. Its buffer is used for read/write */
struct comm_point* cp;
/** the buffer to use to spool reply from mesh into,
* it can then be copied to the result list and written.
* it is a pointer to the shared udp buffer. */
struct sldns_buffer* spool_buffer;
/** are we in worker_handle function call (for recursion callback)*/
int in_worker_handle;
/** is the comm point dropped (by worker handle).
* That means we have to disconnect the channel. */
int is_drop;
/** is the comm point set to send_reply (by mesh new client in worker
* handle), if so answer is available in c.buffer */
int is_reply;
/** read channel has closed, just write pending results */
int read_is_closed;
/** read again */
int read_again;
/** number of outstanding requests */
int num_open_req;
/** list of outstanding requests */
struct tcp_req_open_item* open_req_list;
/** number of pending writeable results */
int num_done_req;
/** list of pending writable result packets, malloced one at a time */
struct tcp_req_done_item* done_req_list;
};
/**
* List of open items in TCP channel
*/
struct tcp_req_open_item {
/** next in list */
struct tcp_req_open_item* next;
/** the mesh area of the mesh_state */
struct mesh_area* mesh;
/** the mesh state */
struct mesh_state* mesh_state;
};
/**
* List of done items in TCP channel
*/
struct tcp_req_done_item {
/** next in list */
struct tcp_req_done_item* next;
/** the buffer with packet contents */
uint8_t* buf;
/** length of the buffer */
size_t len;
};
/**
* Create tcp request info structure that keeps track of open
* requests on the TCP channel that are resolved at the same time,
* and the pending results that have to get written back to that client.
* @param spoolbuf: shared buffer
* @return new structure or NULL on alloc failure.
*/
struct tcp_req_info* tcp_req_info_create(struct sldns_buffer* spoolbuf);
/**
* Delete tcp request structure. Called by owning commpoint.
* Removes mesh entry references and stored results from the lists.
* @param req: the tcp request info
*/
void tcp_req_info_delete(struct tcp_req_info* req);
/**
* Clear tcp request structure. Removes list entries, sets it up ready
* for the next connection.
* @param req: tcp request info structure.
*/
void tcp_req_info_clear(struct tcp_req_info* req);
/**
* Remove mesh state entry from list in tcp_req_info.
* caller has to manage the mesh state reply entry in the mesh state.
* @param req: the tcp req info that has the entry removed from the list.
* @param m: the state removed from the list.
*/
void tcp_req_info_remove_mesh_state(struct tcp_req_info* req,
struct mesh_state* m);
/**
* Handle write done of the last result packet
* @param req: the tcp req info.
*/
void tcp_req_info_handle_writedone(struct tcp_req_info* req);
/**
* Handle read done of a new request from the client
* @param req: the tcp req info.
*/
void tcp_req_info_handle_readdone(struct tcp_req_info* req);
/**
* Add mesh state to the tcp req list of open requests.
* So the comm_reply can be removed off the mesh reply list when
* the tcp channel has to be closed (for other reasons then that that
* request was done, eg. channel closed by client or some format error).
* @param req: tcp req info structure. It keeps track of the simultaneous
* requests and results on a tcp (or TLS) channel.
* @param mesh: mesh area for the state.
* @param m: mesh state to add.
* @return 0 on failure (malloc failure).
*/
int tcp_req_info_add_meshstate(struct tcp_req_info* req,
struct mesh_area* mesh, struct mesh_state* m);
/**
* Send reply on tcp simultaneous answer channel. May queue it up.
* @param req: request info structure.
*/
void tcp_req_info_send_reply(struct tcp_req_info* req);
/** the read channel has closed
* @param req: request. remaining queries are looked up and answered.
* @return zero if nothing to do, just close the tcp.
*/
int tcp_req_info_handle_read_close(struct tcp_req_info* req);
/** get the size of currently used tcp stream wait buffers (in bytes) */
size_t tcp_req_info_get_stream_buffer_size(void);
#endif /* LISTEN_DNSPORT_H */

View File

@ -41,7 +41,6 @@
#include "config.h"
#include "services/localzone.h"
#include "sldns/str2wire.h"
#include "sldns/sbuffer.h"
#include "util/regional.h"
#include "util/config_file.h"
#include "util/data/dname.h"
@ -395,9 +394,30 @@ rrset_insert_rr(struct regional* region, struct packed_rrset_data* pd,
return 1;
}
/** find a data node by exact name */
static struct local_data*
lz_find_node(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs)
/** Delete RR from local-zone RRset, wastes memory as the deleted RRs cannot be
* free'd (regionally alloc'd) */
int
local_rrset_remove_rr(struct packed_rrset_data* pd, size_t index)
{
log_assert(pd->count > 0);
if(index >= pd->count) {
log_warn("Trying to remove RR with out of bound index");
return 0;
}
if(index + 1 < pd->count) {
/* not removing last element */
size_t nexti = index + 1;
size_t num = pd->count - nexti;
memmove(pd->rr_len+index, pd->rr_len+nexti, sizeof(*pd->rr_len)*num);
memmove(pd->rr_ttl+index, pd->rr_ttl+nexti, sizeof(*pd->rr_ttl)*num);
memmove(pd->rr_data+index, pd->rr_data+nexti, sizeof(*pd->rr_data)*num);
}
pd->count--;
return 1;
}
struct local_data*
local_zone_find_data(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs)
{
struct local_data key;
key.node.key = &key;
@ -412,7 +432,7 @@ static int
lz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen,
int nmlabs, struct local_data** res)
{
struct local_data* ld = lz_find_node(z, nm, nmlen, nmlabs);
struct local_data* ld = local_zone_find_data(z, nm, nmlen, nmlabs);
if(!ld) {
/* create a domain name to store rr. */
ld = (struct local_data*)regional_alloc_zero(z->region,
@ -443,45 +463,24 @@ lz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen,
return 1;
}
/** enter data RR into auth zone */
static int
lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr)
int
local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen,
int nmlabs, uint16_t rrtype, uint16_t rrclass, time_t ttl,
uint8_t* rdata, size_t rdata_len, const char* rrstr)
{
uint8_t* nm;
size_t nmlen;
int nmlabs;
struct local_data* node;
struct local_rrset* rrset;
struct packed_rrset_data* pd;
uint16_t rrtype = 0, rrclass = 0;
time_t ttl = 0;
uint8_t rr[LDNS_RR_BUF_SIZE];
uint8_t* rdata;
size_t rdata_len;
if(!rrstr_get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, rr,
sizeof(rr), &rdata, &rdata_len)) {
log_err("bad local-data: %s", rrstr);
return 0;
}
log_assert(z->dclass == rrclass);
if(z->type == local_zone_redirect &&
query_dname_compare(z->name, nm) != 0) {
log_err("local-data in redirect zone must reside at top of zone"
", not at %s", rrstr);
free(nm);
return 0;
}
nmlabs = dname_count_size_labels(nm, &nmlen);
if(!lz_find_create_node(z, nm, nmlen, nmlabs, &node)) {
free(nm);
return 0;
}
log_assert(node);
free(nm);
/* Reject it if we would end up having CNAME and other data (including
* another CNAME) for a redirect zone. */
if(z->type == local_zone_redirect && node->rrsets) {
if((z->type == local_zone_redirect ||
z->type == local_zone_inform_redirect) && node->rrsets) {
const char* othertype = NULL;
if (rrtype == LDNS_RR_TYPE_CNAME)
othertype = "other";
@ -518,6 +517,39 @@ lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr)
return rrset_insert_rr(z->region, pd, rdata, rdata_len, ttl, rrstr);
}
/** enter data RR into auth zone */
int
lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr)
{
uint8_t* nm;
size_t nmlen;
int nmlabs, ret;
uint16_t rrtype = 0, rrclass = 0;
time_t ttl = 0;
uint8_t rr[LDNS_RR_BUF_SIZE];
uint8_t* rdata;
size_t rdata_len;
if(!rrstr_get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, rr,
sizeof(rr), &rdata, &rdata_len)) {
log_err("bad local-data: %s", rrstr);
return 0;
}
log_assert(z->dclass == rrclass);
if((z->type == local_zone_redirect ||
z->type == local_zone_inform_redirect) &&
query_dname_compare(z->name, nm) != 0) {
log_err("local-data in redirect zone must reside at top of zone"
", not at %s", rrstr);
free(nm);
return 0;
}
nmlabs = dname_count_size_labels(nm, &nmlen);
ret = local_zone_enter_rr(z, nm, nmlen, nmlabs, rrtype, rrclass, ttl,
rdata, rdata_len, rrstr);
free(nm);
return ret;
}
/** enter a data RR into auth data; a zone for it must exist */
static int
lz_enter_rr_str(struct local_zones* zones, const char* rr)
@ -821,12 +853,12 @@ int local_zone_enter_defaults(struct local_zones* zones, struct config_file* cfg
log_err("out of memory adding default zone");
return 0;
}
/* test. zone (RFC 7686) */
/* test. zone (RFC 6761) */
if(!add_empty_default(zones, cfg, "test.")) {
log_err("out of memory adding default zone");
return 0;
}
/* invalid. zone (RFC 7686) */
/* invalid. zone (RFC 6761) */
if(!add_empty_default(zones, cfg, "invalid.")) {
log_err("out of memory adding default zone");
return 0;
@ -1111,6 +1143,22 @@ local_zones_find(struct local_zones* zones,
return (struct local_zone*)rbtree_search(&zones->ztree, &key);
}
struct local_zone*
local_zones_find_le(struct local_zones* zones,
uint8_t* name, size_t len, int labs, uint16_t dclass,
int* exact)
{
struct local_zone key;
rbnode_type *node;
key.node.key = &key;
key.dclass = dclass;
key.name = name;
key.namelen = len;
key.namelabs = labs;
*exact = rbtree_find_less_equal(&zones->ztree, &key, &node);
return (struct local_zone*)node;
}
/** print all RRsets in local zone */
static void
local_zone_out(struct local_zone* z)
@ -1119,7 +1167,7 @@ local_zone_out(struct local_zone* z)
struct local_rrset* p;
RBTREE_FOR(d, struct local_data*, &z->data) {
for(p = d->rrsets; p; p = p->next) {
log_nametypeclass(0, "rrset", d->name,
log_nametypeclass(NO_VERBOSE, "rrset", d->name,
ntohs(p->rrset->rk.type),
ntohs(p->rrset->rk.rrset_class));
}
@ -1136,7 +1184,7 @@ void local_zones_print(struct local_zones* zones)
lock_rw_rdlock(&z->lock);
snprintf(buf, sizeof(buf), "%s zone",
local_zone_type2str(z->type));
log_nametypeclass(0, buf, z->name, 0, z->dclass);
log_nametypeclass(NO_VERBOSE, buf, z->name, 0, z->dclass);
local_zone_out(z);
lock_rw_unlock(&z->lock);
}
@ -1307,8 +1355,7 @@ find_tag_datas(struct query_info* qinfo, struct config_strlist* list,
return result;
}
/** answer local data match */
static int
int
local_data_answer(struct local_zone* z, struct module_env* env,
struct query_info* qinfo, struct edns_data* edns,
struct comm_reply* repinfo, sldns_buffer* buf,
@ -1323,7 +1370,8 @@ local_data_answer(struct local_zone* z, struct module_env* env,
key.name = qinfo->qname;
key.namelen = qinfo->qname_len;
key.namelabs = labs;
if(lz_type == local_zone_redirect) {
if(lz_type == local_zone_redirect ||
lz_type == local_zone_inform_redirect) {
key.name = z->name;
key.namelen = z->namelen;
key.namelabs = z->namelabs;
@ -1355,22 +1403,77 @@ local_data_answer(struct local_zone* z, struct module_env* env,
return 0;
/* Special case for alias matching. See local_data_answer(). */
if(lz_type == local_zone_redirect &&
if((lz_type == local_zone_redirect ||
lz_type == local_zone_inform_redirect) &&
qinfo->qtype != LDNS_RR_TYPE_CNAME &&
lr->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
uint8_t* ctarget;
size_t ctargetlen = 0;
qinfo->local_alias =
regional_alloc_zero(temp, sizeof(struct local_rrset));
if(!qinfo->local_alias)
return 0; /* out of memory */
qinfo->local_alias->rrset =
regional_alloc_init(temp, lr->rrset, sizeof(*lr->rrset));
qinfo->local_alias->rrset = regional_alloc_init(
temp, lr->rrset, sizeof(*lr->rrset));
if(!qinfo->local_alias->rrset)
return 0; /* out of memory */
qinfo->local_alias->rrset->rk.dname = qinfo->qname;
qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len;
get_cname_target(lr->rrset, &ctarget, &ctargetlen);
if(!ctargetlen)
return 0; /* invalid cname */
if(dname_is_wild(ctarget)) {
/* synthesize cname target */
struct packed_rrset_data* d;
/* -3 for wildcard label and root label from qname */
size_t newtargetlen = qinfo->qname_len + ctargetlen - 3;
log_assert(ctargetlen >= 3);
log_assert(qinfo->qname_len >= 1);
if(newtargetlen > LDNS_MAX_DOMAINLEN) {
qinfo->local_alias = NULL;
local_error_encode(qinfo, env, edns, repinfo,
buf, temp, LDNS_RCODE_YXDOMAIN,
(LDNS_RCODE_YXDOMAIN|BIT_AA));
return 1;
}
memset(&qinfo->local_alias->rrset->entry, 0,
sizeof(qinfo->local_alias->rrset->entry));
qinfo->local_alias->rrset->entry.key =
qinfo->local_alias->rrset;
qinfo->local_alias->rrset->entry.hash =
rrset_key_hash(&qinfo->local_alias->rrset->rk);
d = (struct packed_rrset_data*)regional_alloc_zero(temp,
sizeof(struct packed_rrset_data) + sizeof(size_t) +
sizeof(uint8_t*) + sizeof(time_t) + sizeof(uint16_t)
+ newtargetlen);
if(!d)
return 0; /* out of memory */
qinfo->local_alias->rrset->entry.data = d;
d->ttl = 0; /* 0 for synthesized CNAME TTL */
d->count = 1;
d->rrsig_count = 0;
d->trust = rrset_trust_ans_noAA;
d->rr_len = (size_t*)((uint8_t*)d +
sizeof(struct packed_rrset_data));
d->rr_len[0] = newtargetlen + sizeof(uint16_t);
packed_rrset_ptr_fixup(d);
d->rr_ttl[0] = d->ttl;
sldns_write_uint16(d->rr_data[0], newtargetlen);
/* write qname */
memmove(d->rr_data[0] + sizeof(uint16_t), qinfo->qname,
qinfo->qname_len - 1);
/* write cname target wilcard wildcard label */
memmove(d->rr_data[0] + sizeof(uint16_t) +
qinfo->qname_len - 1, ctarget + 2,
ctargetlen - 2);
}
return 1;
}
if(lz_type == local_zone_redirect) {
if(lz_type == local_zone_redirect ||
lz_type == local_zone_inform_redirect) {
/* convert rrset name to query name; like a wildcard */
struct ub_packed_rrset_key r = *lr->rrset;
r.rk.dname = qinfo->qname;
@ -1411,26 +1514,15 @@ local_zone_does_not_cover(struct local_zone* z, struct query_info* qinfo,
return (lr == NULL);
}
/**
* Answer in case where no exact match is found.
* @param z: zone for query.
* @param env: module environment.
* @param qinfo: query.
* @param edns: edns from query.
* @param repinfo: source address for checks. may be NULL.
* @param buf: buffer for answer.
* @param temp: temp region for encoding.
* @param ld: local data, if NULL, no such name exists in localdata.
* @param lz_type: type of the local zone.
* @return 1 if a reply is to be sent, 0 if not.
*/
static int
lz_zone_answer(struct local_zone* z, struct module_env* env,
int
local_zones_zone_answer(struct local_zone* z, struct module_env* env,
struct query_info* qinfo, struct edns_data* edns,
struct comm_reply* repinfo, sldns_buffer* buf, struct regional* temp,
struct local_data* ld, enum localzone_type lz_type)
{
if(lz_type == local_zone_deny || lz_type == local_zone_inform_deny) {
if(lz_type == local_zone_deny ||
lz_type == local_zone_always_deny ||
lz_type == local_zone_inform_deny) {
/** no reply at all, signal caller by clearing buffer. */
sldns_buffer_clear(buf);
sldns_buffer_flip(buf);
@ -1442,7 +1534,9 @@ lz_zone_answer(struct local_zone* z, struct module_env* env,
return 1;
} else if(lz_type == local_zone_static ||
lz_type == local_zone_redirect ||
lz_type == local_zone_always_nxdomain) {
lz_type == local_zone_inform_redirect ||
lz_type == local_zone_always_nxdomain ||
lz_type == local_zone_always_nodata) {
/* for static, reply nodata or nxdomain
* for redirect, reply nodata */
/* no additional section processing,
@ -1450,7 +1544,9 @@ lz_zone_answer(struct local_zone* z, struct module_env* env,
* or using closest match for NSEC.
* or using closest match for returning delegation downwards
*/
int rcode = (ld || lz_type == local_zone_redirect)?
int rcode = (ld || lz_type == local_zone_redirect ||
lz_type == local_zone_inform_redirect ||
lz_type == local_zone_always_nodata)?
LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN;
if(z->soa)
return local_encode(qinfo, env, edns, repinfo, buf, temp,
@ -1493,7 +1589,7 @@ lz_inform_print(struct local_zone* z, struct query_info* qinfo,
addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip));
snprintf(txt, sizeof(txt), "%s %s %s@%u", zname, local_zone_type2str(z->type), ip,
(unsigned)port);
log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass);
log_nametypeclass(NO_VERBOSE, txt, qinfo->qname, qinfo->qtype, qinfo->qclass);
}
static enum localzone_type
@ -1624,13 +1720,17 @@ local_zones_answer(struct local_zones* zones, struct module_env* env,
}
}
if((env->cfg->log_local_actions ||
lzt == local_zone_inform || lzt == local_zone_inform_deny)
lzt == local_zone_inform ||
lzt == local_zone_inform_deny ||
lzt == local_zone_inform_redirect)
&& repinfo)
lz_inform_print(z, qinfo, repinfo);
if(lzt != local_zone_always_refuse
&& lzt != local_zone_always_transparent
&& lzt != local_zone_always_nxdomain
&& lzt != local_zone_always_nodata
&& lzt != local_zone_always_deny
&& local_data_answer(z, env, qinfo, edns, repinfo, buf, temp, labs,
&ld, lzt, tag, tag_datas, tag_datas_size, tagname, num_tags)) {
lock_rw_unlock(&z->lock);
@ -1638,7 +1738,7 @@ local_zones_answer(struct local_zones* zones, struct module_env* env,
* a local alias. */
return !qinfo->local_alias;
}
r = lz_zone_answer(z, env, qinfo, edns, repinfo, buf, temp, ld, lzt);
r = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp, ld, lzt);
lock_rw_unlock(&z->lock);
return r && !qinfo->local_alias; /* see above */
}
@ -1656,10 +1756,14 @@ const char* local_zone_type2str(enum localzone_type t)
case local_zone_nodefault: return "nodefault";
case local_zone_inform: return "inform";
case local_zone_inform_deny: return "inform_deny";
case local_zone_inform_redirect: return "inform_redirect";
case local_zone_always_transparent: return "always_transparent";
case local_zone_always_refuse: return "always_refuse";
case local_zone_always_nxdomain: return "always_nxdomain";
case local_zone_always_nodata: return "always_nodata";
case local_zone_always_deny: return "always_deny";
case local_zone_noview: return "noview";
case local_zone_invalid: return "invalid";
}
return "badtyped";
}
@ -1682,12 +1786,18 @@ int local_zone_str2type(const char* type, enum localzone_type* t)
*t = local_zone_inform;
else if(strcmp(type, "inform_deny") == 0)
*t = local_zone_inform_deny;
else if(strcmp(type, "inform_redirect") == 0)
*t = local_zone_inform_redirect;
else if(strcmp(type, "always_transparent") == 0)
*t = local_zone_always_transparent;
else if(strcmp(type, "always_refuse") == 0)
*t = local_zone_always_refuse;
else if(strcmp(type, "always_nxdomain") == 0)
*t = local_zone_always_nxdomain;
else if(strcmp(type, "always_nodata") == 0)
*t = local_zone_always_nodata;
else if(strcmp(type, "always_deny") == 0)
*t = local_zone_always_deny;
else if(strcmp(type, "noview") == 0)
*t = local_zone_noview;
else if(strcmp(type, "nodefault") == 0)
@ -1831,7 +1941,7 @@ del_empty_term(struct local_zone* z, struct local_data* d,
return;
dname_remove_label(&name, &len);
labs--;
d = lz_find_node(z, name, len, labs);
d = local_zone_find_data(z, name, len, labs);
}
}
@ -1864,7 +1974,7 @@ void local_zones_del_data(struct local_zones* zones,
z = local_zones_lookup(zones, name, len, labs, dclass, LDNS_RR_TYPE_DS);
if(z) {
lock_rw_wrlock(&z->lock);
d = lz_find_node(z, name, len, labs);
d = local_zone_find_data(z, name, len, labs);
if(d) {
del_local_rrset(d, LDNS_RR_TYPE_DS);
del_empty_term(z, d, name, len, labs);
@ -1885,7 +1995,7 @@ void local_zones_del_data(struct local_zones* zones,
lock_rw_unlock(&zones->lock);
/* find the domain */
d = lz_find_node(z, name, len, labs);
d = local_zone_find_data(z, name, len, labs);
if(d) {
/* no memory recycling for zone deletions ... */
d->rrsets = NULL;

View File

@ -46,6 +46,7 @@
#include "util/storage/dnstree.h"
#include "util/module.h"
#include "services/view.h"
#include "sldns/sbuffer.h"
struct packed_rrset_data;
struct ub_packed_rrset_key;
struct regional;
@ -83,14 +84,22 @@ enum localzone_type {
local_zone_inform,
/** log client address, and block (drop) */
local_zone_inform_deny,
/** log client address, and direct */
local_zone_inform_redirect,
/** resolve normally, even when there is local data */
local_zone_always_transparent,
/** answer with error, even when there is local data */
local_zone_always_refuse,
/** answer with nxdomain, even when there is local data */
local_zone_always_nxdomain,
/** answer with noerror/nodata, even when there is local data */
local_zone_always_nodata,
/** drop query, even when there is local data */
local_zone_always_deny,
/** answer not from the view, but global or no-answer */
local_zone_noview
local_zone_noview,
/** Invalid type, cannot be used to generate answer */
local_zone_invalid
};
/**
@ -308,6 +317,25 @@ int local_zones_answer(struct local_zones* zones, struct module_env* env,
struct config_strlist** tag_datas, size_t tag_datas_size,
char** tagname, int num_tags, struct view* view);
/**
* Answer using the local zone only (not local data used).
* @param z: zone for query.
* @param env: module environment.
* @param qinfo: query.
* @param edns: edns from query.
* @param repinfo: source address for checks. may be NULL.
* @param buf: buffer for answer.
* @param temp: temp region for encoding.
* @param ld: local data, if NULL, no such name exists in localdata.
* @param lz_type: type of the local zone.
* @return 1 if a reply is to be sent, 0 if not.
*/
int
local_zones_zone_answer(struct local_zone* z, struct module_env* env,
struct query_info* qinfo, struct edns_data* edns,
struct comm_reply* repinfo, sldns_buffer* buf, struct regional* temp,
struct local_data* ld, enum localzone_type lz_type);
/**
* Parse the string into localzone type.
*
@ -338,6 +366,22 @@ const char* local_zone_type2str(enum localzone_type t);
struct local_zone* local_zones_find(struct local_zones* zones,
uint8_t* name, size_t len, int labs, uint16_t dclass);
/**
* Find zone that with exactly or smaller name/class
* User must lock the tree or result zone.
* @param zones: the zones tree
* @param name: dname to lookup
* @param len: length of name.
* @param labs: labelcount of name.
* @param dclass: class to lookup.
* @param exact: 1 on return is this is an exact match.
* @return the exact or smaller local_zone or NULL.
*/
struct local_zone*
local_zones_find_le(struct local_zones* zones,
uint8_t* name, size_t len, int labs, uint16_t dclass,
int* exact);
/**
* Add a new zone. Caller must hold the zones lock.
* Adjusts the other zones as well (parent pointers) after insertion.
@ -471,6 +515,15 @@ int rrstr_get_rr_content(const char* str, uint8_t** nm, uint16_t* type,
int rrset_insert_rr(struct regional* region, struct packed_rrset_data* pd,
uint8_t* rdata, size_t rdata_len, time_t ttl, const char* rrstr);
/**
* Remove RR from rrset that is created using localzone's rrset_insert_rr.
* @param pd: the RRset containing the RR to remove
* @param index: index of RR to remove
* @return: 1 on success; 0 otherwise.
*/
int
local_rrset_remove_rr(struct packed_rrset_data* pd, size_t index);
/**
* Valid response ip actions for the IP-response-driven-action feature;
* defined here instead of in the respip module to enable sharing of enum
@ -491,12 +544,18 @@ enum respip_action {
respip_inform = local_zone_inform,
/** log query source and don't answer query */
respip_inform_deny = local_zone_inform_deny,
/** log query source and redirect */
respip_inform_redirect = local_zone_inform_redirect,
/** resolve normally, even when there is response-ip data */
respip_always_transparent = local_zone_always_transparent,
/** answer with 'refused' response */
respip_always_refuse = local_zone_always_refuse,
/** answer with 'no such domain' response */
respip_always_nxdomain = local_zone_always_nxdomain,
/** answer with nodata response */
respip_always_nodata = local_zone_always_nodata,
/** answer with nodata response */
respip_always_deny = local_zone_always_deny,
/* The rest of the values are only possible as
* access-control-tag-action */
@ -509,6 +568,64 @@ enum respip_action {
respip_transparent = local_zone_transparent,
/** gives response data (if any), else nodata answer. */
respip_typetransparent = local_zone_typetransparent,
/** type invalid */
respip_invalid = local_zone_invalid,
};
/**
* Get local data from local zone and encode answer.
* @param z: local zone to use
* @param env: module env
* @param qinfo: qinfo
* @param edns: edns data, for message encoding
* @param repinfo: reply info, for message encoding
* @param buf: commpoint buffer
* @param temp: scratchpad region
* @param labs: number of labels in qname
* @param ldp: where to store local data
* @param lz_type: type of local zone
* @param tag: matching tag index
* @param tag_datas: alc specific tag data list
* @param tag_datas_size: size of tag_datas
* @param tagname: list of names of tags, for logging purpose
* @param num_tags: number of tags
* @return 1 on success
*/
int
local_data_answer(struct local_zone* z, struct module_env* env,
struct query_info* qinfo, struct edns_data* edns,
struct comm_reply* repinfo, sldns_buffer* buf,
struct regional* temp, int labs, struct local_data** ldp,
enum localzone_type lz_type, int tag, struct config_strlist** tag_datas,
size_t tag_datas_size, char** tagname, int num_tags);
/**
* Add RR to local zone.
* @param z: local zone to add RR to
* @param nm: dname of RR
* @param nmlen: length of nm
* @param nmlabs: number of labels of nm
* @param rrtype: RR type
* @param rrclass: RR class
* @param ttl: TTL of RR to add
* @param rdata: RDATA of RR to add
* @param rdata_len: length of rdata
* @param rrstr: RR in string format, for logging
* @return: 1 on success
*/
int
local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen,
int nmlabs, uint16_t rrtype, uint16_t rrclass, time_t ttl,
uint8_t* rdata, size_t rdata_len, const char* rrstr);
/**
* Find a data node by exact name for a local zone
* @param z: local_zone containing data tree
* @param nm: name of local-data element to find
* @param nmlen: length of nm
* @param nmlabs: labs of nm
* @return local_data on exact match, NULL otherwise.
*/
struct local_data*
local_zone_find_data(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs);
#endif /* SERVICES_LOCALZONE_H */

View File

@ -46,6 +46,7 @@
#include "services/mesh.h"
#include "services/outbound_list.h"
#include "services/cache/dns.h"
#include "services/cache/rrset.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/module.h"
@ -61,6 +62,7 @@
#include "services/localzone.h"
#include "util/data/dname.h"
#include "respip/respip.h"
#include "services/listen_dnsport.h"
/** subtract timers and the values do not overflow or become negative */
static void
@ -84,7 +86,7 @@ timeval_add(struct timeval* d, const struct timeval* add)
#ifndef S_SPLINT_S
d->tv_sec += add->tv_sec;
d->tv_usec += add->tv_usec;
if(d->tv_usec > 1000000 ) {
if(d->tv_usec >= 1000000 ) {
d->tv_usec -= 1000000;
d->tv_sec++;
}
@ -126,7 +128,7 @@ timeval_smaller(const struct timeval* x, const struct timeval* y)
#endif
}
/*
/**
* 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
* 1 or -1 (they mean 'ci_a is larger/smaller than ci_b', respectively, but
@ -249,6 +251,7 @@ mesh_create(struct module_stack* stack, struct module_env* env)
mesh->num_forever_states = 0;
mesh->stats_jostled = 0;
mesh->stats_dropped = 0;
mesh->ans_expired = 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
@ -344,6 +347,97 @@ int mesh_make_new_space(struct mesh_area* mesh, sldns_buffer* qbuf)
return 0;
}
struct dns_msg*
mesh_serve_expired_lookup(struct module_qstate* qstate,
struct query_info* lookup_qinfo)
{
hashvalue_type h;
struct lruhash_entry* e;
struct dns_msg* msg;
struct reply_info* data;
struct msgreply_entry* key;
time_t timenow = *qstate->env->now;
int must_validate = (!(qstate->query_flags&BIT_CD)
|| qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate;
/* Lookup cache */
h = query_info_hash(lookup_qinfo, qstate->query_flags);
e = slabhash_lookup(qstate->env->msg_cache, h, lookup_qinfo, 0);
if(!e) return NULL;
key = (struct msgreply_entry*)e->key;
data = (struct reply_info*)e->data;
msg = tomsg(qstate->env, &key->key, data, qstate->region, timenow,
qstate->env->cfg->serve_expired, qstate->env->scratch);
if(!msg)
goto bail_out;
/* Check CNAME chain (if any)
* This is part of tomsg above; no need to check now. */
/* Check security status of the cached answer.
* tomsg above has a subset of these checks, so we are leaving
* these as is.
* In case of bogus or revalidation we don't care to reply here. */
if(must_validate && (msg->rep->security == sec_status_bogus ||
msg->rep->security == sec_status_secure_sentinel_fail)) {
verbose(VERB_ALGO, "Serve expired: bogus answer found in cache");
goto bail_out;
} else if(msg->rep->security == sec_status_unchecked && must_validate) {
verbose(VERB_ALGO, "Serve expired: unchecked entry needs "
"validation");
goto bail_out; /* need to validate cache entry first */
} else if(msg->rep->security == sec_status_secure &&
!reply_all_rrsets_secure(msg->rep) && must_validate) {
verbose(VERB_ALGO, "Serve expired: secure entry"
" changed status");
goto bail_out; /* rrset changed, re-verify */
}
lock_rw_unlock(&e->lock);
return msg;
bail_out:
lock_rw_unlock(&e->lock);
return NULL;
}
/** Init the serve expired data structure */
static int
mesh_serve_expired_init(struct mesh_state* mstate, int timeout)
{
struct timeval t;
/* Create serve_expired_data if not there yet */
if(!mstate->s.serve_expired_data) {
mstate->s.serve_expired_data = (struct serve_expired_data*)
regional_alloc_zero(
mstate->s.region, sizeof(struct serve_expired_data));
if(!mstate->s.serve_expired_data)
return 0;
}
/* Don't overwrite the function if already set */
mstate->s.serve_expired_data->get_cached_answer =
mstate->s.serve_expired_data->get_cached_answer?
mstate->s.serve_expired_data->get_cached_answer:
mesh_serve_expired_lookup;
/* In case this timer already popped, start it again */
if(!mstate->s.serve_expired_data->timer) {
mstate->s.serve_expired_data->timer = comm_timer_create(
mstate->s.env->worker_base, mesh_serve_expired_callback, mstate);
if(!mstate->s.serve_expired_data->timer)
return 0;
#ifndef S_SPLINT_S
t.tv_sec = timeout/1000;
t.tv_usec = (timeout%1000)*1000;
#endif
comm_timer_set(mstate->s.serve_expired_data->timer, &t);
}
return 1;
}
void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
struct respip_client_info* cinfo, uint16_t qflags,
struct edns_data* edns, struct comm_reply* rep, uint16_t qid)
@ -353,6 +447,12 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
int was_detached = 0;
int was_noreply = 0;
int added = 0;
int timeout = mesh->env->cfg->serve_expired?
mesh->env->cfg->serve_expired_client_timeout:0;
struct sldns_buffer* r_buffer = rep->c->buffer;
if(rep->c->tcp_req_info) {
r_buffer = rep->c->tcp_req_info->spool_buffer;
}
if(!unique)
s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
/* does this create a new reply state? */
@ -361,7 +461,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
verbose(VERB_ALGO, "Too many queries. dropping "
"incoming query.");
comm_point_drop_reply(rep);
mesh->stats_dropped ++;
mesh->stats_dropped++;
return;
}
/* for this new reply state, the reply address is free,
@ -371,8 +471,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
if(mesh->num_reply_addrs > mesh->max_reply_states*16) {
verbose(VERB_ALGO, "Too many requests queued. "
"dropping incoming query.");
mesh->stats_dropped++;
comm_point_drop_reply(rep);
mesh->stats_dropped++;
return;
}
}
@ -388,7 +488,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, NULL,
LDNS_RCODE_SERVFAIL, edns, rep, mesh->env->scratch))
edns->opt_list = NULL;
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
comm_point_send_reply(rep);
return;
@ -404,7 +504,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL,
NULL, LDNS_RCODE_SERVFAIL, edns, rep, mesh->env->scratch))
edns->opt_list = NULL;
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
comm_point_send_reply(rep);
return;
@ -422,22 +522,27 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
mesh->num_detached_states++;
added = 1;
}
if(!s->reply_list && !s->cb_list && s->super_set.count == 0)
was_detached = 1;
if(!s->reply_list && !s->cb_list)
if(!s->reply_list && !s->cb_list) {
was_noreply = 1;
if(s->super_set.count == 0) {
was_detached = 1;
}
}
/* add reply to s */
if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo)) {
log_err("mesh_new_client: out of memory; SERVFAIL");
if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, &s->s,
NULL, LDNS_RCODE_SERVFAIL, edns, rep, mesh->env->scratch))
edns->opt_list = NULL;
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
comm_point_send_reply(rep);
if(added)
mesh_state_delete(&s->s);
return;
log_err("mesh_new_client: out of memory; SERVFAIL");
goto servfail_mem;
}
if(rep->c->tcp_req_info) {
if(!tcp_req_info_add_meshstate(rep->c->tcp_req_info, mesh, s)) {
log_err("mesh_new_client: out of memory add tcpreqinfo");
goto servfail_mem;
}
}
/* add serve expired timer if required and not already there */
if(timeout && !mesh_serve_expired_init(s, timeout)) {
log_err("mesh_new_client: out of memory initializing serve expired");
goto servfail_mem;
}
/* update statistics */
if(was_detached) {
@ -463,6 +568,18 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
}
if(added)
mesh_run(mesh, s, module_event_new, NULL);
return;
servfail_mem:
if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, &s->s,
NULL, LDNS_RCODE_SERVFAIL, edns, rep, mesh->env->scratch))
edns->opt_list = NULL;
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
comm_point_send_reply(rep);
if(added)
mesh_state_delete(&s->s);
return;
}
int
@ -472,6 +589,8 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
{
struct mesh_state* s = NULL;
int unique = unique_mesh_state(edns->opt_list, mesh->env);
int timeout = mesh->env->cfg->serve_expired?
mesh->env->cfg->serve_expired_client_timeout:0;
int was_detached = 0;
int was_noreply = 0;
int added = 0;
@ -510,15 +629,21 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
mesh->num_detached_states++;
added = 1;
}
if(!s->reply_list && !s->cb_list && s->super_set.count == 0)
was_detached = 1;
if(!s->reply_list && !s->cb_list)
if(!s->reply_list && !s->cb_list) {
was_noreply = 1;
if(s->super_set.count == 0) {
was_detached = 1;
}
}
/* add reply to s */
if(!mesh_state_add_cb(s, edns, buf, cb, cb_arg, qid, qflags)) {
if(added)
mesh_state_delete(&s->s);
return 0;
if(added)
mesh_state_delete(&s->s);
return 0;
}
/* add serve expired timer if not already there */
if(timeout && !mesh_serve_expired_init(s, timeout)) {
return 0;
}
/* update statistics */
if(was_detached) {
@ -534,15 +659,6 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
return 1;
}
static void mesh_schedule_prefetch(struct mesh_area* mesh,
struct query_info* qinfo, uint16_t qflags, time_t leeway, int run);
void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, time_t leeway)
{
mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1);
}
/* Internal backend routine of mesh_new_prefetch(). It takes one additional
* parameter, 'run', which controls whether to run the prefetch state
* immediately. When this function is called internally 'run' could be
@ -619,6 +735,12 @@ static void mesh_schedule_prefetch(struct mesh_area* mesh,
mesh_run(mesh, s, module_event_new, NULL);
}
void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, time_t leeway)
{
mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1);
}
void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
struct comm_reply* reply, int what)
{
@ -691,6 +813,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
mstate->s.env = env;
mstate->s.mesh_info = mstate;
mstate->s.prefetch_leeway = 0;
mstate->s.serve_expired_data = NULL;
mstate->s.no_cache_lookup = 0;
mstate->s.no_cache_store = 0;
mstate->s.need_refetch = 0;
@ -730,12 +853,22 @@ mesh_state_cleanup(struct mesh_state* mstate)
if(!mstate)
return;
mesh = mstate->s.env->mesh;
/* Stop and delete the serve expired timer */
if(mstate->s.serve_expired_data && mstate->s.serve_expired_data->timer) {
comm_timer_delete(mstate->s.serve_expired_data->timer);
mstate->s.serve_expired_data->timer = NULL;
}
/* drop unsent replies */
if(!mstate->replies_sent) {
struct mesh_reply* rep;
struct mesh_reply* rep = mstate->reply_list;
struct mesh_cb* cb;
for(rep=mstate->reply_list; rep; rep=rep->next) {
/* in tcp_req_info, the mstates linked are removed, but
* the reply_list is now NULL, so the remove-from-empty-list
* takes no time and also it does not do the mesh accounting */
mstate->reply_list = NULL;
for(; rep; rep=rep->next) {
comm_point_drop_reply(&rep->query_reply);
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
}
while((cb = mstate->cb_list)!=NULL) {
@ -743,6 +876,7 @@ mesh_state_cleanup(struct mesh_state* mstate)
fptr_ok(fptr_whitelist_mesh_cb(cb->cb));
(*cb->cb)(cb->cb_arg, LDNS_RCODE_SERVFAIL, NULL,
sec_status_unchecked, NULL, 0);
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
}
}
@ -810,7 +944,7 @@ find_in_subsub(struct mesh_state* m, struct mesh_state* tofind, size_t *c)
}
/** find cycle for already looked up mesh_state */
static int
static int
mesh_detect_cycle_found(struct module_qstate* qstate, struct mesh_state* dep_m)
{
struct mesh_state* cyc_m = qstate->mesh_info;
@ -1022,6 +1156,7 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
}
}
free(reason);
log_assert(m->s.env->mesh->num_reply_addrs > 0);
m->s.env->mesh->num_reply_addrs--;
}
@ -1031,11 +1166,14 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
* @param rcode: if not 0, error code.
* @param rep: reply to send (or NULL if rcode is set).
* @param r: reply entry
* @param r_buffer: buffer to use for reply entry.
* @param prev: previous reply, already has its answer encoded in buffer.
* @param prev_buffer: buffer for previous reply.
*/
static void
mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
struct mesh_reply* r, struct mesh_reply* prev)
struct mesh_reply* r, struct sldns_buffer* r_buffer,
struct mesh_reply* prev, struct sldns_buffer* prev_buffer)
{
struct timeval end_time;
struct timeval duration;
@ -1063,7 +1201,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
* and still reuse the previous answer if they are the same, but that
* would be complicated and error prone for the relatively minor case.
* So we err on the side of safety. */
if(prev && prev->qflags == r->qflags &&
if(prev && prev_buffer && prev->qflags == r->qflags &&
!prev->local_alias && !r->local_alias &&
prev->edns.edns_present == r->edns.edns_present &&
prev->edns.bits == r->edns.bits &&
@ -1071,13 +1209,11 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
edns_opt_list_compare(prev->edns.opt_list, r->edns.opt_list)
== 0) {
/* if the previous reply is identical to this one, fix ID */
if(prev->query_reply.c->buffer != r->query_reply.c->buffer)
sldns_buffer_copy(r->query_reply.c->buffer,
prev->query_reply.c->buffer);
sldns_buffer_write_at(r->query_reply.c->buffer, 0,
&r->qid, sizeof(uint16_t));
sldns_buffer_write_at(r->query_reply.c->buffer, 12,
r->qname, m->s.qinfo.qname_len);
if(prev_buffer != r_buffer)
sldns_buffer_copy(r_buffer, prev_buffer);
sldns_buffer_write_at(r_buffer, 0, &r->qid, sizeof(uint16_t));
sldns_buffer_write_at(r_buffer, 12, r->qname,
m->s.qinfo.qname_len);
comm_point_send_reply(&r->query_reply);
} else if(rcode) {
m->s.qinfo.qname = r->qname;
@ -1091,8 +1227,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
&r->edns, NULL, m->s.region))
r->edns.opt_list = NULL;
}
error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo,
r->qid, r->qflags, &r->edns);
error_encode(r_buffer, rcode, &m->s.qinfo, r->qid,
r->qflags, &r->edns);
comm_point_send_reply(&r->query_reply);
} else {
size_t udp_size = r->edns.udp_size;
@ -1108,21 +1244,21 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
m->s.env->cfg, r->query_reply.c,
m->s.region) ||
!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
r->qflags, r->query_reply.c->buffer, 0, 1,
m->s.env->scratch, udp_size, &r->edns,
(int)(r->edns.bits & EDNS_DO), secure))
r->qflags, r_buffer, 0, 1, m->s.env->scratch,
udp_size, &r->edns, (int)(r->edns.bits & EDNS_DO),
secure))
{
if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
rep, LDNS_RCODE_SERVFAIL, &r->edns, NULL, m->s.region))
r->edns.opt_list = NULL;
error_encode(r->query_reply.c->buffer,
LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid,
r->qflags, &r->edns);
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
&m->s.qinfo, r->qid, r->qflags, &r->edns);
}
r->edns = edns_bak;
comm_point_send_reply(&r->query_reply);
}
/* account */
log_assert(m->s.env->mesh->num_reply_addrs > 0);
m->s.env->mesh->num_reply_addrs--;
end_time = *m->s.env->now_tv;
timeval_subtract(&duration, &end_time, &r->start_time);
@ -1132,57 +1268,105 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
timeval_add(&m->s.env->mesh->replies_sum_wait, &duration);
timehist_insert(m->s.env->mesh->histogram, &duration);
if(m->s.env->cfg->stat_extended) {
uint16_t rc = FLAGS_GET_RCODE(sldns_buffer_read_u16_at(r->
query_reply.c->buffer, 2));
uint16_t rc = FLAGS_GET_RCODE(sldns_buffer_read_u16_at(
r_buffer, 2));
if(secure) m->s.env->mesh->ans_secure++;
m->s.env->mesh->ans_rcode[ rc ] ++;
if(rc == 0 && LDNS_ANCOUNT(sldns_buffer_begin(r->
query_reply.c->buffer)) == 0)
if(rc == 0 && LDNS_ANCOUNT(sldns_buffer_begin(r_buffer)) == 0)
m->s.env->mesh->ans_nodata++;
}
/* Log reply sent */
if(m->s.env->cfg->log_replies) {
log_reply_info(0, &m->s.qinfo, &r->query_reply.addr,
r->query_reply.addrlen, duration, 0,
r->query_reply.c->buffer);
log_reply_info(NO_VERBOSE, &m->s.qinfo, &r->query_reply.addr,
r->query_reply.addrlen, duration, 0, r_buffer);
}
}
void mesh_query_done(struct mesh_state* mstate)
{
struct mesh_reply* r;
struct mesh_reply* r, *reply_list = NULL;
struct mesh_reply* prev = NULL;
struct sldns_buffer* prev_buffer = NULL;
struct mesh_cb* c;
struct reply_info* rep = (mstate->s.return_msg?
mstate->s.return_msg->rep:NULL);
if((mstate->s.return_rcode == LDNS_RCODE_SERVFAIL ||
(rep && FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_SERVFAIL))
/* No need for the serve expired timer anymore; we are going to reply. */
if(mstate->s.serve_expired_data) {
comm_timer_delete(mstate->s.serve_expired_data->timer);
mstate->s.serve_expired_data->timer = NULL;
}
if(mstate->s.return_rcode == LDNS_RCODE_SERVFAIL ||
(rep && FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_SERVFAIL)) {
/* we are SERVFAILing; check for expired asnwer here */
mesh_serve_expired_callback(mstate);
if((mstate->reply_list || mstate->cb_list)
&& mstate->s.env->cfg->log_servfail
&& !mstate->s.env->cfg->val_log_squelch) {
char* err = errinf_to_str_servfail(&mstate->s);
if(err)
log_err("%s", err);
free(err);
char* err = errinf_to_str_servfail(&mstate->s);
if(err)
log_err("%s", err);
free(err);
}
}
for(r = mstate->reply_list; r; r = r->next) {
if(mstate->reply_list) {
/* set the reply_list to NULL during the mesh_query_done
* processing, so that calls back into the mesh from
* tcp_req_info (deciding to drop the reply and thus
* unregister the mesh_reply from the mstate) are stopped
* because the list is empty.
* The mstate is then likely not a reply_state, and maybe
* also a detached_state.
*/
reply_list = mstate->reply_list;
mstate->reply_list = NULL;
if(!mstate->reply_list && !mstate->cb_list) {
/* was a reply state, not anymore */
log_assert(mstate->s.env->mesh->num_reply_states > 0);
mstate->s.env->mesh->num_reply_states--;
}
if(!mstate->reply_list && !mstate->cb_list &&
mstate->super_set.count == 0)
mstate->s.env->mesh->num_detached_states++;
}
for(r = reply_list; r; r = r->next) {
/* if a response-ip address block has been stored the
* information should be logged for each client. */
if(mstate->s.respip_action_info &&
mstate->s.respip_action_info->addrinfo) {
respip_inform_print(mstate->s.respip_action_info->addrinfo,
respip_inform_print(mstate->s.respip_action_info,
r->qname, mstate->s.qinfo.qtype,
mstate->s.qinfo.qclass, r->local_alias,
&r->query_reply);
if(mstate->s.env->cfg->stat_extended &&
mstate->s.respip_action_info->rpz_used) {
if(mstate->s.respip_action_info->rpz_disabled)
mstate->s.env->mesh->rpz_action[RPZ_DISABLED_ACTION]++;
if(mstate->s.respip_action_info->rpz_cname_override)
mstate->s.env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
else
mstate->s.env->mesh->rpz_action[respip_action_to_rpz_action(
mstate->s.respip_action_info->action)]++;
}
}
/* if this query is determined to be dropped during the
* mesh processing, this is the point to take that action. */
if(mstate->s.is_drop)
if(mstate->s.is_drop) {
comm_point_drop_reply(&r->query_reply);
else {
} else {
struct sldns_buffer* r_buffer = r->query_reply.c->buffer;
if(r->query_reply.c->tcp_req_info) {
r_buffer = r->query_reply.c->tcp_req_info->spool_buffer;
prev_buffer = NULL;
}
mesh_send_reply(mstate, mstate->s.return_rcode, rep,
r, prev);
r, r_buffer, prev, prev_buffer);
if(r->query_reply.c->tcp_req_info) {
tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate);
r_buffer = NULL;
}
prev = r;
prev_buffer = r_buffer;
}
}
mstate->replies_sent = 1;
@ -1191,6 +1375,7 @@ void mesh_query_done(struct mesh_state* mstate)
* changed, eg. by adds from the callback routine */
if(!mstate->reply_list && mstate->cb_list && !c->next) {
/* was a reply state, not anymore */
log_assert(mstate->s.env->mesh->num_reply_states > 0);
mstate->s.env->mesh->num_reply_states--;
}
mstate->cb_list = c->next;
@ -1315,30 +1500,24 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
log_assert(!qinfo->local_alias->next && dsrc->count == 1 &&
qinfo->local_alias->rrset->rk.type ==
htons(LDNS_RR_TYPE_CNAME));
/* Technically, we should make a local copy for the owner
* name of the RRset, but in the case of the first (and
* currently only) local alias RRset, the owner name should
* point to the qname of the corresponding query, which should
* be valid throughout the lifetime of this mesh_reply. So
* we can skip copying. */
log_assert(qinfo->local_alias->rrset->rk.dname ==
sldns_buffer_at(rep->c->buffer, LDNS_HEADER_SIZE));
/* we should make a local copy for the owner name of
* the RRset */
r->local_alias->rrset->rk.dname_len =
qinfo->local_alias->rrset->rk.dname_len;
r->local_alias->rrset->rk.dname = regional_alloc_init(
s->s.region, qinfo->local_alias->rrset->rk.dname,
qinfo->local_alias->rrset->rk.dname_len);
if(!r->local_alias->rrset->rk.dname)
return 0;
d = regional_alloc_init(s->s.region, dsrc,
sizeof(struct packed_rrset_data)
+ sizeof(size_t) + sizeof(uint8_t*) + sizeof(time_t));
/* the rrset is not packed, like in the cache, but it is
* individualy allocated with an allocator from localzone. */
d = regional_alloc_zero(s->s.region, sizeof(*d));
if(!d)
return 0;
r->local_alias->rrset->entry.data = d;
d->rr_len = (size_t*)((uint8_t*)d +
sizeof(struct packed_rrset_data));
d->rr_data = (uint8_t**)&(d->rr_len[1]);
d->rr_ttl = (time_t*)&(d->rr_data[1]);
d->rr_len[0] = dsrc->rr_len[0];
d->rr_ttl[0] = dsrc->rr_ttl[0];
d->rr_data[0] = regional_alloc_init(s->s.region,
dsrc->rr_data[0], d->rr_len[0]);
if(!d->rr_data[0])
if(!rrset_insert_rr(s->s.region, d, dsrc->rr_data[0],
dsrc->rr_len[0], dsrc->rr_ttl[0], "CNAME local alias"))
return 0;
} else
r->local_alias = NULL;
@ -1392,7 +1571,7 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate,
/* module is looping. Stop it. */
log_err("internal error: looping module (%s) stopped",
mesh->mods.mod[mstate->s.curmod]->name);
log_query_info(VERB_QUERY, "pass error for qstate",
log_query_info(NO_VERBOSE, "pass error for qstate",
&mstate->s.qinfo);
s = module_error;
}
@ -1562,7 +1741,9 @@ mesh_stats_clear(struct mesh_area* mesh)
timehist_clear(mesh->histogram);
mesh->ans_secure = 0;
mesh->ans_bogus = 0;
memset(&mesh->ans_rcode[0], 0, sizeof(size_t)*16);
mesh->ans_expired = 0;
memset(&mesh->ans_rcode[0], 0, sizeof(size_t)*UB_STATS_RCODE_NUM);
memset(&mesh->rpz_action[0], 0, sizeof(size_t)*UB_STATS_RPZ_ACTION_NUM);
mesh->ans_nodata = 0;
}
@ -1613,3 +1794,212 @@ void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp,
m->prev->next = m->next;
else *fp = m->next;
}
void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m,
struct comm_point* cp)
{
struct mesh_reply* n, *prev = NULL;
n = m->reply_list;
/* when in mesh_cleanup, it sets the reply_list to NULL, so that
* there is no accounting twice */
if(!n) return; /* nothing to remove, also no accounting needed */
while(n) {
if(n->query_reply.c == cp) {
/* unlink it */
if(prev) prev->next = n->next;
else m->reply_list = n->next;
/* delete it, but allocated in m region */
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
/* prev = prev; */
n = n->next;
continue;
}
prev = n;
n = n->next;
}
/* it was not detached (because it had a reply list), could be now */
if(!m->reply_list && !m->cb_list
&& m->super_set.count == 0) {
mesh->num_detached_states++;
}
/* if not replies any more in mstate, it is no longer a reply_state */
if(!m->reply_list && !m->cb_list) {
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
}
}
static int
apply_respip_action(struct module_qstate* qstate,
const struct query_info* qinfo, struct respip_client_info* cinfo,
struct respip_action_info* actinfo, struct reply_info* rep,
struct ub_packed_rrset_key** alias_rrset,
struct reply_info** encode_repp, struct auth_zones* az)
{
if(qinfo->qtype != LDNS_RR_TYPE_A &&
qinfo->qtype != LDNS_RR_TYPE_AAAA &&
qinfo->qtype != LDNS_RR_TYPE_ANY)
return 1;
if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, actinfo,
alias_rrset, 0, qstate->region, az))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
* was redirected to response-ip data. */
if((actinfo->action == respip_deny ||
actinfo->action == respip_inform_deny) &&
*encode_repp == rep)
*encode_repp = NULL;
return 1;
}
void
mesh_serve_expired_callback(void* arg)
{
struct mesh_state* mstate = (struct mesh_state*) arg;
struct module_qstate* qstate = &mstate->s;
struct mesh_reply* r;
struct mesh_area* mesh = qstate->env->mesh;
struct dns_msg* msg;
struct mesh_cb* c;
struct mesh_reply* prev = NULL;
struct sldns_buffer* prev_buffer = NULL;
struct sldns_buffer* r_buffer = NULL;
struct reply_info* partial_rep = NULL;
struct ub_packed_rrset_key* alias_rrset = NULL;
struct reply_info* encode_rep = NULL;
struct respip_action_info actinfo;
struct query_info* lookup_qinfo = &qstate->qinfo;
struct query_info qinfo_tmp;
int must_validate = (!(qstate->query_flags&BIT_CD)
|| qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate;
if(!qstate->serve_expired_data) return;
verbose(VERB_ALGO, "Serve expired: Trying to reply with expired data");
comm_timer_delete(qstate->serve_expired_data->timer);
qstate->serve_expired_data->timer = NULL;
if(qstate->blacklist || qstate->no_cache_lookup || qstate->is_drop) {
verbose(VERB_ALGO,
"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) {
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);
if(!msg)
return;
/* Reset these in case we pass a second time from here. */
encode_rep = msg->rep;
memset(&actinfo, 0, sizeof(actinfo));
actinfo.action = respip_none;
alias_rrset = NULL;
if((mesh->use_response_ip || mesh->use_rpz) &&
!partial_rep && !apply_respip_action(qstate, &qstate->qinfo,
qstate->client_info, &actinfo, msg->rep, &alias_rrset, &encode_rep,
qstate->env->auth_zones)) {
return;
} 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)) {
return;
}
if(!encode_rep || alias_rrset) {
if(!encode_rep) {
/* Needs drop */
return;
} else {
/* A partial CNAME chain is found. */
partial_rep = encode_rep;
}
}
/* We've found a partial reply ending with an
* alias. Replace the lookup qinfo for the
* alias target and lookup the cache again to
* (possibly) complete the reply. As we're
* passing the "base" reply, there will be no
* more alias chasing. */
if(partial_rep) {
memset(&qinfo_tmp, 0, sizeof(qinfo_tmp));
get_cname_target(alias_rrset, &qinfo_tmp.qname,
&qinfo_tmp.qname_len);
if(!qinfo_tmp.qname) {
log_err("Serve expired: unexpected: invalid answer alias");
return;
}
qinfo_tmp.qtype = qstate->qinfo.qtype;
qinfo_tmp.qclass = qstate->qinfo.qclass;
lookup_qinfo = &qinfo_tmp;
continue;
}
break;
}
if(verbosity >= VERB_ALGO)
log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->rep);
r = mstate->reply_list;
mstate->reply_list = NULL;
if(!mstate->reply_list && !mstate->cb_list) {
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
if(mstate->super_set.count == 0) {
mesh->num_detached_states++;
}
}
for(; r; r = r->next) {
/* If address info is returned, it means the action should be an
* 'inform' variant and the information should be logged. */
if(actinfo.addrinfo) {
respip_inform_print(&actinfo, r->qname,
qstate->qinfo.qtype, qstate->qinfo.qclass,
r->local_alias, &r->query_reply);
if(qstate->env->cfg->stat_extended && actinfo.rpz_used) {
if(actinfo.rpz_disabled)
qstate->env->mesh->rpz_action[RPZ_DISABLED_ACTION]++;
if(actinfo.rpz_cname_override)
qstate->env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
else
qstate->env->mesh->rpz_action[
respip_action_to_rpz_action(actinfo.action)]++;
}
}
r_buffer = r->query_reply.c->buffer;
if(r->query_reply.c->tcp_req_info)
r_buffer = r->query_reply.c->tcp_req_info->spool_buffer;
mesh_send_reply(mstate, LDNS_RCODE_NOERROR, msg->rep,
r, r_buffer, prev, prev_buffer);
if(r->query_reply.c->tcp_req_info)
tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate);
prev = r;
prev_buffer = r_buffer;
/* Account for each reply sent. */
mesh->ans_expired++;
}
while((c = mstate->cb_list) != NULL) {
/* take this cb off the list; so that the list can be
* changed, eg. by adds from the callback routine */
if(!mstate->reply_list && mstate->cb_list && !c->next) {
/* was a reply state, not anymore */
log_assert(qstate->env->mesh->num_reply_states > 0);
qstate->env->mesh->num_reply_states--;
}
mstate->cb_list = c->next;
if(!mstate->reply_list && !mstate->cb_list &&
mstate->super_set.count == 0)
qstate->env->mesh->num_detached_states++;
mesh_do_callback(mstate, LDNS_RCODE_NOERROR, msg->rep, c);
}
}

View File

@ -51,6 +51,8 @@
#include "util/data/msgparse.h"
#include "util/module.h"
#include "services/modstack.h"
#include "services/rpz.h"
#include "libunbound/unbound.h"
struct sldns_buffer;
struct mesh_state;
struct mesh_reply;
@ -65,7 +67,7 @@ struct respip_client_info;
* Maximum number of mesh state activations. Any more is likely an
* infinite loop in the module. It is then terminated.
*/
#define MESH_MAX_ACTIVATION 3000
#define MESH_MAX_ACTIVATION 10000
/**
* Max number of references-to-references-to-references.. search size.
@ -110,6 +112,8 @@ struct mesh_area {
size_t stats_jostled;
/** stats, cumulative number of incoming client msgs dropped */
size_t stats_dropped;
/** stats, number of expired replies sent */
size_t ans_expired;
/** number of replies sent */
size_t replies_sent;
/** sum of waiting times for the replies */
@ -121,9 +125,11 @@ struct mesh_area {
/** (extended stats) bogus replies */
size_t ans_bogus;
/** (extended stats) rcodes in replies */
size_t ans_rcode[16];
size_t ans_rcode[UB_STATS_RCODE_NUM];
/** (extended stats) rcode nodata in replies */
size_t ans_nodata;
/** (extended stats) type of applied RPZ action */
size_t rpz_action[UB_STATS_RPZ_ACTION_NUM];
/** backup of query if other operations recurse and need the
* network buffers */
@ -142,6 +148,11 @@ struct mesh_area {
struct mesh_state* jostle_last;
/** timeout for jostling. if age is lower, it does not get jostled. */
struct timeval jostle_max;
/** If we need to use response ip (value passed from daemon)*/
int use_response_ip;
/** If we need to use RPZ (value passed from daemon) */
int use_rpz;
};
/**
@ -633,4 +644,32 @@ void mesh_list_insert(struct mesh_state* m, struct mesh_state** fp,
void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp,
struct mesh_state** lp);
/**
* Remove mesh reply entry from the reply entry list. Searches for
* the comm_point pointer.
* @param mesh: to update the counters.
* @param m: the mesh state.
* @param cp: the comm_point to remove from the list.
*/
void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m,
struct comm_point* cp);
/** Callback for when the serve expired client timer has run out. Tries to
* find an expired answer in the cache and reply that to the client.
* @param arg: the argument passed to the callback.
*/
void mesh_serve_expired_callback(void* arg);
/**
* Try to get a (expired) cached answer.
* This needs to behave like the worker's answer_from_cache() in order to have
* the same behavior as when replying from cache.
* @param qstate: the module qstate.
* @param lookup_qinfo: the query info to look for in the cache.
* @return dns_msg if a cached answer was found, otherwise NULL.
*/
struct dns_msg*
mesh_serve_expired_lookup(struct module_qstate* qstate,
struct query_info* lookup_qinfo);
#endif /* SERVICES_MESH_H */

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