mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-08-17 21:25:50 +02:00
Compare commits
163
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc94a2e202 | ||
|
|
7c219b4917 | ||
|
|
b7052aa56a | ||
|
|
dc4d8d2838 | ||
|
|
5f9edd6ca6 | ||
|
|
498a10a492 | ||
|
|
bb73083132 | ||
|
|
912999e187 | ||
|
|
185a7cd276 | ||
|
|
b2b5b1e7f9 | ||
|
|
ca9a8ae1bd | ||
|
|
78032304ca | ||
|
|
a037e0317c | ||
|
|
3eb8f9028b | ||
|
|
85bfa1bd35 | ||
|
|
9bd8d5a929 | ||
|
|
8b0bbc6691 | ||
|
|
84a1152109 | ||
|
|
ba1bda607b | ||
|
|
1460965467 | ||
|
|
522220494a | ||
|
|
d2fd438713 | ||
|
|
8277eabcaf | ||
|
|
84b0f323b5 | ||
|
|
62287e64f7 | ||
|
|
306eda3809 | ||
|
|
1d6715544a | ||
|
|
4e4fabfc40 | ||
|
|
5e48023c6c | ||
|
|
a96e895bc9 | ||
|
|
55630293b9 | ||
|
|
59501db3bd | ||
|
|
0b9807d677 | ||
|
|
9db3f47b98 | ||
|
|
c7883a9ebe | ||
|
|
ab7a6bc2b9 | ||
|
|
dc434596e4 | ||
|
|
62dd62efd0 | ||
|
|
6907d9a989 | ||
|
|
959088280f | ||
|
|
5a66104adb | ||
|
|
3d9ea4721a | ||
|
|
ee94589556 | ||
|
|
06cfef3252 | ||
|
|
489e48b3d1 | ||
|
|
479273df3c | ||
|
|
9a1813e0bf | ||
|
|
e939a7689f | ||
|
|
fcd489a12a | ||
|
|
b2deaa21ba | ||
|
|
2b3f0026fe | ||
|
|
a941253a81 | ||
|
|
510b8df4b6 | ||
|
|
a342145ead | ||
|
|
0daf4a0b43 | ||
|
|
78c8161cf4 | ||
|
|
25092accc1 | ||
|
|
c4e81d06b7 | ||
|
|
fed6a738de | ||
|
|
133b65bc67 | ||
|
|
cc9c205d88 | ||
|
|
191e347415 | ||
|
|
2573007d13 | ||
|
|
9c33f8dcf4 | ||
|
|
1c80108a5b | ||
|
|
6f9ec93b4a | ||
|
|
890a3fe0a9 | ||
|
|
53d8141a00 | ||
|
|
534c4c194e | ||
|
|
9f16769aa3 | ||
|
|
d68a8204d2 | ||
|
|
7be70582d2 | ||
|
|
5543bd63b6 | ||
|
|
1a629da2c3 | ||
|
|
605d2dd751 | ||
|
|
34c36a7616 | ||
|
|
59824635da | ||
|
|
7608c92bb7 | ||
|
|
c7b302f43a | ||
|
|
526496a860 | ||
|
|
3c1e638757 | ||
|
|
ccafa0830f | ||
|
|
330a321f13 | ||
|
|
37a8e7e370 | ||
|
|
7a6abc6a11 | ||
|
|
83defc87c6 | ||
|
|
2e352bc48c | ||
|
|
c8b71a8b1e | ||
|
|
d43a081730 | ||
|
|
99d58fb600 | ||
|
|
e8f7d9c150 | ||
|
|
162b28bea1 | ||
|
|
019816dcf1 | ||
|
|
a11cc0c530 | ||
|
|
01c703d3e6 | ||
|
|
aaec3572e4 | ||
|
|
523c1bdb06 | ||
|
|
31b8352bc6 | ||
|
|
e98df72b44 | ||
|
|
7a882802b6 | ||
|
|
a8c3d57e0e | ||
|
|
5eef2381f0 | ||
|
|
81ae42fde0 | ||
|
|
2102aa0a3a | ||
|
|
5e42374cc8 | ||
|
|
e08155e86e | ||
|
|
d15e61a753 | ||
|
|
a5e722d85f | ||
|
|
a84ed747dc | ||
|
|
0001a42efc | ||
|
|
3feea76968 | ||
|
|
ef6480b696 | ||
|
|
28f9864b53 | ||
|
|
a7d3013334 | ||
|
|
80391ee2b8 | ||
|
|
c8939acc7f | ||
|
|
b03a464d58 | ||
|
|
a793689af0 | ||
|
|
635461389c | ||
|
|
95d4dc2eb8 | ||
|
|
3e7a53c5ca | ||
|
|
d2318c50cd | ||
|
|
1065ff7c17 | ||
|
|
1a9238ca5f | ||
|
|
5def8556c6 | ||
|
|
265993017d | ||
|
|
decd94f2a8 | ||
|
|
c7a9776d91 | ||
|
|
6a08bfbb30 | ||
|
|
fa44214c61 | ||
|
|
4a2c15be58 | ||
|
|
42312206c9 | ||
|
|
472e96622c | ||
|
|
5fa0d10608 | ||
|
|
c4096b8654 | ||
|
|
d48aa65684 | ||
|
|
cc48ec587f | ||
|
|
687a99a82e | ||
|
|
66addd85e7 | ||
|
|
b461dc4111 | ||
|
|
1a90ff7b67 | ||
|
|
69bfd93616 | ||
|
|
ccb924d55f | ||
|
|
55f571825a | ||
|
|
0fa6818d48 | ||
|
|
cb0d7805e2 | ||
|
|
7a4e1baca9 | ||
|
|
e6dccd8565 | ||
|
|
62461e23be | ||
|
|
f060064e38 | ||
|
|
09420fde81 | ||
|
|
5b6e516dd1 | ||
|
|
00ebea939a | ||
|
|
fd57c88931 | ||
|
|
5f95ede1fc | ||
|
|
49bc2f887c | ||
|
|
696a07e7eb | ||
|
|
1c90125ab2 | ||
|
|
d388acddaa | ||
|
|
3aa83fe595 | ||
|
|
ced720ad99 | ||
|
|
df07a8ae65 | ||
|
|
67537b99c8 |
+34
-8
@@ -20,12 +20,14 @@ exec_prefix=@exec_prefix@
|
||||
bindir=@bindir@
|
||||
mandir=@mandir@
|
||||
libdir=@libdir@
|
||||
# datarootdir is here to please some checkers, use datadir.
|
||||
datarootdir=@datarootdir@
|
||||
datadir=@datadir@
|
||||
includedir=@includedir@
|
||||
doxygen=@doxygen@
|
||||
libtool=@libtool@
|
||||
ldnsdir=@ldnsdir@
|
||||
staticexe=@staticexe@
|
||||
|
||||
YACC=@YACC@
|
||||
LEX=@LEX@
|
||||
@@ -48,26 +50,33 @@ LINTFLAGS=+quiet -weak -warnposix -unrecog -Din_addr_t=uint32_t -Du_int=unsigned
|
||||
# compat with openssl linux edition.
|
||||
LINTFLAGS+="-DBN_ULONG=unsigned long" -Dkrb5_int32=int "-Dkrb5_ui_4=unsigned int" -DPQ_64BIT=uint64_t
|
||||
|
||||
INSTALL=$(srcdir)/install-sh
|
||||
INSTALL=$(srcdir)/install-sh
|
||||
|
||||
COMMON_SRC=$(wildcard services/*.c util/*.c util/data/*.c util/storage/*.c) util/configparser.c util/configlexer.c testcode/checklocks.c
|
||||
COMMON_SRC=$(wildcard services/*.c services/cache/*.c util/*.c \
|
||||
util/data/*.c util/storage/*.c iterator/*.c) util/configparser.c \
|
||||
util/configlexer.c testcode/checklocks.c
|
||||
COMMON_OBJ=$(addprefix $(BUILD),$(COMMON_SRC:.c=.o))
|
||||
COMPAT_OBJ=$(addprefix $(BUILD)compat/,$(LIBOBJS))
|
||||
UNITTEST_SRC=$(wildcard testcode/unit*.c) testcode/readhex.c $(COMMON_SRC)
|
||||
UNITTEST_OBJ=$(addprefix $(BUILD),$(UNITTEST_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
DAEMON_SRC=$(wildcard daemon/*.c) $(COMMON_SRC)
|
||||
DAEMON_OBJ=$(addprefix $(BUILD),$(DAEMON_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
TESTBOUND_SRC=testcode/testbound.c testcode/ldns-testpkts.c daemon/worker.c daemon/daemon.c daemon/stats.c testcode/replay.c testcode/fake_event.c $(filter-out util/netevent.c services/listen_dnsport.c services/outside_network.c, $(COMMON_SRC))
|
||||
TESTBOUND_SRC=testcode/testbound.c testcode/ldns-testpkts.c \
|
||||
daemon/worker.c daemon/daemon.c daemon/stats.c testcode/replay.c \
|
||||
testcode/fake_event.c $(filter-out util/netevent.c \
|
||||
services/listen_dnsport.c services/outside_network.c, $(COMMON_SRC))
|
||||
TESTBOUND_OBJ=$(addprefix $(BUILD),$(TESTBOUND_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
LOCKVERIFY_SRC=testcode/lock_verify.c $(COMMON_SRC)
|
||||
LOCKVERIFY_OBJ=$(addprefix $(BUILD),$(LOCKVERIFY_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
PKTVIEW_SRC=testcode/pktview.c testcode/readhex.c $(COMMON_SRC)
|
||||
PKTVIEW_OBJ=$(addprefix $(BUILD),$(PKTVIEW_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) $(TESTBOUND_SRC) $(LOCKVERIFY_SRC)
|
||||
ALL_OBJ=$(addprefix $(BUILD),$(ALL_SRC:.c=.o) $(addprefix compat/,$(LIBOBJS))) $(COMPAT_OBJ)
|
||||
ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \
|
||||
$(TESTBOUND_SRC) $(LOCKVERIFY_SRC)
|
||||
ALL_OBJ=$(addprefix $(BUILD),$(ALL_SRC:.c=.o) \
|
||||
$(addprefix compat/,$(LIBOBJS))) $(COMPAT_OBJ)
|
||||
|
||||
COMPILE=$(LIBTOOL) --tag=CC --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS)
|
||||
LINK=$(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) $(LDFLAGS)
|
||||
LINK=$(LIBTOOL) --tag=CC --mode=link $(CC) $(staticexe) $(CFLAGS) $(LDFLAGS)
|
||||
LINK_LIB=$(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) $(LDFLAGS) -release $(VERSION)
|
||||
|
||||
$(BUILD)%.o: $(srcdir)/%.c
|
||||
@@ -75,7 +84,7 @@ $(BUILD)%.o: $(srcdir)/%.c
|
||||
@if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
$Q$(COMPILE) -c $< -o $@
|
||||
|
||||
.PHONY: clean realclean doc lint all
|
||||
.PHONY: clean realclean doc lint all install uninstall
|
||||
|
||||
all: $(COMMON_OBJ) unbound unittest testbound lock-verify pktview
|
||||
|
||||
@@ -109,7 +118,7 @@ util/configlexer.c: $(srcdir)/util/configlexer.lex util/configparser.h
|
||||
$(INFO) Lex $<
|
||||
@if test ! -d util; then $(INSTALL) -d util; fi
|
||||
$Qecho "#include \"util/configyyrename.h\"" > $@
|
||||
$Q$(LEX) -i -t $< >> $@
|
||||
$Q$(LEX) -t $< >> $@
|
||||
|
||||
util/configparser.c util/configparser.h: $(srcdir)/util/configparser.y
|
||||
$(INFO) Yacc $<
|
||||
@@ -142,6 +151,19 @@ ifdef doxygen
|
||||
$(doxygen) $(srcdir)/doc/unbound.doxygen
|
||||
endif
|
||||
|
||||
install:
|
||||
$(INSTALL) -d $(bindir)
|
||||
$(INSTALL) -d $(mandir)
|
||||
$(INSTALL) -d $(mandir)/man8
|
||||
$(INSTALL) -d $(mandir)/man5
|
||||
$(LIBTOOL) --mode=install cp unbound $(bindir)/unbound
|
||||
$(INSTALL) -c -m 644 $(srcdir)/doc/unbound.8 $(mandir)/man8
|
||||
$(INSTALL) -c -m 644 $(srcdir)/doc/unbound.conf.5 $(mandir)/man5
|
||||
|
||||
uninstall:
|
||||
rm -f -- $(bindir)/unbound
|
||||
rm -f -- $(mandir)/man8/unbound.8 $(mandir)/man5/unbound.conf.5
|
||||
|
||||
# Automatic dependencies.
|
||||
$(BUILD)%.d: $(srcdir)/%.c
|
||||
$(INFO) Depend $<
|
||||
@@ -150,4 +172,8 @@ $(BUILD)%.d: $(srcdir)/%.c
|
||||
| sed '\''s!\(.*\)\.o[ :]*!$(dir $@)\1.o $@ : !g'\'' > $@; \
|
||||
[ -s $@ ] || rm -f $@'
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(MAKECMDGOALS),realclean)
|
||||
-include $(addprefix $(BUILD),$(ALL_SRC:.c=.d))
|
||||
endif
|
||||
endif
|
||||
|
||||
Vendored
+5
-3
@@ -1,7 +1,7 @@
|
||||
# generated automatically by aclocal 1.9.6 -*- Autoconf -*-
|
||||
# generated automatically by aclocal 1.10 -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
|
||||
# 2005 Free Software Foundation, Inc.
|
||||
# 2005, 2006 Free Software Foundation, Inc.
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
@@ -1597,7 +1597,7 @@ linux*)
|
||||
|
||||
# Append ld.so.conf contents to the search path
|
||||
if test -f /etc/ld.so.conf; then
|
||||
lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
|
||||
lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/^[ ]*//;s/#.*//;/^[^\/]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
|
||||
sys_lib_dlsearch_path_spec="/lib${libsuff} /usr/lib${libsuff} $lt_ld_extra"
|
||||
fi
|
||||
|
||||
@@ -6370,6 +6370,7 @@ do
|
||||
done
|
||||
done
|
||||
done
|
||||
IFS=$as_save_IFS
|
||||
lt_ac_max=0
|
||||
lt_ac_count=0
|
||||
# Add /usr/xpg4/bin/sed as it is typically found on Solaris
|
||||
@@ -6402,6 +6403,7 @@ for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
|
||||
done
|
||||
])
|
||||
SED=$lt_cv_path_SED
|
||||
AC_SUBST([SED])
|
||||
AC_MSG_RESULT([$SED])
|
||||
])
|
||||
|
||||
|
||||
+31
-1
@@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
AC_PREREQ(2.57)
|
||||
|
||||
AC_INIT(unbound, 0.3, wouter@nlnetlabs.nl, unbound)
|
||||
AC_INIT(unbound, 0.4, wouter@nlnetlabs.nl, unbound)
|
||||
|
||||
CFLAGS=
|
||||
AC_AIX
|
||||
@@ -336,6 +336,26 @@ cv_msgiovlen_type="int" ],
|
||||
AC_MSG_RESULT($cv_msgiovlen_type)
|
||||
AC_DEFINE_UNQUOTED([TYPE_MSGIOVLEN], $cv_msgiovlen_type, [Define to 'int' or type of struct msghdr.msg_iovlen.])
|
||||
|
||||
# Check for yyunput
|
||||
AC_DEFUN([CHECK_YYUNPUT],
|
||||
[AC_MSG_CHECKING(whether lex can not generate yyunput)
|
||||
AC_CACHE_VAL(ac_cv_c_yy_unput,
|
||||
[ac_cv_c_yy_unput=no
|
||||
if $LEX --nounput -h 2>&1 | grep "do not generate" 2>&1 >/dev/null; then
|
||||
ac_cv_c_yy_unput="yes"
|
||||
else
|
||||
ac_cv_c_yy_unput="no"
|
||||
fi
|
||||
])
|
||||
|
||||
AC_MSG_RESULT($ac_cv_c_yy_unput)
|
||||
if test $ac_cv_c_yy_unput = yes; then
|
||||
LEX="$LEX --nounput"
|
||||
fi
|
||||
])dnl
|
||||
|
||||
CHECK_YYUNPUT
|
||||
|
||||
# Checks for libraries.
|
||||
AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=pathname],
|
||||
[enable SSL (will check /usr/local/ssl
|
||||
@@ -449,6 +469,16 @@ else
|
||||
AC_DEFINE(USE_MINI_EVENT, 1, [Define if you want to use internal select based events])
|
||||
fi
|
||||
|
||||
# set static linking if requested
|
||||
AC_SUBST(staticexe)
|
||||
staticexe=""
|
||||
AC_ARG_ENABLE(staticexe, AC_HELP_STRING([--enable-static-exe],
|
||||
[ enable to compile executables statically against event, ldns libs, for debug purposes ]),
|
||||
, )
|
||||
if test x_$enable_static_exe = x_yes; then
|
||||
staticexe="-static"
|
||||
fi
|
||||
|
||||
# check to see if libraries are needed for these functions.
|
||||
AC_CHECK_LIB(socket, socket)
|
||||
AC_CHECK_LIB(nsl, inet_pton)
|
||||
|
||||
+69
-17
@@ -50,6 +50,10 @@
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#include "services/listen_dnsport.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "util/module.h"
|
||||
#include "iterator/iterator.h"
|
||||
#include <signal.h>
|
||||
|
||||
/** How many quit requests happened. */
|
||||
@@ -58,7 +62,10 @@ static int sig_record_quit = 0;
|
||||
static int sig_record_reload = 0;
|
||||
|
||||
/** used when no other sighandling happens, so we don't die
|
||||
* when multiple signals in quick succession are sent to us. */
|
||||
* when multiple signals in quick succession are sent to us.
|
||||
* @param sig: signal number.
|
||||
* @return signal handler return type (void or int).
|
||||
*/
|
||||
static RETSIGTYPE record_sigh(int sig)
|
||||
{
|
||||
#ifdef LIBEVENT_SIGNAL_PROBLEM
|
||||
@@ -118,20 +125,9 @@ daemon_init()
|
||||
signal_handling_record();
|
||||
checklock_start();
|
||||
daemon->need_to_exit = 0;
|
||||
daemon->msg_cache = slabhash_create(HASH_DEFAULT_SLABS,
|
||||
HASH_DEFAULT_STARTARRAY, HASH_DEFAULT_MAXMEM,
|
||||
msgreply_sizefunc, query_info_compare,
|
||||
query_entry_delete, reply_info_delete, NULL);
|
||||
if(!daemon->msg_cache) {
|
||||
free(daemon);
|
||||
return NULL;
|
||||
}
|
||||
daemon->rrset_cache = slabhash_create(HASH_DEFAULT_SLABS,
|
||||
HASH_DEFAULT_STARTARRAY, HASH_DEFAULT_MAXMEM,
|
||||
ub_rrset_sizefunc, ub_rrset_compare,
|
||||
ub_rrset_key_delete, rrset_data_delete, &daemon->superalloc);
|
||||
if(!daemon->rrset_cache) {
|
||||
slabhash_delete(daemon->msg_cache);
|
||||
daemon->num_modules = 0;
|
||||
if(!(daemon->env = (struct module_env*)calloc(1,
|
||||
sizeof(*daemon->env)))) {
|
||||
free(daemon);
|
||||
return NULL;
|
||||
}
|
||||
@@ -152,6 +148,53 @@ daemon_open_shared_ports(struct daemon* daemon)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Desetup the modules, deinit, delete.
|
||||
* @param daemon: the daemon.
|
||||
*/
|
||||
static void
|
||||
daemon_desetup_modules(struct daemon* daemon)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<daemon->num_modules; i++) {
|
||||
(*daemon->modfunc[i]->deinit)(daemon->env, i);
|
||||
}
|
||||
daemon->num_modules = 0;
|
||||
free(daemon->modfunc);
|
||||
daemon->modfunc = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup modules. Assigns ids and calls module_init.
|
||||
* @param daemon: the daemon
|
||||
*/
|
||||
static void daemon_setup_modules(struct daemon* daemon)
|
||||
{
|
||||
int i;
|
||||
if(daemon->num_modules != 0)
|
||||
daemon_desetup_modules(daemon);
|
||||
/* fixed setup of the modules */
|
||||
daemon->num_modules = 1;
|
||||
daemon->modfunc = (struct module_func_block**)calloc((size_t)
|
||||
daemon->num_modules, sizeof(struct module_func_block*));
|
||||
if(!daemon->modfunc) {
|
||||
fatal_exit("malloc failure allocating function callbacks");
|
||||
}
|
||||
daemon->modfunc[0] = iter_get_funcblock();
|
||||
daemon->env->cfg = daemon->cfg;
|
||||
daemon->env->alloc = &daemon->superalloc;
|
||||
daemon->env->worker = NULL;
|
||||
daemon->env->send_packet = &worker_send_packet;
|
||||
daemon->env->send_query = &worker_send_query;
|
||||
for(i=0; i<daemon->num_modules; i++) {
|
||||
log_info("init module %d: %s", i, daemon->modfunc[i]->name);
|
||||
if(!(*daemon->modfunc[i]->init)(daemon->env, i)) {
|
||||
fatal_exit("module init for module %s failed",
|
||||
daemon->modfunc[i]->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate empty worker structures. With backptr and thread-number,
|
||||
* from 0..numthread initialised. Used as user arguments to new threads.
|
||||
@@ -268,6 +311,10 @@ void
|
||||
daemon_fork(struct daemon* daemon)
|
||||
{
|
||||
log_assert(daemon);
|
||||
|
||||
/* setup modules */
|
||||
daemon_setup_modules(daemon);
|
||||
|
||||
/* first create all the worker structures, so we can pass
|
||||
* them to the newly created threads.
|
||||
*/
|
||||
@@ -320,12 +367,17 @@ daemon_delete(struct daemon* daemon)
|
||||
{
|
||||
if(!daemon)
|
||||
return;
|
||||
daemon_desetup_modules(daemon);
|
||||
listening_ports_free(daemon->ports);
|
||||
slabhash_delete(daemon->msg_cache);
|
||||
slabhash_delete(daemon->rrset_cache);
|
||||
if(daemon->env) {
|
||||
slabhash_delete(daemon->env->msg_cache);
|
||||
rrset_cache_delete(daemon->env->rrset_cache);
|
||||
infra_delete(daemon->env->infra_cache);
|
||||
}
|
||||
alloc_clear(&daemon->superalloc);
|
||||
free(daemon->cwd);
|
||||
free(daemon->pidfile);
|
||||
free(daemon->env);
|
||||
free(daemon);
|
||||
checklock_stop();
|
||||
}
|
||||
|
||||
+8
-4
@@ -48,6 +48,8 @@ struct config_file;
|
||||
struct worker;
|
||||
struct listen_port;
|
||||
struct slabhash;
|
||||
struct module_env;
|
||||
struct rrset_cache;
|
||||
|
||||
/**
|
||||
* Structure holding worker list.
|
||||
@@ -72,10 +74,12 @@ struct daemon {
|
||||
int need_to_exit;
|
||||
/** master allocation cache */
|
||||
struct alloc_cache superalloc;
|
||||
/** the message cache, content is struct msgreply_entry* */
|
||||
struct slabhash* msg_cache;
|
||||
/** the rrset cache, content is struct ub_packed_rrset_key* */
|
||||
struct slabhash* rrset_cache;
|
||||
/** the module environment master value, copied and changed by threads*/
|
||||
struct module_env* env;
|
||||
/** number of modules active, ids from 0 to num-1. */
|
||||
int num_modules;
|
||||
/** the module callbacks, array of num_modules length */
|
||||
struct module_func_block** modfunc;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+6
-5
@@ -42,6 +42,7 @@
|
||||
#include "config.h"
|
||||
#include "daemon/stats.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "services/mesh.h"
|
||||
|
||||
void server_stats_init(struct server_stats* stats)
|
||||
{
|
||||
@@ -51,9 +52,9 @@ void server_stats_init(struct server_stats* stats)
|
||||
void server_stats_querymiss(struct server_stats* stats, struct worker* worker)
|
||||
{
|
||||
stats->num_queries_missed_cache++;
|
||||
stats->sum_query_list_size += worker->num_requests;
|
||||
if(worker->num_requests > stats->max_query_list_size)
|
||||
stats->max_query_list_size = worker->num_requests;
|
||||
stats->sum_query_list_size += worker->env.mesh->all.count;
|
||||
if(worker->env.mesh->all.count > stats->max_query_list_size)
|
||||
stats->max_query_list_size = worker->env.mesh->all.count;
|
||||
}
|
||||
|
||||
void server_stats_log(struct server_stats* stats, int threadnum)
|
||||
@@ -65,7 +66,7 @@ void server_stats_log(struct server_stats* stats, int threadnum)
|
||||
log_info("server stats for thread %d: requestlist max %u avg %g "
|
||||
"exceeded %u", threadnum, (unsigned)stats->max_query_list_size,
|
||||
stats->num_queries_missed_cache?
|
||||
(double)stats->sum_query_list_size/
|
||||
stats->num_queries_missed_cache:0.0,
|
||||
(double)stats->sum_query_list_size/
|
||||
stats->num_queries_missed_cache : 0.0,
|
||||
(unsigned)stats->num_query_list_exceeded);
|
||||
}
|
||||
|
||||
+25
-19
@@ -46,7 +46,10 @@
|
||||
#include "util/config_file.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#include "services/listen_dnsport.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/module.h"
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
@@ -89,6 +92,8 @@ checkrlimits(struct config_file* cfg)
|
||||
log_warn("getrlimit: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
if(rlim.rlim_cur == (rlim_t)RLIM_INFINITY)
|
||||
return;
|
||||
if((size_t)rlim.rlim_cur < total) {
|
||||
log_err("Not enough sockets available. Increase "
|
||||
"ulimit(open files).");
|
||||
@@ -118,33 +123,31 @@ apply_dir(struct daemon* daemon, struct config_file* cfg, int cmdline_verbose)
|
||||
log_err("cwd: malloc failed");
|
||||
}
|
||||
}
|
||||
if(cfg->msg_cache_size != slabhash_get_size(daemon->msg_cache) ||
|
||||
cfg->msg_cache_slabs != daemon->msg_cache->size) {
|
||||
slabhash_delete(daemon->msg_cache);
|
||||
daemon->msg_cache = slabhash_create(cfg->msg_cache_slabs,
|
||||
if(!daemon->env->msg_cache ||
|
||||
cfg->msg_cache_size != slabhash_get_size(daemon->env->msg_cache) ||
|
||||
cfg->msg_cache_slabs != daemon->env->msg_cache->size) {
|
||||
slabhash_delete(daemon->env->msg_cache);
|
||||
daemon->env->msg_cache = slabhash_create(cfg->msg_cache_slabs,
|
||||
HASH_DEFAULT_STARTARRAY, cfg->msg_cache_size,
|
||||
msgreply_sizefunc, query_info_compare,
|
||||
query_entry_delete, reply_info_delete, NULL);
|
||||
if(!daemon->msg_cache) {
|
||||
fatal_exit("malloc failure updating config settings");
|
||||
}
|
||||
}
|
||||
if(cfg->rrset_cache_size != slabhash_get_size(daemon->rrset_cache) ||
|
||||
cfg->rrset_cache_slabs != daemon->rrset_cache->size) {
|
||||
slabhash_delete(daemon->rrset_cache);
|
||||
daemon->rrset_cache = slabhash_create(cfg->rrset_cache_slabs,
|
||||
HASH_DEFAULT_STARTARRAY, cfg->rrset_cache_size,
|
||||
ub_rrset_sizefunc, ub_rrset_compare,
|
||||
ub_rrset_key_delete, rrset_data_delete,
|
||||
&daemon->superalloc);
|
||||
if(!daemon->rrset_cache) {
|
||||
if(!daemon->env->msg_cache) {
|
||||
fatal_exit("malloc failure updating config settings");
|
||||
}
|
||||
}
|
||||
if((daemon->env->rrset_cache = rrset_cache_adjust(
|
||||
daemon->env->rrset_cache, cfg, &daemon->superalloc)) == 0)
|
||||
fatal_exit("malloc failure updating config settings");
|
||||
if((daemon->env->infra_cache = infra_adjust(daemon->env->infra_cache,
|
||||
cfg))==0)
|
||||
fatal_exit("malloc failure updating config settings");
|
||||
checkrlimits(cfg);
|
||||
}
|
||||
|
||||
/** Read existing pid from pidfile. */
|
||||
/** Read existing pid from pidfile.
|
||||
* @param file: file name of pid file.
|
||||
* @return: the pid from the file or -1 if none.
|
||||
*/
|
||||
static pid_t
|
||||
readpid (const char* file)
|
||||
{
|
||||
@@ -184,7 +187,10 @@ readpid (const char* file)
|
||||
return pid;
|
||||
}
|
||||
|
||||
/** write pid to file. */
|
||||
/** write pid to file.
|
||||
* @param pidfile: file name of pid file.
|
||||
* @param pid: pid to write to file.
|
||||
*/
|
||||
static void
|
||||
writepid (const char* pidfile, pid_t pid)
|
||||
{
|
||||
|
||||
+368
-360
@@ -47,11 +47,18 @@
|
||||
#include "daemon/daemon.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/module.h"
|
||||
#include "util/region-allocator.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#include "services/listen_dnsport.h"
|
||||
#include "services/outside_network.h"
|
||||
#include "services/outbound_list.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "services/mesh.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/data/dname.h"
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
@@ -59,17 +66,36 @@
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
|
||||
/** size of ID+FLAGS in a DNS message */
|
||||
#define DNS_ID_AND_FLAGS 4
|
||||
/** timeout in seconds for UDP queries to auth servers. TODO: proper rtt */
|
||||
#define UDP_QUERY_TIMEOUT 4
|
||||
/** timeout in seconds for TCP queries to auth servers. TODO: proper rtt */
|
||||
#define TCP_QUERY_TIMEOUT 30
|
||||
/** Advertised version of EDNS capabilities */
|
||||
#define EDNS_ADVERTISED_VERSION 0
|
||||
/** Advertised size of EDNS capabilities */
|
||||
#define EDNS_ADVERTISED_SIZE 4096
|
||||
/** Size of an UDP datagram */
|
||||
#define NORMAL_UDP_SIZE 512 /* bytes */
|
||||
|
||||
/** Report on memory usage by this thread and global */
|
||||
static void
|
||||
worker_mem_report(struct worker* worker)
|
||||
{
|
||||
size_t total, front, back, mesh, msg, rrset, infra, ac, superac;
|
||||
size_t me;
|
||||
if(verbosity < VERB_ALGO)
|
||||
return;
|
||||
front = listen_get_mem(worker->front);
|
||||
back = outnet_get_mem(worker->back);
|
||||
msg = slabhash_get_mem(worker->env.msg_cache);
|
||||
rrset = slabhash_get_mem(&worker->env.rrset_cache->table);
|
||||
infra = slabhash_get_mem(worker->env.infra_cache->hosts);
|
||||
mesh = mesh_get_mem(worker->env.mesh);
|
||||
ac = alloc_get_mem(&worker->alloc);
|
||||
superac = alloc_get_mem(&worker->daemon->superalloc);
|
||||
me = sizeof(*worker) + sizeof(*worker->base) + sizeof(*worker->comsig)
|
||||
+ comm_point_get_mem(worker->cmd_com) +
|
||||
sizeof(worker->rndstate) + region_get_mem(worker->scratchpad);
|
||||
total = front+back+mesh+msg+rrset+infra+ac+superac+me;
|
||||
log_info("Memory conditions: %u front=%u back=%u mesh=%u msg=%u "
|
||||
"rrset=%u infra=%u alloccache=%u globalalloccache=%u me=%u",
|
||||
(unsigned)total, (unsigned)front, (unsigned)back,
|
||||
(unsigned)mesh, (unsigned)msg, (unsigned)rrset,
|
||||
(unsigned)infra, (unsigned)ac, (unsigned)superac,
|
||||
(unsigned)me);
|
||||
}
|
||||
|
||||
void
|
||||
worker_send_cmd(struct worker* worker, ldns_buffer* buffer,
|
||||
@@ -85,236 +111,85 @@ worker_send_cmd(struct worker* worker, ldns_buffer* buffer,
|
||||
log_err("write socket: %s", strerror(errno));
|
||||
}
|
||||
|
||||
/** release workrequest back to the freelist,
|
||||
* note that the w->qinfo still needs to be cleared after this.
|
||||
*/
|
||||
static void
|
||||
req_release(struct work_query* w)
|
||||
{
|
||||
if(w->worker->num_requests == w->worker->request_size) {
|
||||
/* no longer at max, start accepting again. */
|
||||
listen_resume(w->worker->front);
|
||||
}
|
||||
log_assert(w->worker->num_requests >= 1);
|
||||
w->worker->num_requests --;
|
||||
w->next = w->worker->free_queries;
|
||||
w->worker->free_queries = w;
|
||||
}
|
||||
|
||||
/** create error and fill into buffer */
|
||||
static void
|
||||
replyerror_fillbuf(int r, struct comm_reply* repinfo, uint16_t id,
|
||||
uint16_t qflags, struct query_info* qinfo)
|
||||
{
|
||||
ldns_buffer* buf = repinfo->c->buffer;
|
||||
uint16_t flags;
|
||||
verbose(VERB_DETAIL, "reply with error");
|
||||
|
||||
ldns_buffer_clear(buf);
|
||||
ldns_buffer_write(buf, &id, sizeof(uint16_t));
|
||||
flags = (uint16_t)(BIT_QR | r); /* QR and retcode*/
|
||||
flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */
|
||||
ldns_buffer_write_u16(buf, flags);
|
||||
flags = 1;
|
||||
ldns_buffer_write_u16(buf, flags);
|
||||
flags = 0;
|
||||
ldns_buffer_write(buf, &flags, sizeof(uint16_t));
|
||||
ldns_buffer_write(buf, &flags, sizeof(uint16_t));
|
||||
ldns_buffer_write(buf, &flags, sizeof(uint16_t));
|
||||
ldns_buffer_write(buf, qinfo->qname, qinfo->qnamesize);
|
||||
ldns_buffer_write_u16(buf, qinfo->qtype);
|
||||
ldns_buffer_write_u16(buf, qinfo->qclass);
|
||||
ldns_buffer_flip(buf);
|
||||
}
|
||||
|
||||
/** reply to query with given error code */
|
||||
static void
|
||||
replyerror(int r, struct work_query* w)
|
||||
{
|
||||
w->edns.edns_version = EDNS_ADVERTISED_VERSION;
|
||||
w->edns.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
w->edns.ext_rcode = 0;
|
||||
w->edns.bits &= EDNS_DO;
|
||||
replyerror_fillbuf(r, &w->query_reply, w->query_id, w->query_flags,
|
||||
&w->qinfo);
|
||||
attach_edns_record(w->query_reply.c->buffer, &w->edns);
|
||||
comm_point_send_reply(&w->query_reply);
|
||||
req_release(w);
|
||||
query_info_clear(&w->qinfo);
|
||||
}
|
||||
|
||||
/** see if rrset needs to be updated in the cache */
|
||||
static int
|
||||
need_to_update_rrset(struct packed_rrset_data* newd,
|
||||
struct packed_rrset_data* cached)
|
||||
{
|
||||
/* o if current RRset is more trustworthy - insert it */
|
||||
if( newd->trust > cached->trust )
|
||||
return 1;
|
||||
/* o same trust, but different in data - insert it */
|
||||
if( newd->trust == cached->trust &&
|
||||
!rrsetdata_equal(newd, cached))
|
||||
return 1;
|
||||
/* o see if TTL is better than TTL in cache. */
|
||||
/* if so, see if rrset+rdata is the same */
|
||||
/* if so, update TTL in cache, even if trust is worse. */
|
||||
if( newd->ttl > cached->ttl &&
|
||||
rrsetdata_equal(newd, cached))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** store rrsets in the rrset cache. */
|
||||
static void
|
||||
worker_store_rrsets(struct worker* worker, struct reply_info* rep)
|
||||
{
|
||||
struct lruhash_entry* e;
|
||||
size_t i;
|
||||
/* see if rrset already exists in cache, if not insert it. */
|
||||
/* if it does exist: check to 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;
|
||||
/* looks up item with a readlock - no editing! */
|
||||
if((e=slabhash_lookup(worker->daemon->rrset_cache,
|
||||
rep->rrsets[i]->entry.hash, rep->rrsets[i]->entry.key,
|
||||
0)) != 0) {
|
||||
struct packed_rrset_data* data =
|
||||
(struct packed_rrset_data*)e->data;
|
||||
struct packed_rrset_data* rd =
|
||||
(struct packed_rrset_data*)
|
||||
rep->rrsets[i]->entry.data;
|
||||
rep->ref[i].key = (struct ub_packed_rrset_key*)e->key;
|
||||
rep->ref[i].id = rep->rrsets[i]->id;
|
||||
/* found in cache, do checks above */
|
||||
if(!need_to_update_rrset(rd, data)) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
ub_packed_rrset_parsedelete(rep->rrsets[i],
|
||||
&worker->alloc);
|
||||
rep->rrsets[i] = rep->ref[i].key;
|
||||
continue; /* use cached item instead */
|
||||
}
|
||||
if(rd->trust < data->trust)
|
||||
rd->trust = data->trust;
|
||||
lock_rw_unlock(&e->lock);
|
||||
/* small gap here, where entry is not locked.
|
||||
* possibly entry is updated with something else.
|
||||
* this is just too bad, its cache anyway. */
|
||||
/* use insert to update entry to manage lruhash
|
||||
* cache size values nicely. */
|
||||
}
|
||||
slabhash_insert(worker->daemon->rrset_cache,
|
||||
rep->rrsets[i]->entry.hash, &rep->rrsets[i]->entry,
|
||||
rep->rrsets[i]->entry.data, &worker->alloc);
|
||||
if(e) rep->rrsets[i] = rep->ref[i].key;
|
||||
}
|
||||
}
|
||||
|
||||
/** process incoming replies from the network */
|
||||
static int
|
||||
worker_handle_reply(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
struct comm_reply* reply_info)
|
||||
{
|
||||
struct work_query* w = (struct work_query*)arg;
|
||||
struct query_info qinf;
|
||||
struct reply_info* rep;
|
||||
struct msgreply_entry* e;
|
||||
struct edns_data svr_edns; /* unused server edns advertisement */
|
||||
uint16_t us;
|
||||
int r;
|
||||
struct module_qstate* q = (struct module_qstate*)arg;
|
||||
struct worker* worker = q->env->worker;
|
||||
struct outbound_entry e;
|
||||
e.qstate = q;
|
||||
e.qsent = NULL;
|
||||
|
||||
verbose(VERB_DETAIL, "reply to query with stored ID %d",
|
||||
ntohs(w->query_id)); /* byteswapped so same as dig prints */
|
||||
if(error != 0) {
|
||||
replyerror(LDNS_RCODE_SERVFAIL, w);
|
||||
mesh_report_reply(worker->env.mesh, &e, 0, reply_info);
|
||||
worker_mem_report(worker);
|
||||
return 0;
|
||||
}
|
||||
/* sanity check. */
|
||||
if(!LDNS_QR_WIRE(ldns_buffer_begin(c->buffer)))
|
||||
return 0; /* not a reply. */
|
||||
if(LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) != LDNS_PACKET_QUERY)
|
||||
return 0; /* not a reply to a query. */
|
||||
if(LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1)
|
||||
return 0; /* too much in the query section */
|
||||
/* see if it is truncated */
|
||||
if(LDNS_TC_WIRE(ldns_buffer_begin(c->buffer)) && c->type == comm_udp) {
|
||||
log_info("TC: truncated. retry in TCP mode.");
|
||||
qinfo_query_encode(w->worker->back->udp_buff, &w->qinfo);
|
||||
pending_tcp_query(w->worker->back, w->worker->back->udp_buff,
|
||||
&w->worker->fwd_addr, w->worker->fwd_addrlen,
|
||||
TCP_QUERY_TIMEOUT, worker_handle_reply, w,
|
||||
w->worker->rndstate);
|
||||
if(!LDNS_QR_WIRE(ldns_buffer_begin(c->buffer))
|
||||
|| LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) !=
|
||||
LDNS_PACKET_QUERY
|
||||
|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
|
||||
/* error becomes timeout for the module as if this reply
|
||||
* never arrived. */
|
||||
mesh_report_reply(worker->env.mesh, &e, 0, reply_info);
|
||||
worker_mem_report(worker);
|
||||
return 0;
|
||||
}
|
||||
/* woohoo a reply! */
|
||||
if((r=reply_info_parse(c->buffer, &w->worker->alloc, &qinf, &rep,
|
||||
w->worker->scratchpad, &svr_edns))!=0) {
|
||||
if(r == LDNS_RCODE_SERVFAIL)
|
||||
log_err("reply_info_parse: out of memory");
|
||||
/* formerr on my parse gives servfail to my client */
|
||||
replyerror(LDNS_RCODE_SERVFAIL, w);
|
||||
region_free_all(w->worker->scratchpad);
|
||||
return 0;
|
||||
}
|
||||
us = w->edns.udp_size;
|
||||
w->edns.edns_version = EDNS_ADVERTISED_VERSION;
|
||||
w->edns.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
w->edns.ext_rcode = 0;
|
||||
w->edns.bits &= EDNS_DO;
|
||||
if(!reply_info_answer_encode(&qinf, rep, w->query_id, w->query_flags,
|
||||
w->query_reply.c->buffer, 0, 0, w->worker->scratchpad, us,
|
||||
&w->edns)) {
|
||||
replyerror(LDNS_RCODE_SERVFAIL, w);
|
||||
query_info_clear(&qinf);
|
||||
reply_info_parsedelete(rep, &w->worker->alloc);
|
||||
region_free_all(w->worker->scratchpad);
|
||||
return 0;
|
||||
}
|
||||
comm_point_send_reply(&w->query_reply);
|
||||
region_free_all(w->worker->scratchpad);
|
||||
req_release(w);
|
||||
query_info_clear(&w->qinfo);
|
||||
if(rep->ttl == 0) {
|
||||
log_info("TTL 0: dropped");
|
||||
query_info_clear(&qinf);
|
||||
reply_info_parsedelete(rep, &w->worker->alloc);
|
||||
return 0;
|
||||
}
|
||||
reply_info_set_ttls(rep, time(0));
|
||||
worker_store_rrsets(w->worker, rep);
|
||||
reply_info_sortref(rep);
|
||||
/* store msg in the cache */
|
||||
if(!(e = query_info_entrysetup(&qinf, rep, w->query_hash))) {
|
||||
query_info_clear(&qinf);
|
||||
reply_info_parsedelete(rep, &w->worker->alloc);
|
||||
return 0;
|
||||
}
|
||||
slabhash_insert(w->worker->daemon->msg_cache, w->query_hash,
|
||||
&e->entry, rep, &w->worker->alloc);
|
||||
mesh_report_reply(worker->env.mesh, &e, 1, reply_info);
|
||||
worker_mem_report(worker);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** process incoming request */
|
||||
static void
|
||||
worker_process_query(struct worker* worker, struct work_query* w)
|
||||
/** process incoming serviced query replies from the network */
|
||||
static int
|
||||
worker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* reply_info)
|
||||
{
|
||||
/* query the forwarding address */
|
||||
verbose(VERB_DETAIL, "process_query ID %d", ntohs(w->query_id));
|
||||
pending_udp_query(worker->back, w->query_reply.c->buffer,
|
||||
&worker->fwd_addr, worker->fwd_addrlen, UDP_QUERY_TIMEOUT,
|
||||
worker_handle_reply, w, worker->rndstate);
|
||||
struct outbound_entry* e = (struct outbound_entry*)arg;
|
||||
struct worker* worker = e->qstate->env->worker;
|
||||
|
||||
verbose(VERB_ALGO, "worker scvd callback for qstate %p", e->qstate);
|
||||
if(error != 0) {
|
||||
mesh_report_reply(worker->env.mesh, e, 0, reply_info);
|
||||
worker_mem_report(worker);
|
||||
return 0;
|
||||
}
|
||||
/* sanity check. */
|
||||
if(!LDNS_QR_WIRE(ldns_buffer_begin(c->buffer))
|
||||
|| LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) !=
|
||||
LDNS_PACKET_QUERY
|
||||
|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
|
||||
/* error becomes timeout for the module as if this reply
|
||||
* never arrived. */
|
||||
verbose(VERB_ALGO, "worker: bad reply handled as timeout");
|
||||
mesh_report_reply(worker->env.mesh, e, 0, reply_info);
|
||||
worker_mem_report(worker);
|
||||
return 0;
|
||||
}
|
||||
mesh_report_reply(worker->env.mesh, e, 1, reply_info);
|
||||
worker_mem_report(worker);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** check request sanity. Returns error code, 0 OK, or -1 discard.
|
||||
* @param pkt: the wire packet to examine for sanity.
|
||||
* @param worker: parameters for checking.
|
||||
*/
|
||||
static int
|
||||
worker_check_request(ldns_buffer* pkt)
|
||||
worker_check_request(ldns_buffer* pkt, struct worker* worker)
|
||||
{
|
||||
if(ldns_buffer_limit(pkt) < LDNS_HEADER_SIZE) {
|
||||
verbose(VERB_DETAIL, "request too short, discarded");
|
||||
return -1;
|
||||
}
|
||||
if(ldns_buffer_limit(pkt) > NORMAL_UDP_SIZE &&
|
||||
worker->daemon->cfg->harden_large_queries) {
|
||||
verbose(VERB_DETAIL, "request too large, discarded");
|
||||
return -1;
|
||||
}
|
||||
if(LDNS_QR_WIRE(ldns_buffer_begin(pkt))) {
|
||||
verbose(VERB_DETAIL, "request has QR bit on, discarded");
|
||||
return -1;
|
||||
@@ -347,7 +222,12 @@ worker_check_request(ldns_buffer* pkt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** process control messages from the main thread. */
|
||||
/** process control messages from the main thread.
|
||||
* @param c: comm point to read from.
|
||||
* @param arg: worker.
|
||||
* @param error: error status of comm point.
|
||||
* @param reply_info: not used.
|
||||
*/
|
||||
static int
|
||||
worker_handle_control_cmd(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
@@ -377,6 +257,31 @@ worker_handle_control_cmd(struct comm_point* c, void* arg, int error,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** check cname chain in cache reply */
|
||||
static int
|
||||
check_cache_chain(struct reply_info* rep) {
|
||||
/* check only answer section rrs for matching cname chain.
|
||||
* the cache may return changed rdata, but owner names are untouched.*/
|
||||
size_t i;
|
||||
uint8_t* sname = rep->rrsets[0]->rk.dname;
|
||||
size_t snamelen = rep->rrsets[0]->rk.dname_len;
|
||||
for(i=0; i<rep->an_numrrsets; i++) {
|
||||
uint16_t t = ntohs(rep->rrsets[i]->rk.type);
|
||||
if(t == LDNS_RR_TYPE_DNAME)
|
||||
continue; /* skip dnames; note TTL 0 not cached */
|
||||
/* verify that owner matches current sname */
|
||||
if(query_dname_compare(sname, rep->rrsets[i]->rk.dname) != 0){
|
||||
/* cname chain broken */
|
||||
return 0;
|
||||
}
|
||||
/* if this is a cname; move on */
|
||||
if(t == LDNS_RR_TYPE_CNAME) {
|
||||
get_cname_target(rep->rrsets[i], &sname, &snamelen);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** answer query from the cache */
|
||||
static int
|
||||
answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id,
|
||||
@@ -386,49 +291,135 @@ answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id,
|
||||
struct reply_info* rep = (struct reply_info*)e->data;
|
||||
uint32_t timenow = time(0);
|
||||
uint16_t udpsize = edns->udp_size;
|
||||
size_t i;
|
||||
/* see if it is possible */
|
||||
if(rep->ttl <= timenow) {
|
||||
/* the rrsets may have been updated in the meantime */
|
||||
/* but this ignores it */
|
||||
/* the rrsets may have been updated in the meantime.
|
||||
* we will refetch the message format from the
|
||||
* authoritative server
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
edns->edns_version = EDNS_ADVERTISED_VERSION;
|
||||
edns->udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns->ext_rcode = 0;
|
||||
edns->bits &= EDNS_DO;
|
||||
/* check rrsets */
|
||||
for(i=0; i<rep->rrset_count; i++) {
|
||||
if(i>0 && rep->ref[i].key == rep->ref[i-1].key)
|
||||
continue; /* only lock items once */
|
||||
lock_rw_rdlock(&rep->ref[i].key->entry.lock);
|
||||
if(rep->ref[i].id != rep->ref[i].key->id ||
|
||||
rep->ttl <= timenow) {
|
||||
/* failure! rollback our readlocks */
|
||||
size_t j;
|
||||
for(j=0; j<=i; j++)
|
||||
lock_rw_unlock(&rep->ref[j].key->entry.lock);
|
||||
if(!rrset_array_lock(rep->ref, rep->rrset_count, timenow))
|
||||
return 0;
|
||||
/* 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 ==
|
||||
htons(LDNS_RR_TYPE_DNAME))) {
|
||||
if(!check_cache_chain(rep)) {
|
||||
rrset_array_unlock_touch(worker->env.rrset_cache,
|
||||
worker->scratchpad, rep->ref, rep->rrset_count);
|
||||
region_free_all(worker->scratchpad);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* locked and ids and ttls are OK. */
|
||||
if(!reply_info_answer_encode(&mrentry->key, rep, id, flags,
|
||||
repinfo->c->buffer, timenow, 1, worker->scratchpad,
|
||||
udpsize, edns)) {
|
||||
replyerror_fillbuf(LDNS_RCODE_SERVFAIL, repinfo, id,
|
||||
flags, &mrentry->key);
|
||||
}
|
||||
/* unlock */
|
||||
for(i=0; i<rep->rrset_count; i++) {
|
||||
if(i>0 && rep->ref[i].key == rep->ref[i-1].key)
|
||||
continue; /* only unlock items once */
|
||||
lock_rw_unlock(&rep->ref[i].key->entry.lock);
|
||||
udpsize, edns, (int)(edns->bits & EDNS_DO) )) {
|
||||
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
&mrentry->key, id, flags, edns);
|
||||
}
|
||||
/* cannot send the reply right now, because blocking network syscall
|
||||
* is bad while holding locks. */
|
||||
rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad,
|
||||
rep->ref, rep->rrset_count);
|
||||
region_free_all(worker->scratchpad);
|
||||
/* go and return this buffer to the client */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill CH class answer into buffer. Keeps query.
|
||||
* @param pkt: buffer
|
||||
* @param str: string to put into text record (<255).
|
||||
* @param edns: edns reply information.
|
||||
*/
|
||||
static void
|
||||
chaos_replystr(ldns_buffer* pkt, const char* str, struct edns_data* edns)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
unsigned int rd = LDNS_RD_WIRE(ldns_buffer_begin(pkt));
|
||||
unsigned int cd = LDNS_CD_WIRE(ldns_buffer_begin(pkt));
|
||||
if(len>255) len=255; /* cap size of TXT record */
|
||||
ldns_buffer_clear(pkt);
|
||||
ldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip id */
|
||||
ldns_buffer_write_u16(pkt, (uint16_t)(BIT_QR|BIT_RA));
|
||||
if(rd) LDNS_RD_SET(ldns_buffer_begin(pkt));
|
||||
if(cd) LDNS_CD_SET(ldns_buffer_begin(pkt));
|
||||
ldns_buffer_write_u16(pkt, 1); /* qdcount */
|
||||
ldns_buffer_write_u16(pkt, 1); /* ancount */
|
||||
ldns_buffer_write_u16(pkt, 0); /* nscount */
|
||||
ldns_buffer_write_u16(pkt, 0); /* arcount */
|
||||
(void)query_dname_len(pkt); /* skip qname */
|
||||
ldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip qtype */
|
||||
ldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip qclass */
|
||||
ldns_buffer_write_u16(pkt, 0xc00c); /* compr ptr to query */
|
||||
ldns_buffer_write_u16(pkt, LDNS_RR_TYPE_TXT);
|
||||
ldns_buffer_write_u16(pkt, LDNS_RR_CLASS_CH);
|
||||
ldns_buffer_write_u32(pkt, 0); /* TTL */
|
||||
ldns_buffer_write_u16(pkt, sizeof(uint8_t) + len);
|
||||
ldns_buffer_write_u8(pkt, len);
|
||||
ldns_buffer_write(pkt, str, len);
|
||||
ldns_buffer_flip(pkt);
|
||||
edns->edns_version = EDNS_ADVERTISED_VERSION;
|
||||
edns->udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns->bits &= EDNS_DO;
|
||||
attach_edns_record(pkt, edns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Answer CH class queries.
|
||||
* @param w: worker
|
||||
* @param qinfo: query info. Pointer into packet buffer.
|
||||
* @param edns: edns info from query.
|
||||
* @param pkt: packet buffer.
|
||||
* @return: true if a reply is to be sent.
|
||||
*/
|
||||
static int
|
||||
answer_chaos(struct worker* w, struct query_info* qinfo,
|
||||
struct edns_data* edns, ldns_buffer* pkt)
|
||||
{
|
||||
struct config_file* cfg = w->env.cfg;
|
||||
if(qinfo->qtype != LDNS_RR_TYPE_ANY && qinfo->qtype != LDNS_RR_TYPE_TXT)
|
||||
return 0;
|
||||
if(query_dname_compare(qinfo->qname,
|
||||
(uint8_t*)"\002id\006server") == 0 ||
|
||||
query_dname_compare(qinfo->qname,
|
||||
(uint8_t*)"\010hostname\004bind") == 0)
|
||||
{
|
||||
if(cfg->hide_identity)
|
||||
return 0;
|
||||
if(cfg->identity==NULL || cfg->identity[0]==0) {
|
||||
char buf[MAXHOSTNAMELEN];
|
||||
if (gethostname(buf, MAXHOSTNAMELEN) == 0)
|
||||
chaos_replystr(pkt, buf, edns);
|
||||
else {
|
||||
log_err("gethostname: %s", strerror(errno));
|
||||
chaos_replystr(pkt, "no hostname", edns);
|
||||
}
|
||||
}
|
||||
else chaos_replystr(pkt, cfg->identity, edns);
|
||||
return 1;
|
||||
}
|
||||
if(query_dname_compare(qinfo->qname,
|
||||
(uint8_t*)"\007version\006server") == 0 ||
|
||||
query_dname_compare(qinfo->qname,
|
||||
(uint8_t*)"\007version\004bind") == 0)
|
||||
{
|
||||
if(cfg->hide_version)
|
||||
return 0;
|
||||
if(cfg->version==NULL || cfg->version[0]==0)
|
||||
chaos_replystr(pkt, PACKAGE_STRING, edns);
|
||||
else chaos_replystr(pkt, cfg->version, edns);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** handles callbacks from listening event interface */
|
||||
static int
|
||||
worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
@@ -439,15 +430,14 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
hashvalue_t h;
|
||||
struct lruhash_entry* e;
|
||||
struct query_info qinfo;
|
||||
struct work_query* w;
|
||||
struct edns_data edns;
|
||||
|
||||
verbose(VERB_DETAIL, "worker handle request");
|
||||
if(error != NETEVENT_NOERROR) {
|
||||
log_err("called with err=%d", error);
|
||||
log_err("handle request called with err=%d", error);
|
||||
return 0;
|
||||
}
|
||||
if((ret=worker_check_request(c->buffer)) != 0) {
|
||||
if((ret=worker_check_request(c->buffer, worker)) != 0) {
|
||||
verbose(VERB_ALGO, "worker check request: bad query.");
|
||||
if(ret != -1) {
|
||||
LDNS_QR_SET(ldns_buffer_begin(c->buffer));
|
||||
LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), ret);
|
||||
@@ -459,13 +449,22 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
worker->stats.num_queries++;
|
||||
/* see if query is in the cache */
|
||||
if(!query_info_parse(&qinfo, c->buffer)) {
|
||||
verbose(VERB_ALGO, "worker parse request: formerror.");
|
||||
LDNS_QR_SET(ldns_buffer_begin(c->buffer));
|
||||
LDNS_RCODE_SET(ldns_buffer_begin(c->buffer),
|
||||
LDNS_RCODE_FORMERR);
|
||||
return 1;
|
||||
}
|
||||
h = query_info_hash(&qinfo);
|
||||
if(qinfo.qtype == LDNS_RR_TYPE_AXFR ||
|
||||
qinfo.qtype == LDNS_RR_TYPE_IXFR) {
|
||||
verbose(VERB_ALGO, "worker request: refused zone transfer.");
|
||||
LDNS_QR_SET(ldns_buffer_begin(c->buffer));
|
||||
LDNS_RCODE_SET(ldns_buffer_begin(c->buffer),
|
||||
LDNS_RCODE_REFUSED);
|
||||
return 1;
|
||||
}
|
||||
if((ret=parse_edns_from_pkt(c->buffer, &edns)) != 0) {
|
||||
verbose(VERB_ALGO, "worker parse edns: formerror.");
|
||||
LDNS_QR_SET(ldns_buffer_begin(c->buffer));
|
||||
LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), ret);
|
||||
return 1;
|
||||
@@ -475,17 +474,39 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
edns.edns_version = EDNS_ADVERTISED_VERSION;
|
||||
edns.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns.bits &= EDNS_DO;
|
||||
replyerror_fillbuf(EDNS_RCODE_BADVERS&0xf, repinfo,
|
||||
verbose(VERB_ALGO, "query with bad edns version.");
|
||||
error_encode(c->buffer, EDNS_RCODE_BADVERS&0xf, &qinfo,
|
||||
*(uint16_t*)ldns_buffer_begin(c->buffer),
|
||||
ldns_buffer_read_u16_at(c->buffer, 2), &qinfo);
|
||||
ldns_buffer_read_u16_at(c->buffer, 2), NULL);
|
||||
attach_edns_record(c->buffer, &edns);
|
||||
return 1;
|
||||
}
|
||||
if(edns.edns_present && edns.udp_size < NORMAL_UDP_SIZE &&
|
||||
worker->daemon->cfg->harden_short_bufsize) {
|
||||
verbose(VERB_DETAIL, "worker request: EDNS bufsize %d ignored",
|
||||
(int)edns.udp_size);
|
||||
edns.udp_size = NORMAL_UDP_SIZE;
|
||||
}
|
||||
if(edns.edns_present && edns.udp_size < LDNS_HEADER_SIZE) {
|
||||
verbose(VERB_ALGO, "worker request: edns is too small.");
|
||||
LDNS_QR_SET(ldns_buffer_begin(c->buffer));
|
||||
LDNS_TC_SET(ldns_buffer_begin(c->buffer));
|
||||
LDNS_RCODE_SET(ldns_buffer_begin(c->buffer),
|
||||
LDNS_RCODE_SERVFAIL);
|
||||
ldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
|
||||
ldns_buffer_flip(c->buffer);
|
||||
return 1;
|
||||
}
|
||||
if(c->type != comm_udp)
|
||||
edns.udp_size = 65535; /* max size for TCP replies */
|
||||
if((e=slabhash_lookup(worker->daemon->msg_cache, h, &qinfo, 0))) {
|
||||
if(qinfo.qclass == LDNS_RR_CLASS_CH && answer_chaos(worker, &qinfo,
|
||||
&edns, c->buffer)) {
|
||||
verbose(VERB_ALGO, "class CH reply");
|
||||
return 1;
|
||||
}
|
||||
h = query_info_hash(&qinfo);
|
||||
if((e=slabhash_lookup(worker->env.msg_cache, h, &qinfo, 0))) {
|
||||
/* answer from cache - we have acquired a readlock on it */
|
||||
log_info("answer from the cache");
|
||||
if(answer_from_cache(worker, e,
|
||||
*(uint16_t*)ldns_buffer_begin(c->buffer),
|
||||
ldns_buffer_read_u16_at(c->buffer, 2), repinfo,
|
||||
@@ -493,47 +514,35 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 1;
|
||||
}
|
||||
log_info("answer from the cache -- data has timed out");
|
||||
verbose(VERB_DETAIL, "answer from the cache -- data has timed out");
|
||||
lock_rw_unlock(&e->lock);
|
||||
}
|
||||
ldns_buffer_rewind(c->buffer);
|
||||
server_stats_querymiss(&worker->stats, worker);
|
||||
/* perform memory allocation(s) */
|
||||
if(!query_info_allocqname(&qinfo)) {
|
||||
comm_point_drop_reply(repinfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* grab a work request structure for this new request */
|
||||
if(!(w = worker->free_queries)) {
|
||||
if(worker->env.mesh->all.count > worker->request_size) {
|
||||
/* we could get this due to a slow tcp incoming query,
|
||||
that started before we performed listen_pushback */
|
||||
verbose(VERB_DETAIL, "worker: too many incoming requests "
|
||||
"active. dropping incoming query.");
|
||||
verbose(VERB_ALGO, "currently servicing %d of %d queries",
|
||||
(int)worker->env.mesh->all.count,
|
||||
(int)worker->request_size);
|
||||
worker->stats.num_query_list_exceeded++;
|
||||
comm_point_drop_reply(repinfo);
|
||||
query_info_clear(&qinfo);
|
||||
return 0;
|
||||
}
|
||||
w->edns = edns;
|
||||
worker->free_queries = w->next;
|
||||
worker->num_requests ++;
|
||||
log_assert(worker->num_requests <= worker->request_size);
|
||||
if(worker->num_requests == worker->request_size) {
|
||||
mesh_new_client(worker->env.mesh, &qinfo,
|
||||
ldns_buffer_read_u16_at(c->buffer, 2),
|
||||
&edns, repinfo, *(uint16_t*)ldns_buffer_begin(c->buffer));
|
||||
|
||||
if(worker->env.mesh->all.count == worker->request_size) {
|
||||
/* the max request number has been reached, stop accepting */
|
||||
listen_pushback(worker->front);
|
||||
}
|
||||
|
||||
/* init request */
|
||||
w->next = NULL;
|
||||
w->query_hash = h;
|
||||
memcpy(&w->query_reply, repinfo, sizeof(struct comm_reply));
|
||||
memcpy(&w->qinfo, &qinfo, sizeof(struct query_info));
|
||||
memcpy(&w->query_id, ldns_buffer_begin(c->buffer), sizeof(uint16_t));
|
||||
w->query_flags = ldns_buffer_read_u16_at(c->buffer, 2);
|
||||
|
||||
/* answer it */
|
||||
worker_process_query(worker, w);
|
||||
worker_mem_report(worker);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -601,40 +610,6 @@ worker_create(struct daemon* daemon, int id)
|
||||
return worker;
|
||||
}
|
||||
|
||||
/** create request handling structures */
|
||||
static int
|
||||
reqs_init(struct worker* worker)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<worker->request_size; i++) {
|
||||
struct work_query* q = (struct work_query*)calloc(1,
|
||||
sizeof(struct work_query));
|
||||
if(!q) return 0;
|
||||
q->worker = worker;
|
||||
q->next = worker->free_queries;
|
||||
worker->free_queries = q;
|
||||
q->all_next = worker->all_queries;
|
||||
worker->all_queries = q;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** delete request list */
|
||||
static void
|
||||
reqs_delete(struct worker* worker)
|
||||
{
|
||||
struct work_query* q = worker->all_queries;
|
||||
struct work_query* n;
|
||||
while(q) {
|
||||
n = q->all_next;
|
||||
log_assert(q->worker == worker);
|
||||
/* comm_reply closed in outside_network_delete */
|
||||
query_info_clear(&q->qinfo);
|
||||
free(q);
|
||||
q = n;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
worker_init(struct worker* worker, struct config_file *cfg,
|
||||
struct listen_port* ports, size_t buffer_size, int do_sigs)
|
||||
@@ -668,6 +643,21 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
||||
} else { /* !do_sigs */
|
||||
worker->comsig = 0;
|
||||
}
|
||||
/* init random(), large table size. */
|
||||
if(!(worker->rndstate = (struct ub_randstate*)calloc(1,
|
||||
sizeof(struct ub_randstate)))) {
|
||||
log_err("malloc rndtable failed.");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
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(!ub_initstate(seed, worker->rndstate, RND_STATE_SIZE)) {
|
||||
log_err("could not init random numbers.");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
worker->front = listen_create(worker->base, ports,
|
||||
buffer_size, worker_handle_request, worker);
|
||||
if(!worker->front) {
|
||||
@@ -680,26 +670,15 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
||||
worker->back = outside_network_create(worker->base,
|
||||
buffer_size, (size_t)cfg->outgoing_num_ports, cfg->ifs,
|
||||
cfg->num_ifs, cfg->do_ip4, cfg->do_ip6, startport,
|
||||
cfg->do_tcp?cfg->outgoing_num_tcp:0);
|
||||
cfg->do_tcp?cfg->outgoing_num_tcp:0,
|
||||
worker->daemon->env->infra_cache, worker->rndstate);
|
||||
if(!worker->back) {
|
||||
log_err("could not create outgoing sockets");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
/* init random(), large table size. */
|
||||
if(!(worker->rndstate = (struct ub_randstate*)calloc(1,
|
||||
sizeof(struct ub_randstate)))) {
|
||||
log_err("malloc rndtable failed.");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
seed = (unsigned int)time(NULL) ^ (unsigned int)getpid() ^
|
||||
(unsigned int)worker->thread_num;
|
||||
if(!ub_initstate(seed, worker->rndstate, RND_STATE_SIZE)) {
|
||||
log_err("could not init random numbers.");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
outside_network_set_secondary_buffer(worker->back,
|
||||
worker->front->udp_buff);
|
||||
if(worker->thread_num != 0) {
|
||||
/* start listening to commands */
|
||||
if(!(worker->cmd_com=comm_point_create_local(worker->base,
|
||||
@@ -710,24 +689,36 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
worker->request_size = cfg->num_queries_per_thread;
|
||||
if(!reqs_init(worker)) {
|
||||
worker->scratchpad = region_create_custom(malloc, free,
|
||||
65536, 8192, 32, 1);
|
||||
if(!worker->scratchpad) {
|
||||
log_err("malloc failure");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
worker->request_size = cfg->num_queries_per_thread;
|
||||
|
||||
/* set forwarder address */
|
||||
if(cfg->fwd_address && cfg->fwd_address[0]) {
|
||||
if(!worker_set_fwd(worker, cfg->fwd_address, cfg->fwd_port)) {
|
||||
worker_delete(worker);
|
||||
fatal_exit("could not set forwarder address");
|
||||
}
|
||||
}
|
||||
server_stats_init(&worker->stats);
|
||||
alloc_init(&worker->alloc, &worker->daemon->superalloc,
|
||||
worker->thread_num);
|
||||
worker->scratchpad = region_create_custom(malloc, free,
|
||||
65536, 8192, 32, 1);
|
||||
worker->env = *worker->daemon->env;
|
||||
worker->env.worker = worker;
|
||||
worker->env.alloc = &worker->alloc;
|
||||
worker->env.rnd = worker->rndstate;
|
||||
worker->env.scratch = worker->scratchpad;
|
||||
worker->env.mesh = mesh_create(worker->daemon->num_modules,
|
||||
worker->daemon->modfunc, &worker->env);
|
||||
worker->env.detach_subs = &mesh_detach_subs;
|
||||
worker->env.attach_sub = &mesh_attach_sub;
|
||||
worker->env.kill_sub = &mesh_state_delete;
|
||||
worker->env.query_done = &mesh_query_done;
|
||||
worker->env.walk_supers = &mesh_walk_supers;
|
||||
worker->env.detect_cycle = &mesh_detect_cycle;
|
||||
if(!worker->env.mesh) {
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
worker_mem_report(worker);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -742,8 +733,10 @@ worker_delete(struct worker* worker)
|
||||
{
|
||||
if(!worker)
|
||||
return;
|
||||
mesh_stats(worker->env.mesh, "mesh has");
|
||||
server_stats_log(&worker->stats, worker->thread_num);
|
||||
reqs_delete(worker);
|
||||
worker_mem_report(worker);
|
||||
mesh_delete(worker->env.mesh);
|
||||
listen_delete(worker->front);
|
||||
outside_network_delete(worker->back);
|
||||
comm_signal_delete(worker->comsig);
|
||||
@@ -765,34 +758,49 @@ worker_delete(struct worker* worker)
|
||||
}
|
||||
|
||||
int
|
||||
worker_set_fwd(struct worker* worker, const char* ip, int port)
|
||||
worker_send_packet(ldns_buffer* pkt, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, int timeout, struct module_qstate* q, int use_tcp)
|
||||
{
|
||||
uint16_t p;
|
||||
log_assert(worker && ip);
|
||||
p = (uint16_t) port;
|
||||
if(str_is_ip6(ip)) {
|
||||
struct sockaddr_in6* sa =
|
||||
(struct sockaddr_in6*)&worker->fwd_addr;
|
||||
worker->fwd_addrlen = (socklen_t)sizeof(struct sockaddr_in6);
|
||||
memset(sa, 0, worker->fwd_addrlen);
|
||||
sa->sin6_family = AF_INET6;
|
||||
sa->sin6_port = (in_port_t)htons(p);
|
||||
if(inet_pton((int)sa->sin6_family, ip, &sa->sin6_addr) <= 0) {
|
||||
log_err("Bad ip6 address %s", ip);
|
||||
return 0;
|
||||
}
|
||||
} else { /* ip4 */
|
||||
struct sockaddr_in* sa =
|
||||
(struct sockaddr_in*)&worker->fwd_addr;
|
||||
worker->fwd_addrlen = (socklen_t)sizeof(struct sockaddr_in);
|
||||
memset(sa, 0, worker->fwd_addrlen);
|
||||
sa->sin_family = AF_INET;
|
||||
sa->sin_port = (in_port_t)htons(p);
|
||||
if(inet_pton((int)sa->sin_family, ip, &sa->sin_addr) <= 0) {
|
||||
log_err("Bad ip4 address %s", ip);
|
||||
return 0;
|
||||
}
|
||||
struct worker* worker = q->env->worker;
|
||||
if(use_tcp) {
|
||||
return pending_tcp_query(worker->back, pkt, addr, addrlen,
|
||||
timeout, worker_handle_reply, q,
|
||||
worker->rndstate) != 0;
|
||||
}
|
||||
verbose(VERB_ALGO, "fwd queries to: %s %d", ip, p);
|
||||
return 1;
|
||||
return pending_udp_query(worker->back, pkt, addr, addrlen,
|
||||
timeout*1000, worker_handle_reply, q,
|
||||
worker->rndstate) != 0;
|
||||
}
|
||||
|
||||
/** compare outbound entry qstates */
|
||||
static int
|
||||
outbound_entry_compare(void* a, void* b)
|
||||
{
|
||||
struct outbound_entry* e1 = (struct outbound_entry*)a;
|
||||
struct outbound_entry* e2 = (struct outbound_entry*)b;
|
||||
if(e1->qstate == e2->qstate)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct outbound_entry*
|
||||
worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype,
|
||||
uint16_t qclass, uint16_t flags, int dnssec,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
struct module_qstate* q)
|
||||
{
|
||||
struct worker* worker = q->env->worker;
|
||||
struct outbound_entry* e = (struct outbound_entry*)malloc(sizeof(*e));
|
||||
if(!e)
|
||||
return NULL;
|
||||
e->qstate = q;
|
||||
e->qsent = outnet_serviced_query(worker->back, qname,
|
||||
qnamelen, qtype, qclass, flags, dnssec, addr, addrlen,
|
||||
worker_handle_service_reply, e, worker->back->udp_buff,
|
||||
&outbound_entry_compare);
|
||||
if(!e->qsent) {
|
||||
free(e);
|
||||
return NULL;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
+37
-42
@@ -50,6 +50,7 @@
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "daemon/stats.h"
|
||||
#include "util/module.h"
|
||||
struct listen_dnsport;
|
||||
struct outside_network;
|
||||
struct config_file;
|
||||
@@ -67,28 +68,6 @@ enum worker_commands {
|
||||
worker_cmd_quit
|
||||
};
|
||||
|
||||
/** information per query that is in processing */
|
||||
struct work_query {
|
||||
/** next query in freelist */
|
||||
struct work_query* next;
|
||||
/** the worker for this query */
|
||||
struct worker* worker;
|
||||
/** the query reply destination, packet buffer and where to send. */
|
||||
struct comm_reply query_reply;
|
||||
/** the query_info structure from the query */
|
||||
struct query_info qinfo;
|
||||
/** hash value of the query qinfo */
|
||||
hashvalue_t query_hash;
|
||||
/** next query in all-list */
|
||||
struct work_query* all_next;
|
||||
/** id of query, in network byteorder. */
|
||||
uint16_t query_id;
|
||||
/** flags uint16 from query */
|
||||
uint16_t query_flags;
|
||||
/** edns data from the query */
|
||||
struct edns_data edns;
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure holding working information for unbound.
|
||||
* Holds globally visible information.
|
||||
@@ -115,19 +94,8 @@ struct worker {
|
||||
/** commpoint to listen to commands. */
|
||||
struct comm_point* cmd_com;
|
||||
|
||||
/** number of requests currently active */
|
||||
size_t num_requests;
|
||||
/** number of requests that can be handled by this worker */
|
||||
size_t request_size;
|
||||
/** the free working queries */
|
||||
struct work_query* free_queries;
|
||||
/** list of all working queries */
|
||||
struct work_query* all_queries;
|
||||
|
||||
/** address to forward to */
|
||||
struct sockaddr_storage fwd_addr;
|
||||
/** length of fwd_addr */
|
||||
socklen_t fwd_addrlen;
|
||||
|
||||
/** random() table for this worker. */
|
||||
struct ub_randstate* rndstate;
|
||||
@@ -139,6 +107,9 @@ struct worker {
|
||||
struct server_stats stats;
|
||||
/** thread scratch region */
|
||||
struct region* scratchpad;
|
||||
|
||||
/** module environment passed to modules, changed for this thread */
|
||||
struct module_env env;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -173,15 +144,6 @@ void worker_work(struct worker* worker);
|
||||
*/
|
||||
void worker_delete(struct worker* worker);
|
||||
|
||||
/**
|
||||
* Set forwarder
|
||||
* @param worker: the worker to modify.
|
||||
* @param ip: the server name.
|
||||
* @param port: port on server or NULL for default 53.
|
||||
* @return: false on error.
|
||||
*/
|
||||
int worker_set_fwd(struct worker* worker, const char* ip, int port);
|
||||
|
||||
/**
|
||||
* Send a command to a worker. Uses blocking writes.
|
||||
* @param worker: worker to send command to.
|
||||
@@ -198,4 +160,37 @@ void worker_send_cmd(struct worker* worker, ldns_buffer* buffer,
|
||||
*/
|
||||
void worker_sighandler(int sig, void* arg);
|
||||
|
||||
/**
|
||||
* Worker service routine to send udp messages for modules.
|
||||
* @param pkt: packet to send.
|
||||
* @param addr: where to.
|
||||
* @param addrlen: length of addr.
|
||||
* @param timeout: seconds to wait until timeout.
|
||||
* @param q: wich query state to reactivate upon return.
|
||||
* @param use_tcp: true to use TCP, false for UDP.
|
||||
* @return: false on failure (memory or socket related). no query was
|
||||
* sent.
|
||||
*/
|
||||
int worker_send_packet(ldns_buffer* pkt, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, int timeout, struct module_qstate* q, int use_tcp);
|
||||
|
||||
/**
|
||||
* Worker service routine to send serviced queries to authoritative servers.
|
||||
* @param qname: query name. (host order)
|
||||
* @param qnamelen: length in bytes of qname, including trailing 0.
|
||||
* @param qtype: query type. (host order)
|
||||
* @param qclass: query class. (host order)
|
||||
* @param flags: host order flags word, with opcode and CD bit.
|
||||
* @param dnssec: if set, EDNS record will have DO bit set.
|
||||
* @param addr: where to.
|
||||
* @param addrlen: length of addr.
|
||||
* @param q: wich query state to reactivate upon return.
|
||||
* @return: false on failure (memory or socket related). no query was
|
||||
* sent.
|
||||
*/
|
||||
struct outbound_entry* worker_send_query(uint8_t* qname, size_t qnamelen,
|
||||
uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
struct module_qstate* q);
|
||||
|
||||
#endif /* DAEMON_WORKER_H */
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
Unbound was developed at NLnet Labs by Wouter Wijngaards.
|
||||
|
||||
Unbound was devised by Bill Manning, David Blacka, and Matt Larson
|
||||
from the University of California and from Verisign. The Java prototype
|
||||
was made in further cooperation with Geoff Sisson and Roy Arends from Nominet.
|
||||
|
||||
At NLnet Labs, Jelte Jansen and Mark Santcroos reviewed the unbound C sources.
|
||||
+329
@@ -1,3 +1,332 @@
|
||||
31 July 2007: Wouter
|
||||
- updated plan
|
||||
|
||||
30 July 2007: Wouter
|
||||
- changed random state init, so that sequential process IDs are not
|
||||
cancelled out by sequential thread-ids in the random number seed.
|
||||
- the fwd_three test, which sends three queries to unbound, and
|
||||
unbound is kept waiting by ldns-testns for 3 seconds, failed
|
||||
because the retry timeout for default by unbound is 3 seconds too,
|
||||
it would hit that timeout and fail the test. Changed so that unbound
|
||||
is kept waiting for 2 seconds instead.
|
||||
|
||||
27 July 2007: Wouter
|
||||
- removed useless -C debug option. It did not work.
|
||||
- text edit of documentation.
|
||||
- added doc/CREDITS file, referred to by the manpages.
|
||||
- updated planning.
|
||||
|
||||
26 July 2007: Wouter
|
||||
- cycle detection, for query state dependencies. Will attempt to
|
||||
circumvent the cycle, but if no other targets available fails.
|
||||
- unit test for AXFR, IXFR response.
|
||||
- test for cycle detection.
|
||||
|
||||
25 July 2007: Wouter
|
||||
- testbound read ADDRESS and check it.
|
||||
- test for version.bind and friends.
|
||||
- test for iterator chaining through several referrals.
|
||||
- test and fixup for refetch for glue. Refetch fails if glue
|
||||
is still not provided.
|
||||
|
||||
24 July 2007: Wouter
|
||||
- Example section in config manual.
|
||||
- Addr stored for range and moment in replay.
|
||||
|
||||
20 July 2007: Wouter
|
||||
- Check CNAME chain before returning cache entry with CNAMEs.
|
||||
- Option harden-glue, default is on. It will discard out of zone
|
||||
data. If disabled, performance is faster, but spoofing attempts
|
||||
become a possibility. Note that still normalize scrubbing is done,
|
||||
and that the potentially spoofed data is used for infrastructure
|
||||
and not returned to the client.
|
||||
- if glue times out, refetch by asking parent of delegation again.
|
||||
Much like asking for DS at the parent side.
|
||||
- TODO items from forgery-resilience draft.
|
||||
and on memory handling improvements.
|
||||
- renamed module_event_timeout to module_event_noreply.
|
||||
- memory reporting code; reports on memory usage after handling
|
||||
a network packet (not on cache replies).
|
||||
|
||||
19 July 2007: Wouter
|
||||
- shuffle NS selection when getting nameserver target addresses.
|
||||
- fixup of deadlock warnings, yield cpu in checklock code so that
|
||||
freebsd scheduler selects correct process to run.
|
||||
- added identity and version config options and replies.
|
||||
- store cname messages complete answers.
|
||||
|
||||
18 July 2007: Wouter
|
||||
- do not query addresses, 127.0.0.1, and ::1 by default.
|
||||
|
||||
17 July 2007: Wouter
|
||||
- forward zone options in config file.
|
||||
- forward per zone in iterator. takes precendence over stubs.
|
||||
- fixup commithooks.
|
||||
- removed forward-to and forward-to-port features, subsumed by
|
||||
new forward zones.
|
||||
- fix parser to handle absent server: clause.
|
||||
- change untrusted rrset test to account for scrubber that is now
|
||||
applied during the test (which removes the poison, by the way).
|
||||
- feature, addresses can be specified with @portnumber, like nsd.conf.
|
||||
- test config files changed over to new forwarder syntax.
|
||||
|
||||
27 June 2007: Wouter
|
||||
- delete of mesh does a postorder traverse of the tree.
|
||||
- found and fixed a memory leak. For TTL=0 messages, that would
|
||||
not be cached, instead the msg-replyinfo structure was leaked.
|
||||
- changed server selection so it will filter out hosts that are
|
||||
unresponsive. This is defined as a host with the maximum rto value.
|
||||
This means that unbound tried the host for retries up to 120 secs.
|
||||
The rto value will time out after host-ttl seconds from the cache.
|
||||
This keeps such unresolvable queries from taking up resources.
|
||||
- utility for keeping histogram.
|
||||
|
||||
26 June 2007: Wouter
|
||||
- mesh is called by worker, and iterator uses it.
|
||||
This removes the hierarchical code.
|
||||
QueryTargets state and Finished state are merged for iterator.
|
||||
- forwarder mode no longer sets AA bit on first reply.
|
||||
- rcode in walk_supers is not needed.
|
||||
|
||||
25 June 2007: Wouter
|
||||
- more mesh work.
|
||||
- error encode routine for ease.
|
||||
|
||||
22 June 2007: Wouter
|
||||
- removed unused _node iterator value from rbtree_t. Takes up space.
|
||||
- iterator can handle querytargets state without a delegation point
|
||||
set, so that a priming(stub) subquery error can be handled.
|
||||
- iterator stores if it is priming or not.
|
||||
- log_query_info() neater logging.
|
||||
- changed iterator so that it does not alter module_qstate.qinfo
|
||||
but keeps a chase query info. Also query_flags are not altered,
|
||||
the iterator uses chase_flags.
|
||||
- fixup crash in case no ports for the family exist.
|
||||
|
||||
21 June 2007: Wouter
|
||||
- Fixup secondary buffer in case of error callback.
|
||||
- cleanup slumber list of runnable states.
|
||||
- module_subreq_depth fails to work in slumber list.
|
||||
- fixup query release for cached results to sub targets.
|
||||
- neater error for tcp connection failure, shows addr in verbose.
|
||||
- rbtree_init so that it can be used with preallocated memory.
|
||||
|
||||
20 June 2007: Wouter
|
||||
- new -C option to enable coredumps after forking away.
|
||||
- doc update.
|
||||
- fixup CNAME generation by scrubber, and memory allocation of it.
|
||||
- fixup deletion of serviced queries when all callbacks delete too.
|
||||
- set num target queries to 0 when you move them to slumber list.
|
||||
- typo in check caused subquery errors to be ignored, fixed.
|
||||
- make lint happy about rlim_t.
|
||||
- freeup of modules after freeup of module-states.
|
||||
- duplicate replies work, this uses secondary udp buffer in outnet.
|
||||
|
||||
19 June 2007: Wouter
|
||||
- nicer layout in stats.c, review 0.3 change.
|
||||
- spelling improvement, review 0.3 change.
|
||||
- uncapped timeout for server selection, so that very fast or slow
|
||||
servers will stand out from the rest.
|
||||
- target-fetch-policy: "3 2 1 0 0" config setting.
|
||||
- fixup queries answered without RD bit (for root prime results).
|
||||
- refuse AXFR and IXFR requests.
|
||||
- fixup RD flag in error reply from iterator. fixup RA flag from
|
||||
worker error reply.
|
||||
- fixup encoding of very short edns buffer sizes, now sets TC bit.
|
||||
- config options harden-short-bufsize and harden-large-queries.
|
||||
|
||||
18 June 2007: Wouter
|
||||
- same, move subqueries to slumber list when first has resolved.
|
||||
- fixup last fix for duplicate callbacks.
|
||||
- another offbyone in targetcounter. Also in Java prototype by the way.
|
||||
|
||||
15 June 2007: Wouter
|
||||
- if a query asks to be notified of the same serviced query result
|
||||
multiple times, this will succeed. Only one callback will happen;
|
||||
multiple outbound-list entries result (but the double cleanup of it
|
||||
will not matter).
|
||||
- when iterator moves on due to CNAME or referral, it will remove
|
||||
the subqueries (for other targets). These are put on the slumber
|
||||
list.
|
||||
- state module wait subq is OK with no new subqs, an old one may have
|
||||
stopped, with an error, and it is still waiting for other ones.
|
||||
- if a query loops, halt entire query (easy way to clean up properly).
|
||||
|
||||
14 June 2007: Wouter
|
||||
- num query targets was > 0 , not >= 0 compared, so that fetch
|
||||
policy of 0 did nothing.
|
||||
|
||||
13 June 2007: Wouter
|
||||
- debug option: configure --enable-static-exe for compile where
|
||||
ldns and libevent are linked statically. Default is off.
|
||||
- make install and make uninstall. Works with static-exe and without.
|
||||
installation of unbound binary and manual pages.
|
||||
- alignement problem fix on solaris 64.
|
||||
- fixup address in case of TCP error.
|
||||
|
||||
12 June 2007: Wouter
|
||||
- num target queries was set to 0 at a bad time. Default it to 0 and
|
||||
increase as target queries are done.
|
||||
- synthesize CNAME and DNAME responses from the cache.
|
||||
- Updated doxygen config for doxygen 1.5.
|
||||
- aclocal newer version.
|
||||
- doxygen 1.5 fixes for comments (for the strict check on docs).
|
||||
|
||||
11 June 2007: Wouter
|
||||
- replies on TCP queries have the address field set in replyinfo,
|
||||
for serviced queries, because the initiator does not know that
|
||||
a TCP fallback has occured.
|
||||
- omit DNSSEC types from nonDO replies, except if qtype is ANY or
|
||||
if qtype directly queries for the type (and then only show that
|
||||
'unknown type' in the answer section).
|
||||
- fixed message parsing where rrsigs on their own would be put
|
||||
in the signature list over the rrsig type.
|
||||
|
||||
7 June 2007: Wouter
|
||||
- fixup error in double linked list insertion for subqueries and
|
||||
for outbound list of serviced queries for iterator module.
|
||||
- nicer printout of outgoing port selection.
|
||||
- fixup cname target readout.
|
||||
- nicer debug output.
|
||||
- fixup rrset counts when prepending CNAMEs to the answer.
|
||||
- fixup rrset TTL for prepended CNAMEs.
|
||||
- process better check for looping modules, and which submodule to
|
||||
run next.
|
||||
- subreq insertion code fixup for slumber list.
|
||||
- VERB_DETAIL, verbosity: 2 level gives short but readable output.
|
||||
VERB_ALGO, verbosity: 3 gives extensive output.
|
||||
- fixup RA bit in cached replies.
|
||||
- fixup CNAME responses from the cache no longer partial response.
|
||||
- error in network send handled without leakage.
|
||||
- enable ip6 from config, and try ip6 addresses if available,
|
||||
if ip6 is not connected, skips to next server.
|
||||
|
||||
5 June 2007: Wouter
|
||||
- iterator state finished.
|
||||
- subrequests without parent store in cache and stop.
|
||||
- worker slumber list for ongoing promiscuous queries.
|
||||
- subrequest error handling.
|
||||
- priming failure returns SERVFAIL.
|
||||
- priming gives LAME result, returns SERVFAIL.
|
||||
- debug routine to print dns_msg as handled by iterator.
|
||||
- memleak in config file stubs fixup.
|
||||
- more small bugs, in scrubber, query compare no ID for lookup,
|
||||
in dname validation for NS targets.
|
||||
- sets entry.key for new special allocs.
|
||||
- lognametypeclass can display unknown types and classes.
|
||||
|
||||
4 June 2007: Wouter
|
||||
- random selection of equally preferred nameserver targets.
|
||||
- reply info copy routine. Reuses existing code.
|
||||
- cache lameness in response handling.
|
||||
- do not touch qstate after worker_process_query because it may have
|
||||
been deleted by that routine.
|
||||
- Prime response state.
|
||||
- Process target response state.
|
||||
- some memcmp changed to dname_compare for case preservation.
|
||||
|
||||
1 June 2007: Wouter
|
||||
- normalize incoming messages. Like unbound-java, with CNAME chain
|
||||
checked, DNAME checked, CNAME's synthesized, glue checked.
|
||||
- sanitize incoming messages.
|
||||
- split msgreply encode functions into own file msgencode.c.
|
||||
- msg_parse to queryinfo/replyinfo conversion more versatile.
|
||||
- process_response, classify response, delegpt_from_message.
|
||||
|
||||
31 May 2007: Wouter
|
||||
- querytargets state.
|
||||
- dname_subdomain_c() routine.
|
||||
- server selection, based on RTT. ip6 is filtered out if not available,
|
||||
and lameness is checked too.
|
||||
- delegation point copy routine.
|
||||
|
||||
30 May 2007: Wouter
|
||||
- removed FLAG_CD from message and rrset caches. This was useful for
|
||||
an agnostic forwarder, but not for a sophisticated (trust value per
|
||||
rrset enabled) cache.
|
||||
- iterator reponse typing.
|
||||
- iterator cname handle.
|
||||
- iterator prime start.
|
||||
- subquery work.
|
||||
- processInitRequest and processInitRequest2.
|
||||
- cache synthesizes referral messages, with DS and NSEC.
|
||||
- processInitRequest3.
|
||||
- if a request creates multiple subrequests these are all activated.
|
||||
|
||||
29 May 2007: Wouter
|
||||
- routines to lock and unlock array of rrsets moved to cache/rrset.
|
||||
- lookup message from msg cache (and copy to region).
|
||||
- fixed cast error in dns msg lookup.
|
||||
- message with duplicate rrset does not increase its TTLs twice.
|
||||
- 'qnamesize' changed to 'qname_len' for similar naming scheme.
|
||||
|
||||
25 May 2007: Wouter
|
||||
- Acknowledge use of unbound-java code in iterator. Nicer readme.
|
||||
- services/cache/dns.c DNS Cache. Hybrid cache uses msgcache and
|
||||
rrset cache from module environment.
|
||||
- packed rrset key has type and class as easily accessable struct
|
||||
members. They are still kept in network format for fast msg encode.
|
||||
- dns cache find_delegation routine.
|
||||
- iterator main functions setup.
|
||||
- dns cache lookup setup.
|
||||
|
||||
24 May 2007: Wouter
|
||||
- small changes to prepare for subqueries.
|
||||
- iterator forwarder feature separated out.
|
||||
- iterator hints stub code, config file stub code, so that first
|
||||
testing can proceed locally.
|
||||
- replay tests now have config option to enable forwarding mode.
|
||||
|
||||
23 May 2007: Wouter
|
||||
- outside network does precise timers for roundtrip estimates for rtt
|
||||
and for setting timeout for UDP. Pending_udp takes milliseconds.
|
||||
- cleaner iterator sockaddr conversion of forwarder address.
|
||||
- iterator/iter_utils and iter_delegpt setup.
|
||||
- root hints.
|
||||
|
||||
22 May 2007: Wouter
|
||||
- outbound query list for modules and support to callback with the
|
||||
outbound entry to the module.
|
||||
- testbound support for new serviced queries.
|
||||
- test for retry to TCP cannot use testbound any longer.
|
||||
- testns test for EDNS fallback, test for TCP fallback already exists.
|
||||
- fixes for no-locking compile.
|
||||
- mini_event timer precision and fix for change in timeouts during
|
||||
timeout callback. Fix for fwd_three tests, performed nonexit query.
|
||||
|
||||
21 May 2007: Wouter
|
||||
- small comment on hash table locking.
|
||||
- outside network serviced queries, contain edns and tcp fallback,
|
||||
and udp retries and rtt timing.
|
||||
|
||||
16 May 2007: Wouter
|
||||
- lruhash_touch() would cause locking order problems. Fixup in
|
||||
lock-verify in case locking cycle is found.
|
||||
- services/cache/rrset.c for rrset cache code.
|
||||
- special rrset_cache LRU updating function that uses the rrset id.
|
||||
- no dependencies calculation when make clean is called.
|
||||
- config settings for infra cache.
|
||||
- daemon code slightly cleaner, only creates caches once.
|
||||
|
||||
15 May 2007: Wouter
|
||||
- host cache code.
|
||||
- unit test for host cache.
|
||||
|
||||
14 May 2007: Wouter
|
||||
- Port to OS/X and Dec Alpha. Printf format and alignment fixes.
|
||||
- extensive lock debug report on join timeout.
|
||||
- proper RTT calculation, in utility code.
|
||||
- setup of services/cache/infra, host cache.
|
||||
|
||||
11 May 2007: Wouter
|
||||
- iterator/iterator.c module.
|
||||
- fixup to pass reply_info in testcode and in netevent.
|
||||
|
||||
10 May 2007: Wouter
|
||||
- created release-0.3 svn tag.
|
||||
- util/module.h
|
||||
- fixed compression - no longer compresses root name.
|
||||
|
||||
9 May 2007: Wouter
|
||||
- outside network cleans up waiting tcp queries on exit.
|
||||
- fallback to TCP.
|
||||
|
||||
+26
-6
@@ -4,20 +4,31 @@ http://unbound.net
|
||||
|
||||
This software is under BSD license, see LICENSE for details.
|
||||
|
||||
* Download the latest version of this software from
|
||||
http://unbound.net
|
||||
or get a beta version from the svn repository at
|
||||
http://unbound.net/svn/
|
||||
|
||||
* Needs the following libraries
|
||||
* ldns http://www.nlnetlabs.nl/ldns/ (BSD license)
|
||||
* libevent http://www.monkey.org/~provos/libevent/ (BSD license)
|
||||
|
||||
* Make and install: ./configure; make
|
||||
* --with-ldns=/path/to/ldns
|
||||
* --with-libevent=/path/to/libevent
|
||||
Can be set to either the system install or the build directory.
|
||||
--with-libevent=no gives an alternative to libevent (based on select).
|
||||
|
||||
* Create build environment
|
||||
* run libtoolize -c if config.sub is missing.
|
||||
* autoreconf (autoheader && autoconf), if ./configure is missing.
|
||||
|
||||
* Make and install: ./configure; make; make install
|
||||
* --with-ldns=/path/to/ldns
|
||||
* --with-libevent=/path/to/libevent
|
||||
Can be set to either the system install or the build directory.
|
||||
--with-libevent=no gives a builtin alternative implementation.
|
||||
* --without-pthreads
|
||||
This disables pthreads, and uses Solaris thr library or no threading.
|
||||
Without this option the pthreads library is detected automatically.
|
||||
* --enable-static-exe
|
||||
This enables a debug option to statically link, against ldns and
|
||||
libevent libraries.
|
||||
|
||||
Known issues
|
||||
------------
|
||||
o If libevent is older (1.3 and before), unbound will exit instead of reload
|
||||
@@ -25,4 +36,13 @@ o If libevent is older (1.3 and before), unbound will exit instead of reload
|
||||
printed. Perform ./configure --with-libevent=no or update libevent, rerun
|
||||
configure and recompile unbound to make sighup work correctly.
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
o Thanks to David Blacka and Matt Larson (Verisign) for the unbound-java
|
||||
prototype. Design and code from that prototype has been used to create
|
||||
this program. Such as the iterator state machine and the cache design.
|
||||
o Other code origins are from the NSD (NLnet Labs) and LDNS (NLnet Labs)
|
||||
projects. Such as buffer, region-allocator and red-black tree code.
|
||||
o See Credits file for contributors.
|
||||
|
||||
* mailto:wouter@nlnetlabs.nl
|
||||
|
||||
@@ -5,16 +5,26 @@ o profile memory allocation, and if performance issues, use special memory
|
||||
allocator. For example, with caches per thread.
|
||||
o #define BIT_... different on bigendian and smallendian systems so that
|
||||
the htons on flags is not needed to send a message from the cache.
|
||||
o possible optimization with delayed malloc of msgreply after parse
|
||||
(reuse parse structures and ptrs to packet in meantime).
|
||||
o possible optimization, so that precious id number resource is not depleted
|
||||
by parsing of messages. Delay malloc, as above, or try to reverse release
|
||||
special id numbers, and if you release the next_id number for the thread
|
||||
it reuses that id number.
|
||||
o use rbtree to compress domain names in messages, sorted AXFRs of 16Kb
|
||||
and so on. For current packets, even worstcase, list, is ok. Only if we
|
||||
start to send out compressed AXFRs then it becomes a problem.
|
||||
We can cap AXFR packets on 256 or 1024 domain names, for example.
|
||||
o speed up pkt domain name decompression loop detection using counter perhaps.
|
||||
o detect OS/400 pthreads implementation that allows upgrading to writelock
|
||||
on pthreads rwlocks and use it to examine-rd before storing-wr rrset cache.
|
||||
o understand synthesized DNAMEs, so those TTL=0 packets are cached properly.
|
||||
o understand NSEC/NSEC3, aggressive negative caching, so that updates to
|
||||
NSEC/NSEC3 will result in proper negative responses.
|
||||
o fallback without EDNS if result is NOTIMPL, now only on FORMERR like in java.
|
||||
o scrubber has slow pkt_subdomain and pkt_strict_subdomain functions.
|
||||
o get serverselection algorithm out of local optimum.
|
||||
make subtargets to get rtt info for a couple of targets, like fetch-policy.
|
||||
or send out multiple queries to multiple servers.
|
||||
o donotqueryaddresses allow specification of subnets, use trie to store.
|
||||
extend default list to contain illegal ip4 and ip6 addresses.
|
||||
o memory profile; and if needed put serviced queries under qstate->region
|
||||
and special purpose region code, reuse blocks, shrink if too big.
|
||||
o check query, option to enforce presence of qdsection in noerror answers.
|
||||
o configuration option where port 53 is used for send and receive, no other
|
||||
ports are used.
|
||||
o prime sequence in multiple queries, if glue missing from prime sequence.
|
||||
o (option) to not send replies to clients after a timeout of (say 5 secs) has
|
||||
passed, but keep task active for later retries by client.
|
||||
o private TTL feature
|
||||
o pretend-dnssec-unaware, and pretend-edns-unaware modes for debug/workshops.
|
||||
|
||||
+79
-8
@@ -63,6 +63,24 @@ server:
|
||||
# more slabs reduce lock contention, but fragment memory usage.
|
||||
# rrset-cache-slabs: 4
|
||||
|
||||
# the time to live (TTL) value for cached roundtrip times and
|
||||
# EDNS version information for hosts. In seconds.
|
||||
# infra-host-ttl: 900
|
||||
|
||||
# the time to live (TTL) value for cached lame delegations. In sec.
|
||||
# infra-lame-ttl: 900
|
||||
|
||||
# the number of slabs to use for the Infrastructure cache.
|
||||
# the number of slabs must be a power of 2.
|
||||
# more slabs reduce lock contention, but fragment memory usage.
|
||||
# infra-cache-slabs: 4
|
||||
|
||||
# the maximum number of hosts that are cached (roundtrip times, EDNS).
|
||||
# infra-cache-numhosts: 1000
|
||||
|
||||
# the maximum number of lame zones per host that are cached.
|
||||
# infra-cache-numlame: 1000
|
||||
|
||||
# Enable IPv4, "yes" or "no".
|
||||
# do-ip4: yes
|
||||
|
||||
@@ -75,14 +93,6 @@ server:
|
||||
# Enable TCP, "yes" or "no".
|
||||
# do-tcp: yes
|
||||
|
||||
# Set this to configure unbound to act as a forwarder. All queries are
|
||||
# sent to the remote nameserver that will resolve them.
|
||||
# Set to "" to disable forwarding, or give ip-address to enable.
|
||||
# forward-to: ""
|
||||
|
||||
# The port number to send forwarded queries to.
|
||||
# forward-to-port: 53
|
||||
|
||||
# if given, a chroot(2) is done to the given directory.
|
||||
# i.e. you can chroot to the working directory, for example,
|
||||
# for extra security, but make sure all files are in that directory.
|
||||
@@ -100,3 +110,64 @@ server:
|
||||
|
||||
# the pid file.
|
||||
# pidfile: "unbound.pid"
|
||||
|
||||
# enable to not answer id.server and hostname.bind queries.
|
||||
# hide-identity: no
|
||||
|
||||
# enable to not answer version.server and version.bind queries.
|
||||
# hide-version: no
|
||||
|
||||
# the identity to report. Leave "" or default to return hostname.
|
||||
# identity: ""
|
||||
|
||||
# the version to report. Leave "" or default to return package version.
|
||||
# version: ""
|
||||
|
||||
# the target fetch policy.
|
||||
# series of integers describing the policy per dependency depth.
|
||||
# The number of values in the list determines the maximum dependency
|
||||
# depth the recursor will pursue before giving up. Each integer means:
|
||||
# -1 : fetch all targets opportunistically,
|
||||
# 0: fetch on demand,
|
||||
# positive value: fetch that many targets opportunistically.
|
||||
# Enclose the list of numbers between quotes ("").
|
||||
# target-fetch-policy: "3 2 1 0 0"
|
||||
|
||||
# Harden against very small EDNS buffer sizes.
|
||||
# harden-short-bufsize: no
|
||||
|
||||
# Harden against unseemly large queries.
|
||||
# harden-large-queries: no
|
||||
|
||||
# Harden against out of zone rrsets, to avoid spoofing attempts.
|
||||
# harden-glue: yes
|
||||
|
||||
# Do not query the following addresses. No DNS queries are sent there.
|
||||
# List one address per entry. To block other ports than the default
|
||||
# DNS port, use "1.2.3.4@123" to block port 123 for 1.2.3.4.
|
||||
# do-not-query-address: 127.0.0.1
|
||||
# do-not-query-address: ::1
|
||||
|
||||
# Stub zones.
|
||||
# Create entries like below, to make all queries for 'example.com' and
|
||||
# 'example.org' go to the given list of nameservers. list zero or more
|
||||
# nameservers by hostname or by ipaddress.
|
||||
# stub-zone:
|
||||
# name: "example.com"
|
||||
# stub-addr: 192.0.2.68
|
||||
# stub-zone:
|
||||
# name: "example.org"
|
||||
# stub-host: ns.example.com.
|
||||
|
||||
# Forward zones
|
||||
# Create entries like below, to make all queries for 'example.com' and
|
||||
# 'example.org' go to the given list of servers. These servers have to handle
|
||||
# recursion to other nameservers. List zero or more nameservers by hostname
|
||||
# or by ipaddress. Use an entry with name "." to forward all queries.
|
||||
# forward-zone:
|
||||
# name: "example.com"
|
||||
# forward-addr: 192.0.2.68
|
||||
# forward-addr: 192.0.2.73@5355 # forward to port 5355.
|
||||
# forward-zone:
|
||||
# name: "example.org"
|
||||
# forward-host: fwd.example.com
|
||||
|
||||
@@ -29,14 +29,23 @@ Roughly the boxes are as follows:
|
||||
This stage takes longer, due to complexity in the iterator module.
|
||||
Twice as long; one box for module layout, one box for iterator module.
|
||||
0.5 Validator - validator module.
|
||||
0.6 Bigger and better - good syslog, compressed packets, port ranges,
|
||||
config file format with config checker.
|
||||
0.6 Bigger and better - Operational useful features (config, log, memory)
|
||||
0.7 Local zones feature - localzones stubzones fwdzones, no leak rfc1918.
|
||||
views support; for selective recursive service.
|
||||
0.8 Library use - resolver validator lib (and test apps)
|
||||
0.9 Corner cases - be able to resolve in the wild. Run fuzzers.
|
||||
Run as many tests as we can think of.
|
||||
Go through logs and check for long, unresolved cases
|
||||
Use profiler.
|
||||
0.10 Beta release. Run shadow for a resolver in production for several
|
||||
weeks.
|
||||
0.11 Features features
|
||||
aggressive negative caching for NSEC, NSEC3.
|
||||
multiple queries per question, server exploration, server selection.
|
||||
option to use real entropy for randomness (mix it in once in a while).
|
||||
check query, option to enforce qdsection checking (forgery-resilience).
|
||||
NSID support.
|
||||
Be able to prime roots using several queries (only NS on first).
|
||||
|
||||
For boxes 0.5-1.0 the planning is to be revised, at the 0.5 stage external
|
||||
coders are welcome. Since the project is bigger, there is room for them.
|
||||
@@ -115,7 +124,7 @@ Styleguide:
|
||||
* Speed test of caching forwarder.
|
||||
|
||||
*** Basic Resolver
|
||||
* Create module interfacce and module caller algorithm.
|
||||
* Create module interface and module caller algorithm.
|
||||
* Daemon config to use modules. Test the module caller.
|
||||
* Create basic iterator and scrubber modules.
|
||||
* Test every state of the iterator by passing test data into
|
||||
@@ -134,19 +143,18 @@ Styleguide:
|
||||
* Speed test.
|
||||
|
||||
*** Bigger and Better
|
||||
* DNS packet compression of output.
|
||||
* test packet compression (that it compresses.)
|
||||
* config option to compress yes/no
|
||||
* speed test with and without compression.
|
||||
* Config file syntax checker program. Tests on checker.
|
||||
* Use a huge number of random outgoing ports.
|
||||
* Logging first class feature with config options.
|
||||
* donotqueryaddresses with trie for blocking entire netblocks.
|
||||
* Memory overhaul, special allocators for hashtable caches, and mesh qstates.
|
||||
* read root hints from file.
|
||||
|
||||
*** Local zones feature.
|
||||
* Build in local zone features. First the total stop for1912.
|
||||
* Then 'local content' for minimal serving of localhost.localdomain,
|
||||
and so on.
|
||||
* Remember jakob's diagram.
|
||||
* views support, selective recursive service
|
||||
* Forward-local-zone to NSD.
|
||||
- include in package, autoforkexec on localhost to do so.
|
||||
* forward local zone to remote server.
|
||||
@@ -169,6 +177,27 @@ Styleguide:
|
||||
* Try to setup corner cases of (mis)configured DNS service/websites.
|
||||
* Resolve msoft, google, yahoo, etc weird websites.
|
||||
* Try to resolve many many different queries, perhaps compared with bind.
|
||||
* create module testers, specific for the modules
|
||||
* read a file with cache contents and settings, provide fake
|
||||
environment for module-handle-state-X functions, then check
|
||||
resulting module state structure to correct answer.
|
||||
|
||||
*** Beta release.
|
||||
* Run shadow for a resolver in production for several weeks.
|
||||
* Check logs for errors, long queries.
|
||||
* Run in valgrind, speed profiling (as production shadow).
|
||||
|
||||
*** Features features
|
||||
* aggressive negative caching for NSEC, NSEC3.
|
||||
* multiple queries per question, server exploration, server selection.
|
||||
* option to use real entropy for randomness (mix it in once in a while).
|
||||
* check query, enforce qdsection, checking for forgery-resilience.
|
||||
* NSID support.
|
||||
* support TSIG on queries, for validating resolver deployment.
|
||||
* Be able to prime roots using several queries (like, get only NS first).
|
||||
* Nicer statistics
|
||||
* private TTL, dTLS features.
|
||||
|
||||
|
||||
treeshrew/
|
||||
validator/ *.c *.h
|
||||
|
||||
+8
-35
@@ -1,38 +1,11 @@
|
||||
.ig
|
||||
unbound.8 -- unbound manual
|
||||
|
||||
Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
|
||||
This software is open source.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
..
|
||||
.\"
|
||||
.\" unbound.8 -- unbound manual
|
||||
.\"
|
||||
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
.\"
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.Dd @date@
|
||||
.Dt unbound 8
|
||||
.Sh NAME
|
||||
|
||||
+120
-8
@@ -24,6 +24,31 @@ The notation is: attribute: value.
|
||||
Comments start with # and last to the end of line. Empty lines are
|
||||
ignored as is whitespace at the beginning of a line.
|
||||
|
||||
.El
|
||||
.Sh EXAMPLE
|
||||
An example config file is shown below. Copy this to /etc/unbound/unbound.conf
|
||||
and start the server with:
|
||||
.nf
|
||||
$ unbound -c /etc/unbound/unbound.conf
|
||||
.fi
|
||||
Most settings are the defaults. Stop the server with:
|
||||
.nf
|
||||
$ kill `cat /etc/unbound/unbound.pid`
|
||||
.fi
|
||||
Below is a minimal config file. The source distribution contains an extensive
|
||||
example.conf file with all the options.
|
||||
|
||||
.nf
|
||||
# unbound.conf(5) config file for unbound(8).
|
||||
server:
|
||||
directory: "/etc/unbound"
|
||||
username: unbound # make sure it can write to pidfile, logfile.
|
||||
chroot: "/etc/unbound"
|
||||
logfile: "/etc/unbound/unbound.log"
|
||||
pidfile: "/etc/unbound/unbound.pid"
|
||||
# verbosity: 1 # uncomment and increase to get more logging.
|
||||
.fi
|
||||
|
||||
.El
|
||||
.Sh FILE FORMAT
|
||||
There must be whitespace between keywords. Attribute keywords end with a colon ':'. An attribute
|
||||
@@ -79,6 +104,18 @@ Number of bytes size of the RRset cache. Default is 4 megabytes.
|
||||
.It \fBrrset-cache-slabs:\fR <number>
|
||||
Number of slabs in the RRset cache. Slabs reduce lock contention by threads.
|
||||
Must be set to a power of 2.
|
||||
.It \fBinfra-host-ttl:\fR <seconds>
|
||||
Time to live for entries in the host cache. The host cache contains
|
||||
roundtrip timing and EDNS support information. Default is 900.
|
||||
.It \fBinfra-lame-ttl:\fR <seconds>
|
||||
The time to live when a delegation is discovered to be lame. Default is 900.
|
||||
.It \fBinfra-cache-slabs:\fR <number>
|
||||
Number of slabs in the infrastructure cache. Slabs reduce lock contention
|
||||
by threads. Must be set to a power of 2.
|
||||
.It \fBinfra-cache-numhosts:\fR <number>
|
||||
Number of hosts for which information is cached. Default is 1000.
|
||||
.It \fBinfra-cache-numlame:\fR <number>
|
||||
Number of zones per host for which lameness is cached. Default is 1000.
|
||||
.It \fBdo-ip4:\fR <yes or no>
|
||||
Enable or disable whether ip4 queries are answered. Default is yes.
|
||||
.It \fBdo-ip6:\fR <yes or no>
|
||||
@@ -87,20 +124,16 @@ Enable or disable whether ip6 queries are answered. Default is yes.
|
||||
Enable or disable whether UDP queries are answered. Default is yes.
|
||||
.It \fBdo-tcp:\fR <yes or no>
|
||||
Enable or disable whether TCP queries are answered. Default is yes.
|
||||
.It \fBforward-to:\fR <ip address>
|
||||
If set (not "") then forwarder mode is enabled. Default is "" (disabled).
|
||||
The ip address is used to forward all DNS queries to.
|
||||
.It \fBforward-to-port:\fR <port number>
|
||||
The port on which the remote server is running that answers forwarded queries.
|
||||
Default is 53.
|
||||
.It \fBchroot:\fR <directory>
|
||||
If given a chroot is done to the given directory. The default is none ("").
|
||||
.It \fBusername:\fR <name>
|
||||
If given, after binding the port the user privileges are dropped. Default is
|
||||
not to change user, username: "". If this user is not capable of binding the
|
||||
not to change user, username: "".
|
||||
|
||||
If this user is not capable of binding the
|
||||
port, reloads (by signal HUP) will still retain the opened ports.
|
||||
If you change the port number in the config file, and that new port number
|
||||
requires privileges, then a restart is needed.
|
||||
requires privileges, then a reload will fail; a restart is needed.
|
||||
.It \fBdirectory:\fR <directory>
|
||||
Sets the working directory for the program.
|
||||
.It \fBlogfile:\fR <filename>
|
||||
@@ -111,11 +144,90 @@ The logfile is appended to, in the following format:
|
||||
The process id is written to the file. Default is "unbound.pid". So,
|
||||
kill -HUP `cat /etc/unbound/unbound.pid` will trigger a reload,
|
||||
kill -QUIT `cat /etc/unbound/unbound.pid` will gracefully terminate.
|
||||
.It \fBhide-identity:\fR <yes or no>
|
||||
If enabled id.server and hostname.bind queries are refused.
|
||||
.It \fBidentity:\fR <string>
|
||||
Set the identity to report. If set to "", the default, then the hostname
|
||||
of the server is returned.
|
||||
.It \fBhide-version:\fR <yes or no>
|
||||
If enabled version.server and version.bind queries are refused.
|
||||
.It \fBversion:\fR <string>
|
||||
Set the version to report. If set to "", the default, then the package
|
||||
version is returned.
|
||||
.It \fBtarget-fetch-policy:\fR <"list of numbers">
|
||||
Set the target fetch policy used by unbound to determine if it should fetch
|
||||
nameserver target addresses opportunistically. The policy is described per
|
||||
dependency depth.
|
||||
|
||||
The number of values determines the maximum dependency depth
|
||||
that unbound will pursue in answering a query.
|
||||
A value of -1 means to fetch all targets opportunistically for that dependency
|
||||
depth. A value of 0 means to fetch on demand only. A positive value fetches
|
||||
that many targets opportunistically.
|
||||
|
||||
Enclose the list between quotes ("") and put spaces between numbers.
|
||||
The default is "3 2 1 0 0". Setting all zeroes, "0 0 0 0 0" gives behaviour
|
||||
closer to that of BIND 9, while setting "-1 -1 -1 -1 -1" gives behaviour
|
||||
rumoured to be closer to that of BIND 8.
|
||||
.It \fBharden-short-bufsize:\fR <yes or no>
|
||||
Very small EDNS buffer sizes from queries are ignored. Default is off, since
|
||||
it is legal protocol wise to send these, and unbound tries to give very
|
||||
small answers to these queries, where possible.
|
||||
.It \fBharden-large-queries:\fR <yes or no>
|
||||
Very large queries are ignored. Default is off, since it is legal protocol
|
||||
wise to send these, and could be necessary for operation if TSIG or EDNS
|
||||
payload is very large.
|
||||
.It \fBharden-glue:\fR <yes or no>
|
||||
Will trust glue only if it is within the servers authority. Default is on.
|
||||
.It \fBdo-not-query-address:\fR <IP address>
|
||||
Do not query the given IP address. Can be IP4 or IP6. By default the
|
||||
DNS port is blocked for that address. Appending the character '@' and then
|
||||
the portnumber will block other port numbers.
|
||||
.El
|
||||
|
||||
.Ss Stub Zone Options
|
||||
There may be multiple
|
||||
.Ic stub-zone:
|
||||
clauses. Each with a name: and zero or more hostnames or IP addresses.
|
||||
For the stub zone this list of nameservers is used. Class IN is assumed.
|
||||
.Bl -tag -width indent
|
||||
.It \fBname:\fR <domain name>
|
||||
Name of the stub zone.
|
||||
.It \fBstub-host:\fR <domain name>
|
||||
Name of stub zone nameserver. Is itself resolved before it is used.
|
||||
.It \fBstub-addr:\fR <IP address>
|
||||
IP address of stub zone nameserver. Can be IP 4 or IP 6.
|
||||
To use a nondefault port for DNS communication append '@' with the port number.
|
||||
.El
|
||||
|
||||
.Ss Forward Zone Options
|
||||
There may be multiple
|
||||
.Ic forward-zone:
|
||||
clauses. Each with a name: and zero or more hostnames or IP addresses.
|
||||
For the forward zone this list of nameservers is used to forward the queries
|
||||
to. The servers have to handle further recursion for the query. Class IN is
|
||||
assumed. A forward-zone entry with name "." and a forward-addr target will
|
||||
forward all queries to that other server (unless it can answer from the cache).
|
||||
.Bl -tag -width indent
|
||||
.It \fBname:\fR <domain name>
|
||||
Name of the forward zone.
|
||||
.It \fBforward-host:\fR <domain name>
|
||||
Name of server to forward to. Is itself resolved before it is used.
|
||||
.It \fBforward-addr:\fR <IP address>
|
||||
IP address of server to forward to. Can be IP 4 or IP 6.
|
||||
To use a nondefault port for DNS communication append '@' with the port number.
|
||||
.El
|
||||
|
||||
.Sh FILES
|
||||
.Bl -tag -width indent
|
||||
.It Pa /etc/unbound
|
||||
default unbound working directory
|
||||
.It Pa unbound.conf
|
||||
unbound configuration file.
|
||||
.It Pa unbound.pid
|
||||
default unbound pidfile with process ID of the running daemon.
|
||||
.It Pa unbound.log
|
||||
unbound log file.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr unbound 8
|
||||
|
||||
+70
-45
@@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.4.6
|
||||
# Doxyfile 1.5.2
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project
|
||||
@@ -14,6 +14,14 @@
|
||||
# Project related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# This tag specifies the encoding used for all characters in the config file that
|
||||
# follow. The default is UTF-8 which is also the encoding used for all text before
|
||||
# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into
|
||||
# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of
|
||||
# possible encodings.
|
||||
|
||||
# DOXYFILE_ENCODING = UTF-8
|
||||
|
||||
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
|
||||
# by quotes) that should identify the project.
|
||||
|
||||
@@ -45,24 +53,14 @@ CREATE_SUBDIRS = NO
|
||||
# documentation generated by doxygen is written. Doxygen will use this
|
||||
# information to generate all constant output in the proper language.
|
||||
# The default language is English, other supported languages are:
|
||||
# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish,
|
||||
# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese,
|
||||
# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian,
|
||||
# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish,
|
||||
# Swedish, and Ukrainian.
|
||||
# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
|
||||
# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
|
||||
# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
|
||||
# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
|
||||
# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
|
||||
|
||||
OUTPUT_LANGUAGE = English
|
||||
|
||||
# This tag can be used to specify the encoding used in the generated output.
|
||||
# The encoding is not always determined by the language that is chosen,
|
||||
# but also whether or not the output is meant for Windows or non-Windows users.
|
||||
# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
|
||||
# forces the Windows encoding (this is the default for the Windows binary),
|
||||
# whereas setting the tag to NO uses a Unix-style encoding (the default for
|
||||
# all platforms other than Windows).
|
||||
|
||||
USE_WINDOWS_ENCODING = NO
|
||||
|
||||
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
|
||||
# include brief member descriptions after the members that are listed in
|
||||
# the file and class documentation (similar to JavaDoc).
|
||||
@@ -204,6 +202,11 @@ OPTIMIZE_OUTPUT_JAVA = NO
|
||||
|
||||
BUILTIN_STL_SUPPORT = NO
|
||||
|
||||
# If you use Microsoft's C++/CLI language, you should set this option to YES to
|
||||
# enable parsing support.
|
||||
|
||||
# CPP_CLI_SUPPORT = NO
|
||||
|
||||
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
|
||||
# tag is set to YES, then doxygen will reuse the documentation of the first
|
||||
# member in the group (if any) for the other members of the group. By default
|
||||
@@ -459,7 +462,14 @@ WARN_LOGFILE =
|
||||
# directories like "/usr/src/myproject". Separate the files or directories
|
||||
# with spaces.
|
||||
|
||||
INPUT = .
|
||||
INPUT = .
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files that
|
||||
# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default
|
||||
# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding.
|
||||
# See http://www.gnu.org/software/libiconv for the list of possible encodings.
|
||||
|
||||
# INPUT_ENCODING = UTF-8
|
||||
|
||||
# If the value of the INPUT tag contains directories, you can use the
|
||||
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
|
||||
@@ -468,7 +478,7 @@ INPUT = .
|
||||
# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
|
||||
# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
|
||||
|
||||
#FILE_PATTERNS = *.h *.c *.dox
|
||||
FILE_PATTERNS =
|
||||
|
||||
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
|
||||
# should be searched for input files as well. Possible values are YES and NO.
|
||||
@@ -480,7 +490,12 @@ RECURSIVE = YES
|
||||
# excluded from the INPUT source files. This way you can easily exclude a
|
||||
# subdirectory from a directory tree whose root is specified with the INPUT tag.
|
||||
|
||||
EXCLUDE = ./build ./compat util/configparser.c util/configparser.h util/configlexer.c util/locks.h
|
||||
EXCLUDE = ./build \
|
||||
./compat \
|
||||
util/configparser.c \
|
||||
util/configparser.h \
|
||||
util/configlexer.c \
|
||||
util/locks.h
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
|
||||
# directories that are symbolic links (a Unix filesystem feature) are excluded
|
||||
@@ -496,6 +511,13 @@ EXCLUDE_SYMLINKS = NO
|
||||
|
||||
EXCLUDE_PATTERNS =
|
||||
|
||||
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
|
||||
# (namespaces, classes, functions, etc.) that should be excluded from the output.
|
||||
# The symbol name can be a fully qualified name, a word, or if the wildcard * is used,
|
||||
# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
|
||||
|
||||
# EXCLUDE_SYMBOLS =
|
||||
|
||||
# The EXAMPLE_PATH tag can be used to specify one or more files or
|
||||
# directories that contain example code fragments that are included (see
|
||||
# the \include command).
|
||||
@@ -581,6 +603,13 @@ REFERENCED_BY_RELATION = YES
|
||||
|
||||
REFERENCES_RELATION = YES
|
||||
|
||||
# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
|
||||
# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
|
||||
# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
|
||||
# link to the source code. Otherwise they will link to the documentstion.
|
||||
|
||||
# REFERENCES_LINK_SOURCE = YES
|
||||
|
||||
# If the USE_HTAGS tag is set to YES then the references to source code
|
||||
# will point to the HTML generated by the htags(1) tool instead of doxygen
|
||||
# built-in source browser. The htags tool is part of GNU's global source
|
||||
@@ -1078,6 +1107,14 @@ PERL_PATH = /usr/bin/perl
|
||||
|
||||
CLASS_DIAGRAMS = YES
|
||||
|
||||
# You can define message sequence charts within doxygen comments using the \msc
|
||||
# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to
|
||||
# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to
|
||||
# specify the directory where the mscgen tool resides. If left empty the tool is assumed to
|
||||
# be found in the default search path.
|
||||
|
||||
# MSCGEN_PATH =
|
||||
|
||||
# If set to YES, the inheritance and collaboration graphs will hide
|
||||
# inheritance and usage relations if the target is undocumented
|
||||
# or is not a class.
|
||||
@@ -1143,6 +1180,14 @@ INCLUDED_BY_GRAPH = YES
|
||||
|
||||
CALL_GRAPH = NO
|
||||
|
||||
# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will
|
||||
# generate a caller dependency graph for every global function or class method.
|
||||
# Note that enabling this option will significantly increase the time of a run.
|
||||
# So in most cases it will be better to enable caller graphs for selected
|
||||
# functions only using the \callergraph command.
|
||||
|
||||
# CALLER_GRAPH = NO
|
||||
|
||||
# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
|
||||
# will graphical hierarchy of all classes instead of a textual one.
|
||||
|
||||
@@ -1172,33 +1217,13 @@ DOT_PATH =
|
||||
|
||||
DOTFILE_DIRS =
|
||||
|
||||
# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
|
||||
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
|
||||
# this value, doxygen will try to truncate the graph, so that it fits within
|
||||
# the specified constraint. Beware that most browsers cannot cope with very
|
||||
# large images.
|
||||
# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
|
||||
# nodes that will be shown in the graph. If the number of nodes in a graph
|
||||
# becomes larger than this value, doxygen will truncate the graph, which is
|
||||
# visualized by representing a node as a red box. Note that doxygen will always
|
||||
# show the root nodes and its direct children regardless of this setting.
|
||||
|
||||
MAX_DOT_GRAPH_WIDTH = 1024
|
||||
|
||||
# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
|
||||
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
|
||||
# this value, doxygen will try to truncate the graph, so that it fits within
|
||||
# the specified constraint. Beware that most browsers cannot cope with very
|
||||
# large images.
|
||||
|
||||
MAX_DOT_GRAPH_HEIGHT = 1024
|
||||
|
||||
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
|
||||
# graphs generated by dot. A depth value of 3 means that only nodes reachable
|
||||
# from the root by following a path via at most 3 edges will be shown. Nodes
|
||||
# that lay further from the root node will be omitted. Note that setting this
|
||||
# option to 1 or 2 may greatly reduce the computation time needed for large
|
||||
# code bases. Also note that a graph may be further truncated if the graph's
|
||||
# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH
|
||||
# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default),
|
||||
# the graph is not depth-constrained.
|
||||
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
# DOT_GRAPH_MAX_NODES = 50
|
||||
|
||||
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
|
||||
# background. This is disabled by default, which results in a white background.
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
* iterator/iter_delegpt.c - delegation point with NS and address information.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file implements the Delegation Point. It contains a list of name servers
|
||||
* and their addresses if known.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "util/region-allocator.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/net_help.h"
|
||||
|
||||
struct delegpt*
|
||||
delegpt_create(struct region* region)
|
||||
{
|
||||
struct delegpt* dp=(struct delegpt*)region_alloc(region, sizeof(*dp));
|
||||
if(!dp)
|
||||
return NULL;
|
||||
memset(dp, 0, sizeof(*dp));
|
||||
return dp;
|
||||
}
|
||||
|
||||
struct delegpt* delegpt_copy(struct delegpt* dp, struct region* region)
|
||||
{
|
||||
struct delegpt* copy = delegpt_create(region);
|
||||
struct delegpt_ns* ns;
|
||||
struct delegpt_addr* a;
|
||||
if(!copy)
|
||||
return NULL;
|
||||
if(!delegpt_set_name(copy, region, dp->name))
|
||||
return NULL;
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
if(!delegpt_add_ns(copy, region, ns->name))
|
||||
return NULL;
|
||||
copy->nslist->resolved = ns->resolved;
|
||||
}
|
||||
for(a = dp->target_list; a; a = a->next_target) {
|
||||
if(!delegpt_add_addr(copy, region, &a->addr, a->addrlen))
|
||||
return NULL;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_set_name(struct delegpt* dp, struct region* region, uint8_t* name)
|
||||
{
|
||||
dp->namelabs = dname_count_size_labels(name, &dp->namelen);
|
||||
dp->name = region_alloc_init(region, name, dp->namelen);
|
||||
return dp->name != 0;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_add_ns(struct delegpt* dp, struct region* region, uint8_t* name)
|
||||
{
|
||||
struct delegpt_ns* ns = (struct delegpt_ns*)region_alloc(region,
|
||||
sizeof(struct delegpt_ns));
|
||||
if(!ns)
|
||||
return 0;
|
||||
ns->next = dp->nslist;
|
||||
dp->nslist = ns;
|
||||
(void)dname_count_size_labels(name, &ns->namelen);
|
||||
ns->name = region_alloc_init(region, name, ns->namelen);
|
||||
ns->resolved = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct delegpt_ns*
|
||||
delegpt_find_ns(struct delegpt* dp, uint8_t* name, size_t namelen)
|
||||
{
|
||||
struct delegpt_ns* p = dp->nslist;
|
||||
while(p) {
|
||||
if(namelen == p->namelen &&
|
||||
query_dname_compare(name, p->name) == 0) {
|
||||
return p;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_add_target(struct delegpt* dp, struct region* region,
|
||||
uint8_t* name, size_t namelen, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
struct delegpt_ns* ns = delegpt_find_ns(dp, name, namelen);
|
||||
if(!ns) {
|
||||
/* ignore it */
|
||||
return 1;
|
||||
}
|
||||
ns->resolved = 1;
|
||||
return delegpt_add_addr(dp, region, addr, addrlen);
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_add_addr(struct delegpt* dp, struct region* region,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen)
|
||||
{
|
||||
struct delegpt_addr* a = (struct delegpt_addr*)region_alloc(region,
|
||||
sizeof(struct delegpt_addr));
|
||||
if(!a)
|
||||
return 0;
|
||||
a->next_target = dp->target_list;
|
||||
dp->target_list = a;
|
||||
a->next_result = 0;
|
||||
a->next_usable = dp->usable_list;
|
||||
dp->usable_list = a;
|
||||
memcpy(&a->addr, addr, addrlen);
|
||||
a->addrlen = addrlen;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** count NS and number missing */
|
||||
static void
|
||||
delegpt_count_ns(struct delegpt* dp, size_t* numns, size_t* missing)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
*numns = 0;
|
||||
*missing = 0;
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
(*numns)++;
|
||||
if(!ns->resolved)
|
||||
(*missing)++;
|
||||
}
|
||||
}
|
||||
|
||||
/** count addresses, and number in result and available lists */
|
||||
static void
|
||||
delegpt_count_addr(struct delegpt* dp, size_t* numaddr, size_t* numres,
|
||||
size_t* numavail)
|
||||
{
|
||||
struct delegpt_addr* a;
|
||||
*numaddr = 0;
|
||||
*numres = 0;
|
||||
*numavail = 0;
|
||||
for(a = dp->target_list; a; a = a->next_target) {
|
||||
(*numaddr)++;
|
||||
}
|
||||
for(a = dp->result_list; a; a = a->next_result) {
|
||||
(*numres)++;
|
||||
}
|
||||
for(a = dp->usable_list; a; a = a->next_usable) {
|
||||
(*numavail)++;
|
||||
}
|
||||
}
|
||||
|
||||
void delegpt_log(struct delegpt* dp)
|
||||
{
|
||||
char buf[LDNS_MAX_DOMAINLEN+1];
|
||||
struct delegpt_ns* ns;
|
||||
struct delegpt_addr* a;
|
||||
size_t missing=0, numns=0, numaddr=0, numres=0, numavail=0;
|
||||
dname_str(dp->name, buf);
|
||||
if(dp->nslist == NULL && dp->target_list == NULL) {
|
||||
log_info("DelegationPoint<%s>: empty", buf);
|
||||
return;
|
||||
}
|
||||
delegpt_count_ns(dp, &numns, &missing);
|
||||
delegpt_count_addr(dp, &numaddr, &numres, &numavail);
|
||||
log_info("DelegationPoint<%s>: %u names (%u missing), "
|
||||
"%u addrs (%u result, %u avail)",
|
||||
buf, (unsigned)numns, (unsigned)missing,
|
||||
(unsigned)numaddr, (unsigned)numres, (unsigned)numavail);
|
||||
if(verbosity >= VERB_ALGO) {
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
dname_str(ns->name, buf);
|
||||
log_info(" %s%s", buf, (ns->resolved?"*":""));
|
||||
}
|
||||
for(a = dp->target_list; a; a = a->next_target) {
|
||||
log_addr(" ", &a->addr, a->addrlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
delegpt_add_unused_targets(struct delegpt* dp)
|
||||
{
|
||||
struct delegpt_addr* usa = dp->usable_list;
|
||||
dp->usable_list = NULL;
|
||||
while(usa) {
|
||||
usa->next_result = dp->result_list;
|
||||
dp->result_list = usa;
|
||||
usa = usa->next_usable;
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
delegpt_count_missing_targets(struct delegpt* dp)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
size_t n = 0;
|
||||
for(ns = dp->nslist; ns; ns = ns->next)
|
||||
if(!ns->resolved)
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
/** find NS rrset in given list */
|
||||
static struct ub_packed_rrset_key*
|
||||
find_NS(struct reply_info* rep, size_t from, size_t to)
|
||||
{
|
||||
size_t i;
|
||||
for(i=from; i<to; i++) {
|
||||
if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
|
||||
return rep->rrsets[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct delegpt*
|
||||
delegpt_from_message(struct dns_msg* msg, struct region* region)
|
||||
{
|
||||
struct ub_packed_rrset_key* ns_rrset = NULL;
|
||||
struct delegpt* dp;
|
||||
size_t i;
|
||||
/* look for NS records in the authority section... */
|
||||
ns_rrset = find_NS(msg->rep, msg->rep->an_numrrsets,
|
||||
msg->rep->an_numrrsets+msg->rep->ns_numrrsets);
|
||||
|
||||
/* In some cases (even legitimate, perfectly legal cases), the
|
||||
* NS set for the "referral" might be in the answer section. */
|
||||
if(!ns_rrset)
|
||||
ns_rrset = find_NS(msg->rep, 0, msg->rep->an_numrrsets);
|
||||
|
||||
/* If there was no NS rrset in the authority section, then this
|
||||
* wasn't a referral message. (It might not actually be a
|
||||
* referral message anyway) */
|
||||
if(!ns_rrset)
|
||||
return NULL;
|
||||
|
||||
/* If we found any, then Yay! we have a delegation point. */
|
||||
dp = delegpt_create(region);
|
||||
if(!dp)
|
||||
return NULL;
|
||||
if(!delegpt_set_name(dp, region, ns_rrset->rk.dname))
|
||||
return NULL;
|
||||
if(!delegpt_rrset_add_ns(dp, region, ns_rrset))
|
||||
return NULL;
|
||||
|
||||
/* add glue, A and AAAA in answer and additional section */
|
||||
for(i=0; i<msg->rep->rrset_count; i++) {
|
||||
struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
|
||||
/* skip auth section. FIXME really needed?*/
|
||||
if(msg->rep->an_numrrsets <= i &&
|
||||
i < (msg->rep->an_numrrsets+msg->rep->ns_numrrsets))
|
||||
continue;
|
||||
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_A) {
|
||||
if(!delegpt_add_rrset_A(dp, region, s))
|
||||
return NULL;
|
||||
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_AAAA) {
|
||||
if(!delegpt_add_rrset_AAAA(dp, region, s))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_rrset_add_ns(struct delegpt* dp, struct region* region,
|
||||
struct ub_packed_rrset_key* ns_rrset)
|
||||
{
|
||||
struct packed_rrset_data* nsdata = (struct packed_rrset_data*)
|
||||
ns_rrset->entry.data;
|
||||
size_t i;
|
||||
for(i=0; i<nsdata->count; i++) {
|
||||
if(nsdata->rr_len[i] < 2+1) continue; /* len + root label */
|
||||
if(dname_valid(nsdata->rr_data[i]+2, nsdata->rr_len[i]-2) !=
|
||||
(size_t)ldns_read_uint16(nsdata->rr_data[i]))
|
||||
continue; /* bad format */
|
||||
/* add rdata of NS (= wirefmt dname), skip rdatalen bytes */
|
||||
if(!delegpt_add_ns(dp, region, nsdata->rr_data[i]+2))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_add_rrset_A(struct delegpt* dp, struct region* region,
|
||||
struct ub_packed_rrset_key* ak)
|
||||
{
|
||||
struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
|
||||
size_t i;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t len = (socklen_t)sizeof(sa);
|
||||
memset(&sa, 0, len);
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
|
||||
for(i=0; i<d->count; i++) {
|
||||
if(d->rr_len[i] != 2 + INET_SIZE)
|
||||
continue;
|
||||
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))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_add_rrset_AAAA(struct delegpt* dp, struct region* region,
|
||||
struct ub_packed_rrset_key* ak)
|
||||
{
|
||||
struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
|
||||
size_t i;
|
||||
struct sockaddr_in6 sa;
|
||||
socklen_t len = (socklen_t)sizeof(sa);
|
||||
memset(&sa, 0, len);
|
||||
sa.sin6_family = AF_INET6;
|
||||
sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
|
||||
for(i=0; i<d->count; i++) {
|
||||
if(d->rr_len[i] != 2 + INET6_SIZE) /* rdatalen + len of IP6 */
|
||||
continue;
|
||||
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))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_add_rrset(struct delegpt* dp, struct region* region,
|
||||
struct ub_packed_rrset_key* rrset)
|
||||
{
|
||||
if(!rrset)
|
||||
return 1;
|
||||
if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_NS)
|
||||
return delegpt_rrset_add_ns(dp, region, rrset);
|
||||
else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_A)
|
||||
return delegpt_add_rrset_A(dp, region, rrset);
|
||||
else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA)
|
||||
return delegpt_add_rrset_AAAA(dp, region, rrset);
|
||||
log_warn("Unknown rrset type added to delegpt");
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* iterator/iter_delegpt.h - delegation point with NS and address information.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file implements the Delegation Point. It contains a list of name servers
|
||||
* and their addresses if known.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_DELEGPT_H
|
||||
#define ITERATOR_ITER_DELEGPT_H
|
||||
struct region;
|
||||
struct delegpt_ns;
|
||||
struct delegpt_addr;
|
||||
struct dns_msg;
|
||||
struct ub_packed_rrset_key;
|
||||
|
||||
/**
|
||||
* Delegation Point.
|
||||
* For a domain name, the NS rrset, and the A and AAAA records for those.
|
||||
*/
|
||||
struct delegpt {
|
||||
/** the domain name of the delegation point. */
|
||||
uint8_t* name;
|
||||
/** length of the delegation point name */
|
||||
size_t namelen;
|
||||
/** number of labels in delegation point */
|
||||
int namelabs;
|
||||
|
||||
/** the nameservers, names from the NS RRset rdata. */
|
||||
struct delegpt_ns* nslist;
|
||||
/** the target addresses for delegation */
|
||||
struct delegpt_addr* target_list;
|
||||
/** the list of usable targets; subset of target_list
|
||||
* the items in this list are not part of the result list. */
|
||||
struct delegpt_addr* usable_list;
|
||||
/** the list of returned targets; subset of target_list */
|
||||
struct delegpt_addr* result_list;
|
||||
};
|
||||
|
||||
/**
|
||||
* Nameservers for a delegation point.
|
||||
*/
|
||||
struct delegpt_ns {
|
||||
/** next in list */
|
||||
struct delegpt_ns* next;
|
||||
/** name of nameserver */
|
||||
uint8_t* name;
|
||||
/** length of name */
|
||||
size_t namelen;
|
||||
/**
|
||||
* If the name has been resolved. false if not queried for yet.
|
||||
* true if the address is known, or marked true if failed.
|
||||
*/
|
||||
int resolved;
|
||||
};
|
||||
|
||||
/**
|
||||
* Address of target nameserver in delegation point.
|
||||
*/
|
||||
struct delegpt_addr {
|
||||
/** next delegation point in results */
|
||||
struct delegpt_addr* next_result;
|
||||
/** next delegation point in usable list */
|
||||
struct delegpt_addr* next_usable;
|
||||
/** next delegation point in all targets list */
|
||||
struct delegpt_addr* next_target;
|
||||
|
||||
/** delegation point address */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr */
|
||||
socklen_t addrlen;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create new delegation point.
|
||||
* @param region: where to allocate it.
|
||||
* @return new delegation point or NULL on error.
|
||||
*/
|
||||
struct delegpt* delegpt_create(struct region* region);
|
||||
|
||||
/**
|
||||
* Create a copy of a delegation point.
|
||||
* @param dp: delegation point to copy.
|
||||
* @param region: where to allocate it.
|
||||
* @return new delegation point or NULL on error.
|
||||
*/
|
||||
struct delegpt* delegpt_copy(struct delegpt* dp, struct region* region);
|
||||
|
||||
/**
|
||||
* Set name of delegation point.
|
||||
* @param dp: delegation point.
|
||||
* @param region: where to allocate the name copy.
|
||||
* @param name: name to use.
|
||||
* @return false on error.
|
||||
*/
|
||||
int delegpt_set_name(struct delegpt* dp, struct region* region, uint8_t* name);
|
||||
|
||||
/**
|
||||
* Add a name to the delegation point.
|
||||
* @param dp: delegation point.
|
||||
* @param region: where to allocate the info.
|
||||
* @param name: domain name in wire format.
|
||||
* @return false on error.
|
||||
*/
|
||||
int delegpt_add_ns(struct delegpt* dp, struct region* region, uint8_t* name);
|
||||
|
||||
/**
|
||||
* Add NS rrset; calls add_ns repeatedly.
|
||||
* @param dp: delegation point.
|
||||
* @param region: where to allocate the info.
|
||||
* @param ns_rrset: NS rrset.
|
||||
* return 0 on alloc error.
|
||||
*/
|
||||
int delegpt_rrset_add_ns(struct delegpt* dp, struct region* region,
|
||||
struct ub_packed_rrset_key* ns_rrset);
|
||||
|
||||
/**
|
||||
* Add target address to the delegation point.
|
||||
* @param dp: delegation point.
|
||||
* @param region: where to allocate the info.
|
||||
* @param name: name for which target was found (must be in nslist).
|
||||
* This name is marked resolved.
|
||||
* @param namelen: length of name.
|
||||
* @param addr: the address.
|
||||
* @param addrlen: the length of addr.
|
||||
* @return false on error.
|
||||
*/
|
||||
int delegpt_add_target(struct delegpt* dp, struct region* region,
|
||||
uint8_t* name, size_t namelen, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen);
|
||||
|
||||
/**
|
||||
* Add A RRset to delegpt.
|
||||
* @param dp: delegation point.
|
||||
* @param region: where to allocate the info.
|
||||
* @param rrset: RRset A to add.
|
||||
* @return 0 on alloc error.
|
||||
*/
|
||||
int delegpt_add_rrset_A(struct delegpt* dp, struct region* region,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
|
||||
/**
|
||||
* Add AAAA RRset to delegpt.
|
||||
* @param dp: delegation point.
|
||||
* @param region: where to allocate the info.
|
||||
* @param rrset: RRset AAAA to add.
|
||||
* @return 0 on alloc error.
|
||||
*/
|
||||
int delegpt_add_rrset_AAAA(struct delegpt* dp, struct region* region,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
|
||||
/**
|
||||
* Add any RRset to delegpt.
|
||||
* @param dp: delegation point.
|
||||
* @param region: where to allocate the info.
|
||||
* @param rrset: RRset to add, NS, A, AAAA.
|
||||
* @return 0 on alloc error.
|
||||
*/
|
||||
int delegpt_add_rrset(struct delegpt* dp, struct region* region,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
|
||||
/**
|
||||
* Add address to the delegation point. No servername is associated or checked.
|
||||
* @param dp: delegation point.
|
||||
* @param region: where to allocate the info.
|
||||
* @param addr: the address.
|
||||
* @param addrlen: the length of addr.
|
||||
* @return false on error.
|
||||
*/
|
||||
int delegpt_add_addr(struct delegpt* dp, struct region* region,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen);
|
||||
|
||||
/**
|
||||
* Find NS record in name list of delegation point.
|
||||
* @param dp: delegation point.
|
||||
* @param name: name of nameserver to look for, uncompressed wireformat.
|
||||
* @param namelen: length of name.
|
||||
* @return the ns structure or NULL if not found.
|
||||
*/
|
||||
struct delegpt_ns* delegpt_find_ns(struct delegpt* dp, uint8_t* name,
|
||||
size_t namelen);
|
||||
|
||||
/**
|
||||
* Print the delegation point to the log. For debugging.
|
||||
* @param dp: delegation point.
|
||||
*/
|
||||
void delegpt_log(struct delegpt* dp);
|
||||
|
||||
/**
|
||||
* Add all usable targets to the result list.
|
||||
* @param dp: delegation point.
|
||||
*/
|
||||
void delegpt_add_unused_targets(struct delegpt* dp);
|
||||
|
||||
/**
|
||||
* Count number of missing targets. These are ns names with no resolved flag.
|
||||
* @param dp: delegation point.
|
||||
* @return number of missing targets (or 0).
|
||||
*/
|
||||
size_t delegpt_count_missing_targets(struct delegpt* dp);
|
||||
|
||||
/**
|
||||
* Create new delegation point from a dns message
|
||||
*
|
||||
* Note that this method does not actually test to see if the message is an
|
||||
* actual referral. It really is just checking to see if it can construct a
|
||||
* delegation point, so the message could be of some other type (some ANSWER
|
||||
* messages, some CNAME messages, generally.) Note that the resulting
|
||||
* DelegationPoint will contain targets for all "relevant" glue (i.e.,
|
||||
* address records whose ownernames match the target of one of the NS
|
||||
* records), so if policy dictates that some glue should be discarded beyond
|
||||
* that, discard it before calling this method. Note that this method will
|
||||
* find "glue" in either the ADDITIONAL section or the ANSWER section.
|
||||
*
|
||||
* @param msg: the dns message, referral.
|
||||
* @param region: where to allocate delegation point.
|
||||
* @return new delegation point or NULL on alloc error, or if the
|
||||
* message was not appropriate.
|
||||
*/
|
||||
struct delegpt* delegpt_from_message(struct dns_msg* msg,
|
||||
struct region* region);
|
||||
|
||||
#endif /* ITERATOR_ITER_DELEGPT_H */
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* iterator/iter_donotq.c - iterative resolver donotqueryaddresses storage.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to assist the iterator module.
|
||||
* The donotqueryaddresses are stored and looked up. These addresses
|
||||
* (like 127.0.0.1) must not be used to send queries to, and can be
|
||||
* discarded immediately from the server selection.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_donotq.h"
|
||||
#include "util/region-allocator.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
|
||||
/** compare two donotq entries */
|
||||
static int
|
||||
donotq_cmp(const void* k1, const void* k2)
|
||||
{
|
||||
struct iter_donotq_addr* n1 = (struct iter_donotq_addr*)k1;
|
||||
struct iter_donotq_addr* n2 = (struct iter_donotq_addr*)k2;
|
||||
return sockaddr_cmp(&n1->addr, n1->addrlen, &n2->addr, n2->addrlen);
|
||||
}
|
||||
|
||||
struct iter_donotq*
|
||||
donotq_create()
|
||||
{
|
||||
struct iter_donotq* dq = (struct iter_donotq*)calloc(1,
|
||||
sizeof(struct iter_donotq));
|
||||
if(!dq)
|
||||
return NULL;
|
||||
dq->region = region_create(malloc, free);
|
||||
if(!dq->region) {
|
||||
donotq_delete(dq);
|
||||
return NULL;
|
||||
}
|
||||
return dq;
|
||||
}
|
||||
|
||||
void
|
||||
donotq_delete(struct iter_donotq* dq)
|
||||
{
|
||||
if(!dq)
|
||||
return;
|
||||
region_destroy(dq->region);
|
||||
free(dq->tree);
|
||||
free(dq);
|
||||
}
|
||||
|
||||
/** insert new address into donotq structure */
|
||||
static int
|
||||
donotq_insert(struct iter_donotq* dq, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
struct iter_donotq_addr* node = region_alloc(dq->region,
|
||||
sizeof(struct iter_donotq_addr));
|
||||
if(!node)
|
||||
return 0;
|
||||
node->node.key = node;
|
||||
memcpy(&node->addr, addr, addrlen);
|
||||
node->addrlen = addrlen;
|
||||
if(!rbtree_insert(dq->tree, &node->node)) {
|
||||
log_warn("duplicate donotquery address ignored.");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** read donotq config */
|
||||
static int
|
||||
read_donotq(struct iter_donotq* dq, struct config_file* cfg)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
for(p = cfg->donotqueryaddrs; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
if(!extstrtoaddr(p->str, &addr, &addrlen)) {
|
||||
log_err("cannot parse donotquery ip address: '%s'",
|
||||
p->str);
|
||||
return 0;
|
||||
}
|
||||
if(!donotq_insert(dq, &addr, addrlen)) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
donotq_apply_cfg(struct iter_donotq* dq, struct config_file* cfg)
|
||||
{
|
||||
free(dq->tree);
|
||||
dq->tree = rbtree_create(donotq_cmp);
|
||||
if(!dq->tree)
|
||||
return 0;
|
||||
if(!read_donotq(dq, cfg))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
donotq_lookup(struct iter_donotq* donotq, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
/* lookup in the tree */
|
||||
struct iter_donotq_addr key;
|
||||
key.node.key = &key;
|
||||
memcpy(&key.addr, addr, addrlen);
|
||||
key.addrlen = addrlen;
|
||||
if(rbtree_search(donotq->tree, &key))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* iterator/iter_donotq.h - iterative resolver donotqueryaddresses storage.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to assist the iterator module.
|
||||
* Keep track of the donotquery addresses and lookup fast.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_DONOTQ_H
|
||||
#define ITERATOR_ITER_DONOTQ_H
|
||||
#include "util/rbtree.h"
|
||||
struct iter_env;
|
||||
struct config_file;
|
||||
struct region;
|
||||
|
||||
/**
|
||||
* Iterator donotqueryaddresses structure
|
||||
*/
|
||||
struct iter_donotq {
|
||||
/** region for allocation */
|
||||
struct region* region;
|
||||
/**
|
||||
* Tree of the single addresses that are blocked.
|
||||
* contents of type iter_donotq_addr.
|
||||
*/
|
||||
rbtree_t* tree;
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterator donotquery address.
|
||||
* A single address that must not be used to send queries to.
|
||||
*/
|
||||
struct iter_donotq_addr {
|
||||
/** redblacktree node, key is this structure: addr and addrlen */
|
||||
rbnode_t node;
|
||||
/** address */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr */
|
||||
socklen_t addrlen;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create donotqueryaddresses structure
|
||||
* @return new structure or NULL on error.
|
||||
*/
|
||||
struct iter_donotq* donotq_create();
|
||||
|
||||
/**
|
||||
* Delete donotqueryaddresses structure.
|
||||
* @param donotq: to delete.
|
||||
*/
|
||||
void donotq_delete(struct iter_donotq* donotq);
|
||||
|
||||
/**
|
||||
* Process donotqueryaddresses config.
|
||||
* @param donotq: where to store.
|
||||
* @param cfg: config options.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int donotq_apply_cfg(struct iter_donotq* donotq, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* See if an address is blocked.
|
||||
* @param donotq: structure for address storage.
|
||||
* @param addr: address to check
|
||||
* @param addrlen: length of addr.
|
||||
* @return: true if the address must not be queried. false if unlisted.
|
||||
*/
|
||||
int donotq_lookup(struct iter_donotq* donotq, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen);
|
||||
|
||||
#endif /* ITERATOR_ITER_DONOTQ_H */
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* iterator/iter_fwd.c - iterative resolver module forward zones.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to assist the iterator module.
|
||||
* Keep track of forward zones and config settings.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_fwd.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "util/region-allocator.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/dname.h"
|
||||
|
||||
/** compare two fwd entries */
|
||||
static int
|
||||
fwd_cmp(const void* k1, const void* k2)
|
||||
{
|
||||
int m;
|
||||
struct iter_forward_zone* n1 = (struct iter_forward_zone*)k1;
|
||||
struct iter_forward_zone* n2 = (struct iter_forward_zone*)k2;
|
||||
if(n1->dclass != n2->dclass) {
|
||||
if(n1->dclass < n2->dclass)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
return dname_lab_cmp(n1->name, n1->namelabs, n2->name, n2->namelabs,
|
||||
&m);
|
||||
}
|
||||
|
||||
struct iter_forwards*
|
||||
forwards_create()
|
||||
{
|
||||
struct iter_forwards* fwd = (struct iter_forwards*)calloc(1,
|
||||
sizeof(struct iter_forwards));
|
||||
if(!fwd)
|
||||
return NULL;
|
||||
fwd->region = region_create(malloc, free);
|
||||
if(!fwd->region) {
|
||||
forwards_delete(fwd);
|
||||
return NULL;
|
||||
}
|
||||
return fwd;
|
||||
}
|
||||
|
||||
void
|
||||
forwards_delete(struct iter_forwards* fwd)
|
||||
{
|
||||
if(!fwd)
|
||||
return;
|
||||
region_destroy(fwd->region);
|
||||
free(fwd->tree);
|
||||
free(fwd);
|
||||
}
|
||||
|
||||
/** insert new info into forward structure */
|
||||
static int
|
||||
forwards_insert(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp)
|
||||
{
|
||||
struct iter_forward_zone* node = region_alloc(fwd->region,
|
||||
sizeof(struct iter_forward_zone));
|
||||
if(!node)
|
||||
return 0;
|
||||
node->node.key = node;
|
||||
node->dclass = c;
|
||||
node->name = region_alloc_init(fwd->region, dp->name, dp->namelen);
|
||||
if(!node->name)
|
||||
return 0;
|
||||
node->namelen = dp->namelen;
|
||||
node->namelabs = dp->namelabs;
|
||||
node->dp = dp;
|
||||
if(!rbtree_insert(fwd->tree, &node->node)) {
|
||||
log_err("duplicate forward zone ignored.");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** initialise parent pointers in the tree */
|
||||
static void
|
||||
fwd_init_parents(struct iter_forwards* fwd)
|
||||
{
|
||||
struct iter_forward_zone* node, *prev = NULL, *p;
|
||||
int m;
|
||||
RBTREE_FOR(node, struct iter_forward_zone*, fwd->tree) {
|
||||
node->parent = NULL;
|
||||
if(!prev || prev->dclass != node->dclass) {
|
||||
prev = node;
|
||||
continue;
|
||||
}
|
||||
(void)dname_lab_cmp(prev->name, prev->namelabs, node->name,
|
||||
node->namelabs, &m); /* we know prev is smaller */
|
||||
/* sort order like: . com. bla.com. zwb.com. net. */
|
||||
/* find the previous, or parent-parent-parent */
|
||||
for(p = prev; p; p = p->parent)
|
||||
/* looking for name with few labels, a parent */
|
||||
if(p->namelabs <= m) {
|
||||
/* ==: since prev matched m, this is closest*/
|
||||
/* <: prev matches more, but is not a parent,
|
||||
* this one is a (grand)parent */
|
||||
node->parent = p;
|
||||
break;
|
||||
}
|
||||
prev = node;
|
||||
}
|
||||
}
|
||||
|
||||
/** set zone name */
|
||||
static int
|
||||
read_fwds_name(struct iter_forwards* fwd, struct config_stub* s,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
ldns_rdf* rdf;
|
||||
if(!s->name) {
|
||||
log_err("forward zone without a name (use name \".\" to forward everything)");
|
||||
return 0;
|
||||
}
|
||||
rdf = ldns_dname_new_frm_str(s->name);
|
||||
if(!rdf) {
|
||||
log_err("cannot parse forward zone name %s", s->name);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_set_name(dp, fwd->region, ldns_rdf_data(rdf))) {
|
||||
ldns_rdf_deep_free(rdf);
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
ldns_rdf_deep_free(rdf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** set fwd host names */
|
||||
static int
|
||||
read_fwds_host(struct iter_forwards* fwd, struct config_stub* s,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
ldns_rdf* rdf;
|
||||
for(p = s->hosts; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
rdf = ldns_dname_new_frm_str(p->str);
|
||||
if(!rdf) {
|
||||
log_err("cannot parse forward %s server name: '%s'",
|
||||
s->name, p->str);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_add_ns(dp, fwd->region, ldns_rdf_data(rdf))) {
|
||||
ldns_rdf_deep_free(rdf);
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
ldns_rdf_deep_free(rdf);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** set fwd server addresses */
|
||||
static int
|
||||
read_fwds_addr(struct iter_forwards* fwd, struct config_stub* s,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
for(p = s->addrs; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
if(!extstrtoaddr(p->str, &addr, &addrlen)) {
|
||||
log_err("cannot parse forward %s ip address: '%s'",
|
||||
s->name, p->str);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_add_addr(dp, fwd->region, &addr, addrlen)) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** read forwards config */
|
||||
static int
|
||||
read_forwards(struct iter_forwards* fwd, struct config_file* cfg)
|
||||
{
|
||||
struct config_stub* s;
|
||||
for(s = cfg->forwards; s; s = s->next) {
|
||||
struct delegpt* dp = delegpt_create(fwd->region);
|
||||
if(!dp) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
if(!read_fwds_name(fwd, s, dp) ||
|
||||
!read_fwds_host(fwd, s, dp) ||
|
||||
!read_fwds_addr(fwd, s, dp))
|
||||
return 0;
|
||||
if(!forwards_insert(fwd, LDNS_RR_CLASS_IN, dp))
|
||||
return 0;
|
||||
log_info("Forward zone server list:");
|
||||
delegpt_log(dp);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg)
|
||||
{
|
||||
free(fwd->tree);
|
||||
fwd->tree = rbtree_create(fwd_cmp);
|
||||
if(!fwd->tree)
|
||||
return 0;
|
||||
|
||||
/* read forward zones */
|
||||
if(!read_forwards(fwd, cfg))
|
||||
return 0;
|
||||
fwd_init_parents(fwd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct delegpt*
|
||||
forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass)
|
||||
{
|
||||
/* lookup the forward zone in the tree */
|
||||
rbnode_t* res = NULL;
|
||||
struct iter_forward_zone *result;
|
||||
struct iter_forward_zone key;
|
||||
key.node.key = &key;
|
||||
key.dclass = qclass;
|
||||
key.name = qname;
|
||||
key.namelabs = dname_count_size_labels(qname, &key.namelen);
|
||||
if(rbtree_find_less_equal(fwd->tree, &key, &res)) {
|
||||
/* exact */
|
||||
result = (struct iter_forward_zone*)res;
|
||||
} else {
|
||||
/* smaller element (or no element) */
|
||||
int m;
|
||||
result = (struct iter_forward_zone*)res;
|
||||
if(!result || result->dclass != qclass)
|
||||
return NULL;
|
||||
/* count number of labels matched */
|
||||
(void)dname_lab_cmp(result->name, result->namelabs, key.name,
|
||||
key.namelabs, &m);
|
||||
while(result) { /* go up until qname is subdomain of stub */
|
||||
if(result->namelabs <= m)
|
||||
break;
|
||||
result = result->parent;
|
||||
}
|
||||
}
|
||||
if(result)
|
||||
return result->dp;
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* iterator/iter_fwd.h - iterative resolver module forward zones.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to assist the iterator module.
|
||||
* Keep track of forward zones, and read those from config.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_FWD_H
|
||||
#define ITERATOR_ITER_FWD_H
|
||||
#include "util/rbtree.h"
|
||||
struct iter_env;
|
||||
struct config_file;
|
||||
struct delegpt;
|
||||
struct region;
|
||||
|
||||
/**
|
||||
* Iterator forward zones structure
|
||||
*/
|
||||
struct iter_forwards {
|
||||
/** region where forward zone server addresses are allocated */
|
||||
struct region* region;
|
||||
/**
|
||||
* Zones are stored in this tree. Sort order is specially chosen.
|
||||
* first sorted on qtype. Then on dname in nsec-like order, so that
|
||||
* a lookup on class, name will return an exact match or the closest
|
||||
* match which gives the ancestor needed.
|
||||
* contents of type iter_forward_zone.
|
||||
*/
|
||||
rbtree_t* tree;
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterator forward servers for a particular zone.
|
||||
*/
|
||||
struct iter_forward_zone {
|
||||
/** redblacktree node, key is this structure: class and name */
|
||||
rbnode_t node;
|
||||
/** name */
|
||||
uint8_t* name;
|
||||
/** length of name */
|
||||
size_t namelen;
|
||||
/** number of labels in name */
|
||||
int namelabs;
|
||||
/** delegation point with forward server information for this zone. */
|
||||
struct delegpt* dp;
|
||||
/** pointer to parent in tree (or NULL if none) */
|
||||
struct iter_forward_zone* parent;
|
||||
/** class. host order. */
|
||||
uint16_t dclass;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create forwards
|
||||
* @return new forwards or NULL on error.
|
||||
*/
|
||||
struct iter_forwards* forwards_create();
|
||||
|
||||
/**
|
||||
* Delete forwards.
|
||||
* @param fwd: to delete.
|
||||
*/
|
||||
void forwards_delete(struct iter_forwards* fwd);
|
||||
|
||||
/**
|
||||
* Process forwards config.
|
||||
* @param fwd: where to store.
|
||||
* @param cfg: config options.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Find forward zone information
|
||||
* For this qname/qclass find forward zone information, returns delegation
|
||||
* point with server names and addresses, or NULL if no forwarding is needed.
|
||||
*
|
||||
* @param fwd: forward storage.
|
||||
* @param qname: The qname of the query.
|
||||
* @param qclass: The qclass of the query.
|
||||
* @return: A delegation point if the query has to be forwarded to that list,
|
||||
* otherwise null.
|
||||
*/
|
||||
struct delegpt* forwards_lookup(struct iter_forwards* fwd,
|
||||
uint8_t* qname, uint16_t qclass);
|
||||
|
||||
#endif /* ITERATOR_ITER_FWD_H */
|
||||
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* iterator/iter_hints.c - iterative resolver module stub and root hints.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to assist the iterator module.
|
||||
* Keep track of stub and root hints, and read those from config.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_hints.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "util/region-allocator.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/dname.h"
|
||||
|
||||
/** compare two hint entries */
|
||||
static int
|
||||
stub_cmp(const void* k1, const void* k2)
|
||||
{
|
||||
int m;
|
||||
struct iter_hints_stub* n1 = (struct iter_hints_stub*)k1;
|
||||
struct iter_hints_stub* n2 = (struct iter_hints_stub*)k2;
|
||||
if(n1->hint_class != n2->hint_class) {
|
||||
if(n1->hint_class < n2->hint_class)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
return dname_lab_cmp(n1->name, n1->namelabs, n2->name, n2->namelabs,
|
||||
&m);
|
||||
}
|
||||
|
||||
struct iter_hints*
|
||||
hints_create()
|
||||
{
|
||||
struct iter_hints* hints = (struct iter_hints*)calloc(1,
|
||||
sizeof(struct iter_hints));
|
||||
if(!hints)
|
||||
return NULL;
|
||||
hints->region = region_create(malloc, free);
|
||||
if(!hints->region) {
|
||||
hints_delete(hints);
|
||||
return NULL;
|
||||
}
|
||||
return hints;
|
||||
}
|
||||
|
||||
void
|
||||
hints_delete(struct iter_hints* hints)
|
||||
{
|
||||
if(!hints)
|
||||
return;
|
||||
region_destroy(hints->region);
|
||||
free(hints->tree);
|
||||
free(hints);
|
||||
}
|
||||
|
||||
/** add hint to delegation hints */
|
||||
static int
|
||||
ah(struct delegpt* dp, struct region* r, const char* sv, const char* ip)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
ldns_rdf* rdf = ldns_dname_new_frm_str(sv);
|
||||
if(!rdf) {
|
||||
log_err("could not parse %s", sv);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_add_ns(dp, r, ldns_rdf_data(rdf)) ||
|
||||
!extstrtoaddr(ip, &addr, &addrlen) ||
|
||||
!delegpt_add_target(dp, r, ldns_rdf_data(rdf), ldns_rdf_size(rdf),
|
||||
&addr, addrlen)) {
|
||||
ldns_rdf_deep_free(rdf);
|
||||
return 0;
|
||||
}
|
||||
ldns_rdf_deep_free(rdf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** obtain compiletime provided root hints */
|
||||
static struct delegpt*
|
||||
compile_time_root_prime(struct region* r)
|
||||
{
|
||||
/* from:
|
||||
; This file is made available by InterNIC
|
||||
; under anonymous FTP as
|
||||
; file /domain/named.cache
|
||||
; on server FTP.INTERNIC.NET
|
||||
; -OR- RS.INTERNIC.NET
|
||||
;
|
||||
; last update: Jan 29, 2004
|
||||
; related version of root zone: 2004012900
|
||||
*/
|
||||
struct delegpt* dp = delegpt_create(r);
|
||||
if(!dp)
|
||||
return NULL;
|
||||
if(!delegpt_set_name(dp, r, (uint8_t*)"\000"))
|
||||
return NULL;
|
||||
if(!ah(dp, r, "A.ROOT-SERVERS.NET.", "198.41.0.4")) return 0;
|
||||
if(!ah(dp, r, "B.ROOT-SERVERS.NET.", "192.228.79.201")) return 0;
|
||||
if(!ah(dp, r, "C.ROOT-SERVERS.NET.", "192.33.4.12")) return 0;
|
||||
if(!ah(dp, r, "D.ROOT-SERVERS.NET.", "128.8.10.90")) return 0;
|
||||
if(!ah(dp, r, "E.ROOT-SERVERS.NET.", "192.203.230.10")) return 0;
|
||||
if(!ah(dp, r, "F.ROOT-SERVERS.NET.", "192.5.5.241")) return 0;
|
||||
if(!ah(dp, r, "G.ROOT-SERVERS.NET.", "192.112.36.4")) return 0;
|
||||
if(!ah(dp, r, "H.ROOT-SERVERS.NET.", "128.63.2.53")) return 0;
|
||||
if(!ah(dp, r, "I.ROOT-SERVERS.NET.", "192.36.148.17")) return 0;
|
||||
if(!ah(dp, r, "J.ROOT-SERVERS.NET.", "192.58.128.30")) return 0;
|
||||
if(!ah(dp, r, "K.ROOT-SERVERS.NET.", "193.0.14.129")) return 0;
|
||||
if(!ah(dp, r, "L.ROOT-SERVERS.NET.", "198.32.64.12")) return 0;
|
||||
if(!ah(dp, r, "M.ROOT-SERVERS.NET.", "202.12.27.33")) return 0;
|
||||
return dp;
|
||||
}
|
||||
|
||||
/** insert new hint info into hint structure */
|
||||
static int
|
||||
hints_insert(struct iter_hints* hints, uint16_t c, struct delegpt* dp)
|
||||
{
|
||||
struct iter_hints_stub* node = region_alloc(hints->region,
|
||||
sizeof(struct iter_hints_stub));
|
||||
if(!node)
|
||||
return 0;
|
||||
node->node.key = node;
|
||||
node->hint_class = c;
|
||||
node->name = region_alloc_init(hints->region, dp->name, dp->namelen);
|
||||
if(!node->name)
|
||||
return 0;
|
||||
node->namelen = dp->namelen;
|
||||
node->namelabs = dp->namelabs;
|
||||
node->dp = dp;
|
||||
if(!rbtree_insert(hints->tree, &node->node)) {
|
||||
log_err("second hints ignored.");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** initialise parent pointers in the tree */
|
||||
static void
|
||||
init_parents(struct iter_hints* hints)
|
||||
{
|
||||
struct iter_hints_stub* node, *prev = NULL, *p;
|
||||
int m;
|
||||
RBTREE_FOR(node, struct iter_hints_stub*, hints->tree) {
|
||||
node->parent = NULL;
|
||||
if(!prev || prev->hint_class != node->hint_class) {
|
||||
prev = node;
|
||||
continue;
|
||||
}
|
||||
(void)dname_lab_cmp(prev->name, prev->namelabs, node->name,
|
||||
node->namelabs, &m); /* we know prev is smaller */
|
||||
/* sort order like: . com. bla.com. zwb.com. net. */
|
||||
/* find the previous, or parent-parent-parent */
|
||||
for(p = prev; p; p = p->parent)
|
||||
/* looking for name with few labels, a parent */
|
||||
if(p->namelabs <= m) {
|
||||
/* ==: since prev matched m, this is closest*/
|
||||
/* <: prev matches more, but is not a parent,
|
||||
* this one is a (grand)parent */
|
||||
node->parent = p;
|
||||
break;
|
||||
}
|
||||
prev = node;
|
||||
}
|
||||
}
|
||||
|
||||
/** set stub name */
|
||||
static int
|
||||
read_stubs_name(struct iter_hints* hints, struct config_stub* s,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
ldns_rdf* rdf;
|
||||
if(!s->name) {
|
||||
log_err("stub zone without a name");
|
||||
return 0;
|
||||
}
|
||||
rdf = ldns_dname_new_frm_str(s->name);
|
||||
if(!rdf) {
|
||||
log_err("cannot parse stub zone name %s", s->name);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_set_name(dp, hints->region, ldns_rdf_data(rdf))) {
|
||||
ldns_rdf_deep_free(rdf);
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
ldns_rdf_deep_free(rdf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** set stub host names */
|
||||
static int
|
||||
read_stubs_host(struct iter_hints* hints, struct config_stub* s,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
ldns_rdf* rdf;
|
||||
for(p = s->hosts; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
rdf = ldns_dname_new_frm_str(p->str);
|
||||
if(!rdf) {
|
||||
log_err("cannot parse stub %s nameserver name: '%s'",
|
||||
s->name, p->str);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_add_ns(dp, hints->region, ldns_rdf_data(rdf))) {
|
||||
ldns_rdf_deep_free(rdf);
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
ldns_rdf_deep_free(rdf);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** set stub server addresses */
|
||||
static int
|
||||
read_stubs_addr(struct iter_hints* hints, struct config_stub* s,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
for(p = s->addrs; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
if(!extstrtoaddr(p->str, &addr, &addrlen)) {
|
||||
log_err("cannot parse stub %s ip address: '%s'",
|
||||
s->name, p->str);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_add_addr(dp, hints->region, &addr, addrlen)) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** read stubs config */
|
||||
static int
|
||||
read_stubs(struct iter_hints* hints, struct config_file* cfg)
|
||||
{
|
||||
struct config_stub* s;
|
||||
for(s = cfg->stubs; s; s = s->next) {
|
||||
struct delegpt* dp = delegpt_create(hints->region);
|
||||
if(!dp) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
if(!read_stubs_name(hints, s, dp) ||
|
||||
!read_stubs_host(hints, s, dp) ||
|
||||
!read_stubs_addr(hints, s, dp))
|
||||
return 0;
|
||||
if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp))
|
||||
return 0;
|
||||
delegpt_log(dp);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg)
|
||||
{
|
||||
free(hints->tree);
|
||||
hints->tree = rbtree_create(stub_cmp);
|
||||
if(!hints->tree)
|
||||
return 0;
|
||||
/* TODO: read root hints from file named in cfg */
|
||||
|
||||
/* read stub hints */
|
||||
if(!read_stubs(hints, cfg))
|
||||
return 0;
|
||||
|
||||
/* use fallback compiletime root hints */
|
||||
if(!hints_lookup_root(hints, LDNS_RR_CLASS_IN)) {
|
||||
struct delegpt* dp = compile_time_root_prime(hints->region);
|
||||
verbose(VERB_ALGO, "no config, using builtin root hints.");
|
||||
if(!dp)
|
||||
return 0;
|
||||
if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp))
|
||||
return 0;
|
||||
delegpt_log(dp);
|
||||
}
|
||||
|
||||
init_parents(hints);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct delegpt*
|
||||
hints_lookup_root(struct iter_hints* hints, uint16_t qclass)
|
||||
{
|
||||
uint8_t rootlab = 0;
|
||||
struct iter_hints_stub key, *stub;
|
||||
key.node.key = &key;
|
||||
key.hint_class = qclass;
|
||||
key.name = &rootlab;
|
||||
key.namelen = 1;
|
||||
key.namelabs = 1;
|
||||
stub = (struct iter_hints_stub*)rbtree_search(hints->tree, &key);
|
||||
if(!stub)
|
||||
return NULL;
|
||||
return stub->dp;
|
||||
}
|
||||
|
||||
struct delegpt*
|
||||
hints_lookup_stub(struct iter_hints* hints, uint8_t* qname,
|
||||
uint16_t qclass, struct delegpt* cache_dp)
|
||||
{
|
||||
/* first lookup the stub */
|
||||
rbnode_t* res = NULL;
|
||||
struct iter_hints_stub *result;
|
||||
struct iter_hints_stub key;
|
||||
key.node.key = &key;
|
||||
key.hint_class = qclass;
|
||||
key.name = qname;
|
||||
key.namelabs = dname_count_size_labels(qname, &key.namelen);
|
||||
if(rbtree_find_less_equal(hints->tree, &key, &res)) {
|
||||
/* exact */
|
||||
result = (struct iter_hints_stub*)res;
|
||||
} else {
|
||||
/* smaller element (or no element) */
|
||||
int m;
|
||||
result = (struct iter_hints_stub*)res;
|
||||
if(!result || result->hint_class != qclass)
|
||||
return NULL;
|
||||
/* count number of labels matched */
|
||||
(void)dname_lab_cmp(result->name, result->namelabs, key.name,
|
||||
key.namelabs, &m);
|
||||
while(result) { /* go up until qname is subdomain of stub */
|
||||
if(result->namelabs <= m)
|
||||
break;
|
||||
result = result->parent;
|
||||
}
|
||||
if(!result)
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* If our cached delegation point is above the hint, we need to prime.
|
||||
*/
|
||||
if(dname_strict_subdomain(result->dp->name, result->dp->namelabs,
|
||||
cache_dp->name, cache_dp->namelabs))
|
||||
return result->dp; /* need to prime this stub */
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* iterator/iter_hints.h - iterative resolver module stub and root hints.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to assist the iterator module.
|
||||
* Keep track of stub and root hints, and read those from config.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_HINTS_H
|
||||
#define ITERATOR_ITER_HINTS_H
|
||||
#include "util/rbtree.h"
|
||||
struct iter_env;
|
||||
struct config_file;
|
||||
struct delegpt;
|
||||
struct region;
|
||||
|
||||
/**
|
||||
* Iterator hints structure
|
||||
*/
|
||||
struct iter_hints {
|
||||
/** region where hints are allocated */
|
||||
struct region* region;
|
||||
/**
|
||||
* Hints are stored in this tree. Sort order is specially chosen.
|
||||
* first sorted on qtype. Then on dname in nsec-like order, so that
|
||||
* a lookup on class, name will return an exact match or the closest
|
||||
* match which gives the ancestor needed.
|
||||
* contents of type iter_hints_stub. The class IN root is in here.
|
||||
*/
|
||||
rbtree_t* tree;
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterator hints for a particular stub.
|
||||
*/
|
||||
struct iter_hints_stub {
|
||||
/** redblacktree node, key is this structure: class and name */
|
||||
rbnode_t node;
|
||||
/** name */
|
||||
uint8_t* name;
|
||||
/** length of name */
|
||||
size_t namelen;
|
||||
/** number of labels in name */
|
||||
int namelabs;
|
||||
/** delegation point with hint information for this stub. */
|
||||
struct delegpt* dp;
|
||||
/** pointer to parent in stub hint tree (or NULL if none) */
|
||||
struct iter_hints_stub* parent;
|
||||
/** class of hints. host order. */
|
||||
uint16_t hint_class;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create hints
|
||||
* @return new hints or NULL on error.
|
||||
*/
|
||||
struct iter_hints* hints_create();
|
||||
|
||||
/**
|
||||
* Delete hints.
|
||||
* @param hints: to delete.
|
||||
*/
|
||||
void hints_delete(struct iter_hints* hints);
|
||||
|
||||
/**
|
||||
* Process hints config. Sets default values for root hints if no config.
|
||||
* @param hints: where to store.
|
||||
* @param cfg: config options.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Find root hints for the given class.
|
||||
* @param hints: hint storage.
|
||||
* @param qclass: class for which root hints are requested. host order.
|
||||
* @return: NULL if no hints, or a ptr to stored hints.
|
||||
*/
|
||||
struct delegpt* hints_lookup_root(struct iter_hints* hints, uint16_t qclass);
|
||||
|
||||
/**
|
||||
* Given a qname/qclass combination, and the delegation point from the cache
|
||||
* for this qname/qclass, determine if this combination indicates that a
|
||||
* stub hint exists and must be primed.
|
||||
*
|
||||
* @param hints: hint storage.
|
||||
* @param qname: The qname that generated the delegation point.
|
||||
* @param qclass: The qclass that generated the delegation point.
|
||||
* @param dp: The cache generated delegation point.
|
||||
* @return: A priming delegation point if there is a stub hint that must
|
||||
* be primed, otherwise null.
|
||||
*/
|
||||
struct delegpt* hints_lookup_stub(struct iter_hints* hints,
|
||||
uint8_t* qname, uint16_t qclass, struct delegpt* dp);
|
||||
|
||||
#endif /* ITERATOR_ITER_HINTS_H */
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* iterator/iter_resptype.c - response type information and classification.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file defines the response type. DNS Responses can be classified as
|
||||
* one of the response types.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_resptype.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/dname.h"
|
||||
|
||||
enum response_type
|
||||
response_type_from_cache(struct dns_msg* msg,
|
||||
struct query_info* request)
|
||||
{
|
||||
/* If the message is NXDOMAIN, then it is an ANSWER. */
|
||||
if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN)
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
|
||||
/* First we look at the answer section. This can tell us if this is
|
||||
* CNAME or positive ANSWER. */
|
||||
if(msg->rep->an_numrrsets > 0) {
|
||||
/* Now look at the answer section first. 3 states:
|
||||
* o our answer is there directly,
|
||||
* o our answer is there after a cname,
|
||||
* o or there is just a cname. */
|
||||
uint8_t* mname = request->qname;
|
||||
size_t mname_len = request->qname_len;
|
||||
size_t i;
|
||||
for(i=0; i<msg->rep->an_numrrsets; i++) {
|
||||
struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
|
||||
|
||||
/* If we have encountered an answer (before or
|
||||
* after a CNAME), then we are done! Note that
|
||||
* if qtype == CNAME then this will be noted as
|
||||
* an ANSWER before it gets treated as a CNAME,
|
||||
* as it should */
|
||||
if(ntohs(s->rk.type) == request->qtype &&
|
||||
ntohs(s->rk.rrset_class) == request->qclass &&
|
||||
query_dname_compare(mname, s->rk.dname) == 0) {
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
}
|
||||
|
||||
/* If we have encountered a CNAME, make sure that
|
||||
* it is relevant. */
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
|
||||
query_dname_compare(mname, s->rk.dname) == 0) {
|
||||
get_cname_target(s, &mname, &mname_len);
|
||||
}
|
||||
}
|
||||
|
||||
/* if we encountered a CNAME (or a bunch of CNAMEs), and
|
||||
* still got to here, then it is a CNAME response. (i.e.,
|
||||
* the CNAME chain didn't terminate in an answer rrset.) */
|
||||
if(mname != request->qname) {
|
||||
return RESPONSE_TYPE_CNAME;
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point, since we don't need to detect REFERRAL or LAME
|
||||
* messages, it can only be an ANSWER. */
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
}
|
||||
|
||||
enum response_type
|
||||
response_type_from_server(struct dns_msg* msg, struct query_info* request,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
uint8_t* origzone = (uint8_t*)"\000"; /* the default */
|
||||
size_t origzonelen = 1;
|
||||
size_t i;
|
||||
|
||||
if(!msg || !request)
|
||||
return RESPONSE_TYPE_THROWAWAY;
|
||||
|
||||
/* If the message is NXDOMAIN, then it answers the question. */
|
||||
if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN)
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
|
||||
/* Other response codes mean (so far) to throw the response away as
|
||||
* meaningless and move on to the next nameserver. */
|
||||
if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR)
|
||||
return RESPONSE_TYPE_THROWAWAY;
|
||||
|
||||
/* Note: TC bit has already been handled */
|
||||
|
||||
if(dp) {
|
||||
origzone = dp->name;
|
||||
origzonelen = dp->namelen;
|
||||
}
|
||||
|
||||
/* First we look at the answer section. This can tell us if this is a
|
||||
* CNAME or ANSWER or (provisional) ANSWER. */
|
||||
if(msg->rep->an_numrrsets > 0) {
|
||||
uint8_t* mname = request->qname;
|
||||
size_t mname_len = request->qname_len;
|
||||
|
||||
/* Now look at the answer section first. 3 states: our
|
||||
* answer is there directly, our answer is there after
|
||||
* a cname, or there is just a cname. */
|
||||
for(i=0; i<msg->rep->an_numrrsets; i++) {
|
||||
struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
|
||||
|
||||
/* If we have encountered an answer (before or
|
||||
* after a CNAME), then we are done! Note that
|
||||
* if qtype == CNAME then this will be noted as an
|
||||
* ANSWER before it gets treated as a CNAME, as
|
||||
* it should. */
|
||||
if(ntohs(s->rk.type) == request->qtype &&
|
||||
ntohs(s->rk.rrset_class) == request->qclass &&
|
||||
query_dname_compare(mname, s->rk.dname) == 0) {
|
||||
if((msg->rep->flags&BIT_AA))
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
/* If the AA bit isn't on, and we've seen
|
||||
* the answer, we only provisionally say
|
||||
* 'ANSWER' -- it very well could be a
|
||||
* REFERRAL. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we have encountered a CNAME, make sure that
|
||||
* it is relevant. */
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
|
||||
query_dname_compare(mname, s->rk.dname) == 0) {
|
||||
get_cname_target(s, &mname, &mname_len);
|
||||
}
|
||||
}
|
||||
/* if we encountered a CNAME (or a bunch of CNAMEs), and
|
||||
* still got to here, then it is a CNAME response.
|
||||
* (This is regardless of the AA bit at this point) */
|
||||
if(mname != request->qname) {
|
||||
return RESPONSE_TYPE_CNAME;
|
||||
}
|
||||
}
|
||||
|
||||
/* Looking at the authority section, we just look and see if
|
||||
* there is a delegation NS set, turning it into a delegation.
|
||||
* Otherwise, we will have to conclude ANSWER (either it is
|
||||
* NOERROR/NODATA, or an non-authoritative answer). */
|
||||
for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets +
|
||||
msg->rep->ns_numrrsets); i++) {
|
||||
struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
|
||||
|
||||
/* The normal way of detecting NOERROR/NODATA. */
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA &&
|
||||
dname_subdomain_c(request->qname, s->rk.dname)) {
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
}
|
||||
|
||||
/* Detect REFERRAL/LAME/ANSWER based on the relationship
|
||||
* of the NS set to the originating zone name. */
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NS) {
|
||||
/* If we are getting an NS set for the zone we
|
||||
* thought we were contacting, then it is an answer.*/
|
||||
/* FIXME: is this correct? */
|
||||
if(query_dname_compare(s->rk.dname, origzone) == 0) {
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
}
|
||||
/* If we are getting a referral upwards (or to
|
||||
* the same zone), then the server is 'lame'. */
|
||||
if(dname_subdomain_c(origzone, s->rk.dname)) {
|
||||
return RESPONSE_TYPE_LAME;
|
||||
}
|
||||
/* If the NS set is below the delegation point we
|
||||
* are on, and it is non-authoritative, then it is
|
||||
* a referral, otherwise it is an answer. */
|
||||
if(dname_subdomain_c(s->rk.dname, origzone)) {
|
||||
/* NOTE: I no longer remember in what case
|
||||
* we would like this to be an answer.
|
||||
* NODATA should have a SOA or nothing,
|
||||
* not an NS rrset.
|
||||
* True, referrals should not have the AA
|
||||
* bit set, but... */
|
||||
|
||||
/* if((msg->rep->flags&BIT_AA))
|
||||
return RESPONSE_TYPE_ANSWER; */
|
||||
return RESPONSE_TYPE_REFERRAL;
|
||||
}
|
||||
/* Otherwise, the NS set is irrelevant. */
|
||||
}
|
||||
}
|
||||
|
||||
/* If we've gotten this far, this is NOERROR/NODATA (which could
|
||||
* be an entirely empty message) */
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* iterator/iter_resptype.h - response type information and classification.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file defines the response type. DNS Responses can be classified as
|
||||
* one of the response types.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_RESPTYPE_H
|
||||
#define ITERATOR_ITER_RESPTYPE_H
|
||||
struct dns_msg;
|
||||
struct query_info;
|
||||
struct delegpt;
|
||||
|
||||
/**
|
||||
* The response type is used to interpret the response.
|
||||
*/
|
||||
enum response_type {
|
||||
/**
|
||||
* 'untyped' means that the type of this response hasn't been
|
||||
* assigned.
|
||||
*/
|
||||
RESPONSE_TYPE_UNTYPED = 0,
|
||||
|
||||
/**
|
||||
* 'answer' means that the response terminates the resolution
|
||||
* process.
|
||||
*/
|
||||
RESPONSE_TYPE_ANSWER,
|
||||
|
||||
/** 'delegation' means that the response is a delegation. */
|
||||
RESPONSE_TYPE_REFERRAL,
|
||||
|
||||
/**
|
||||
* 'cname' means that the response is a cname without the final
|
||||
* answer, and thus must be restarted.
|
||||
*/
|
||||
RESPONSE_TYPE_CNAME,
|
||||
|
||||
/**
|
||||
* 'throwaway' means that this particular response should be
|
||||
* discarded and the next nameserver should be contacted
|
||||
*/
|
||||
RESPONSE_TYPE_THROWAWAY,
|
||||
|
||||
/**
|
||||
* 'lame' means that this particular response indicates that
|
||||
* the nameserver knew nothing about the question.
|
||||
*/
|
||||
RESPONSE_TYPE_LAME
|
||||
};
|
||||
|
||||
/**
|
||||
* Classifies a response message from cache based on the current request.
|
||||
* Note that this routine assumes that THROWAWAY or LAME responses will not
|
||||
* occur. Also, it will not detect REFERRAL type messages, since those are
|
||||
* (currently) automatically classified based on how they came from the
|
||||
* cache (findDelegation() instead of lookup()).
|
||||
*
|
||||
* @param msg: the message from the cache.
|
||||
* @param request: the request that generated the response.
|
||||
* @return the response type (CNAME or ANSWER).
|
||||
*/
|
||||
enum response_type response_type_from_cache(struct dns_msg* msg,
|
||||
struct query_info* request);
|
||||
|
||||
/**
|
||||
* Classifies a response message (from the wire) based on the current
|
||||
* request.
|
||||
*
|
||||
* NOTE: currently this routine uses the AA bit in the response to help
|
||||
* distinguish between some non-standard referrals and answers. It also
|
||||
* relies somewhat on the originating zone to be accurate (for lameness
|
||||
* detection, mostly).
|
||||
*
|
||||
* @param msg: the message from the cache.
|
||||
* @param request: the request that generated the response.
|
||||
* @param dp: The delegation point that was being queried
|
||||
* when the response was returned.
|
||||
* @return the response type (CNAME or ANSWER).
|
||||
*/
|
||||
enum response_type response_type_from_server(struct dns_msg* msg,
|
||||
struct query_info* request, struct delegpt* dp);
|
||||
|
||||
#endif /* ITERATOR_ITER_RESPTYPE_H */
|
||||
@@ -0,0 +1,565 @@
|
||||
/*
|
||||
* iterator/iter_scrub.c - scrubbing, normalization, sanitization of DNS msgs.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file has routine(s) for cleaning up incoming DNS messages from
|
||||
* possible useless or malicious junk in it.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_scrub.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/region-allocator.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/module.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/alloc.h"
|
||||
|
||||
/** RRset flag used during scrubbing. The RRset is OK. */
|
||||
#define RRSET_SCRUB_OK 0x80
|
||||
|
||||
/** remove rrset, update loop variables */
|
||||
static void
|
||||
remove_rrset(const char* str, ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct rrset_parse* prev, struct rrset_parse** rrset)
|
||||
{
|
||||
if(verbosity >= VERB_DETAIL
|
||||
&& (*rrset)->dname_len <= LDNS_MAX_DOMAINLEN) {
|
||||
uint8_t buf[LDNS_MAX_DOMAINLEN+1];
|
||||
dname_pkt_copy(pkt, buf, (*rrset)->dname);
|
||||
log_nametypeclass(VERB_DETAIL, str, buf,
|
||||
(*rrset)->type, ntohs((*rrset)->rrset_class));
|
||||
}
|
||||
if(prev)
|
||||
prev->rrset_all_next = (*rrset)->rrset_all_next;
|
||||
else msg->rrset_first = (*rrset)->rrset_all_next;
|
||||
if(msg->rrset_last == *rrset)
|
||||
msg->rrset_last = prev;
|
||||
msg->rrset_count --;
|
||||
switch((*rrset)->section) {
|
||||
case LDNS_SECTION_ANSWER: msg->an_rrsets--; break;
|
||||
case LDNS_SECTION_AUTHORITY: msg->ns_rrsets--; break;
|
||||
case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets--; break;
|
||||
default: log_assert(0);
|
||||
}
|
||||
msgparse_bucket_remove(msg, *rrset);
|
||||
*rrset = (*rrset)->rrset_all_next;
|
||||
}
|
||||
|
||||
/** return true if rr type has additional names in it */
|
||||
static int
|
||||
has_additional(uint16_t t)
|
||||
{
|
||||
switch(t) {
|
||||
case LDNS_RR_TYPE_MB:
|
||||
case LDNS_RR_TYPE_MD:
|
||||
case LDNS_RR_TYPE_MF:
|
||||
case LDNS_RR_TYPE_NS:
|
||||
case LDNS_RR_TYPE_MX:
|
||||
case LDNS_RR_TYPE_KX:
|
||||
case LDNS_RR_TYPE_SRV:
|
||||
return 1;
|
||||
case LDNS_RR_TYPE_NAPTR:
|
||||
/* TODO: NAPTR not supported, glue stripped off */
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** get additional name from rrset RR, return false if no name present */
|
||||
static int
|
||||
get_additional_name(struct rrset_parse* rrset, struct rr_parse* rr,
|
||||
uint8_t** nm, size_t* nmlen, ldns_buffer* pkt)
|
||||
{
|
||||
size_t offset = 0;
|
||||
size_t len, oldpos;
|
||||
switch(rrset->type) {
|
||||
case LDNS_RR_TYPE_MB:
|
||||
case LDNS_RR_TYPE_MD:
|
||||
case LDNS_RR_TYPE_MF:
|
||||
case LDNS_RR_TYPE_NS:
|
||||
offset = 0;
|
||||
break;
|
||||
case LDNS_RR_TYPE_MX:
|
||||
case LDNS_RR_TYPE_KX:
|
||||
offset = 2;
|
||||
break;
|
||||
case LDNS_RR_TYPE_SRV:
|
||||
offset = 6;
|
||||
break;
|
||||
case LDNS_RR_TYPE_NAPTR:
|
||||
/* TODO: NAPTR not supported, glue stripped off */
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
len = ldns_read_uint16(rr->ttl_data+sizeof(uint32_t));
|
||||
if(len < offset+1)
|
||||
return 0; /* rdata field too small */
|
||||
*nm = rr->ttl_data+sizeof(uint32_t)+sizeof(uint16_t)+offset;
|
||||
oldpos = ldns_buffer_position(pkt);
|
||||
ldns_buffer_set_position(pkt, (size_t)(*nm - ldns_buffer_begin(pkt)));
|
||||
*nmlen = pkt_dname_len(pkt);
|
||||
ldns_buffer_set_position(pkt, oldpos);
|
||||
if(*nmlen == 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Place mark on rrsets in additional section they are OK */
|
||||
static void
|
||||
mark_additional_rrset(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct rrset_parse* rrset)
|
||||
{
|
||||
/* Mark A and AAAA for NS as appropriate additional section info. */
|
||||
uint8_t* nm = NULL;
|
||||
size_t nmlen = 0;
|
||||
struct rr_parse* rr;
|
||||
|
||||
if(!has_additional(rrset->type))
|
||||
return;
|
||||
for(rr = rrset->rr_first; rr; rr = rr->next) {
|
||||
if(get_additional_name(rrset, rr, &nm, &nmlen, pkt)) {
|
||||
/* mark A */
|
||||
hashvalue_t h = pkt_hash_rrset(pkt, nm, LDNS_RR_TYPE_A,
|
||||
rrset->rrset_class, 0);
|
||||
struct rrset_parse* r = msgparse_hashtable_lookup(
|
||||
msg, pkt, h, 0, nm, nmlen,
|
||||
LDNS_RR_TYPE_A, rrset->rrset_class);
|
||||
if(r && r->section == LDNS_SECTION_ADDITIONAL) {
|
||||
r->flags |= RRSET_SCRUB_OK;
|
||||
}
|
||||
|
||||
/* mark AAAA */
|
||||
h = pkt_hash_rrset(pkt, nm, LDNS_RR_TYPE_AAAA,
|
||||
rrset->rrset_class, 0);
|
||||
r = msgparse_hashtable_lookup(msg, pkt, h, 0, nm,
|
||||
nmlen, LDNS_RR_TYPE_AAAA, rrset->rrset_class);
|
||||
if(r && r->section == LDNS_SECTION_ADDITIONAL) {
|
||||
r->flags |= RRSET_SCRUB_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Get target name of a CNAME */
|
||||
static int
|
||||
parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname,
|
||||
size_t* snamelen)
|
||||
{
|
||||
if(rrset->rr_count != 1) {
|
||||
verbose(VERB_ALGO, "Found CNAME rrset with "
|
||||
"size > 1: %u", (unsigned)rrset->rr_count);
|
||||
return 0;
|
||||
}
|
||||
if(rrset->rr_first->size < sizeof(uint16_t)+1)
|
||||
return 0; /* CNAME rdata too small */
|
||||
*sname = rrset->rr_first->ttl_data + sizeof(uint32_t)
|
||||
+ sizeof(uint16_t); /* skip ttl, rdatalen */
|
||||
*snamelen = rrset->rr_first->size - sizeof(uint16_t);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Synthesize CNAME from DNAME, false if too long */
|
||||
static int
|
||||
synth_cname(uint8_t* qname, size_t qnamelen, struct rrset_parse* dname_rrset,
|
||||
uint8_t* alias, size_t* aliaslen, ldns_buffer* pkt)
|
||||
{
|
||||
/* 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))
|
||||
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 */
|
||||
*aliaslen = qnamelen + dtarglen - dname_rrset->dname_len;
|
||||
if(*aliaslen > LDNS_MAX_DOMAINLEN)
|
||||
return 0; /* should have been RCODE YXDOMAIN */
|
||||
/* decompress dnames into buffer, we know it fits */
|
||||
dname_pkt_copy(pkt, alias, qname);
|
||||
dname_pkt_copy(pkt, alias+(qnamelen-dname_rrset->dname_len), dtarg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** synthesize a CNAME rrset */
|
||||
static struct rrset_parse*
|
||||
synth_cname_rrset(uint8_t** sname, size_t* snamelen, uint8_t* alias,
|
||||
size_t aliaslen, struct region* region, struct msg_parse* msg,
|
||||
struct rrset_parse* rrset, struct rrset_parse* prev,
|
||||
struct rrset_parse* nx, ldns_buffer* pkt)
|
||||
{
|
||||
struct rrset_parse* cn = (struct rrset_parse*)region_alloc(region,
|
||||
sizeof(struct rrset_parse));
|
||||
if(!cn)
|
||||
return NULL;
|
||||
memset(cn, 0, sizeof(*cn));
|
||||
cn->rr_first = (struct rr_parse*)region_alloc(region,
|
||||
sizeof(struct rr_parse));
|
||||
if(!cn->rr_first)
|
||||
return NULL;
|
||||
cn->rr_last = cn->rr_first;
|
||||
/* CNAME from sname to alias */
|
||||
cn->dname = (uint8_t*)region_alloc(region, *snamelen);
|
||||
if(!cn->dname)
|
||||
return NULL;
|
||||
dname_pkt_copy(pkt, cn->dname, *sname);
|
||||
cn->dname_len = *snamelen;
|
||||
cn->type = LDNS_RR_TYPE_CNAME;
|
||||
cn->section = rrset->section;
|
||||
cn->rrset_class = rrset->rrset_class;
|
||||
cn->rr_count = 1;
|
||||
cn->size = sizeof(uint16_t) + aliaslen;
|
||||
cn->hash=pkt_hash_rrset(pkt, cn->dname, cn->type, cn->rrset_class, 0);
|
||||
/* allocate TTL + rdatalen + uncompressed dname */
|
||||
memset(cn->rr_first, 0, sizeof(struct rr_parse));
|
||||
cn->rr_first->outside_packet = 1;
|
||||
cn->rr_first->ttl_data = (uint8_t*)region_alloc(region,
|
||||
sizeof(uint32_t)+sizeof(uint16_t)+aliaslen);
|
||||
if(!cn->rr_first->ttl_data)
|
||||
return NULL;
|
||||
ldns_write_uint32(cn->rr_first->ttl_data, 0); /* TTL = 0 */
|
||||
ldns_write_uint16(cn->rr_first->ttl_data+4, aliaslen);
|
||||
memmove(cn->rr_first->ttl_data+6, alias, aliaslen);
|
||||
cn->rr_first->size = sizeof(uint16_t)+aliaslen;
|
||||
|
||||
/* link it in */
|
||||
cn->rrset_all_next = nx;
|
||||
if(prev)
|
||||
prev->rrset_all_next = cn;
|
||||
else msg->rrset_first = cn;
|
||||
if(nx == NULL)
|
||||
msg->rrset_last = cn;
|
||||
msg->rrset_count ++;
|
||||
msg->an_rrsets++;
|
||||
/* it is not inserted in the msg hashtable. */
|
||||
|
||||
*sname = cn->rr_first->ttl_data + sizeof(uint32_t)+sizeof(uint16_t);
|
||||
*snamelen = aliaslen;
|
||||
return cn;
|
||||
}
|
||||
|
||||
/** check if DNAME applies to a name */
|
||||
static int
|
||||
pkt_strict_sub(ldns_buffer* pkt, uint8_t* sname, uint8_t* dr)
|
||||
{
|
||||
uint8_t buf1[LDNS_MAX_DOMAINLEN+1];
|
||||
uint8_t buf2[LDNS_MAX_DOMAINLEN+1];
|
||||
/* decompress names */
|
||||
dname_pkt_copy(pkt, buf1, sname);
|
||||
dname_pkt_copy(pkt, buf2, dr);
|
||||
return dname_strict_subdomain_c(buf1, buf2);
|
||||
}
|
||||
|
||||
/** check subdomain with decompression */
|
||||
static int
|
||||
pkt_sub(ldns_buffer* pkt, uint8_t* comprname, uint8_t* zone)
|
||||
{
|
||||
uint8_t buf[LDNS_MAX_DOMAINLEN+1];
|
||||
dname_pkt_copy(pkt, buf, comprname);
|
||||
return dname_subdomain_c(buf, zone);
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine normalizes a response. This includes removing "irrelevant"
|
||||
* records from the answer and additional sections and (re)synthesizing
|
||||
* CNAMEs from DNAMEs, if present.
|
||||
*
|
||||
* @param pkt: packet.
|
||||
* @param msg: msg to normalize.
|
||||
* @param qinfo: original query.
|
||||
* @param region: where to allocate synthesized CNAMEs.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
static int
|
||||
scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct query_info* qinfo, struct region* region)
|
||||
{
|
||||
uint8_t* sname = qinfo->qname;
|
||||
size_t snamelen = qinfo->qname_len;
|
||||
struct rrset_parse* rrset, *prev;
|
||||
|
||||
if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR &&
|
||||
FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN)
|
||||
return 1;
|
||||
|
||||
/* For the ANSWER section, remove all "irrelevant" records and add
|
||||
* synthesized CNAMEs from DNAMEs
|
||||
* This will strip out-of-order CNAMEs as well. */
|
||||
|
||||
/* walk through the parse packet rrset list, keep track of previous
|
||||
* for insert and delete ease, and examine every RRset */
|
||||
prev = NULL;
|
||||
rrset = msg->rrset_first;
|
||||
while(rrset && rrset->section == LDNS_SECTION_ANSWER) {
|
||||
if(rrset->type == LDNS_RR_TYPE_DNAME &&
|
||||
pkt_strict_sub(pkt, sname, rrset->dname)) {
|
||||
/* check if next rrset is correct CNAME. else,
|
||||
* synthesize a CNAME */
|
||||
struct rrset_parse* nx = rrset->rrset_all_next;
|
||||
uint8_t alias[LDNS_MAX_DOMAINLEN+1];
|
||||
size_t aliaslen = 0;
|
||||
if(rrset->rr_count != 1) {
|
||||
verbose(VERB_ALGO, "Found DNAME rrset with "
|
||||
"size > 1: %u",
|
||||
(unsigned)rrset->rr_count);
|
||||
return 0;
|
||||
}
|
||||
if(!synth_cname(sname, snamelen, rrset, alias,
|
||||
&aliaslen, pkt)) {
|
||||
verbose(VERB_ALGO, "synthesized CNAME "
|
||||
"too long");
|
||||
return 0;
|
||||
}
|
||||
if(nx && nx->type == LDNS_RR_TYPE_CNAME &&
|
||||
dname_pkt_compare(pkt, sname, nx->dname) == 0) {
|
||||
/* check next cname */
|
||||
uint8_t* t = NULL;
|
||||
size_t tlen = 0;
|
||||
if(!parse_get_cname_target(rrset, &t, &tlen))
|
||||
return 0;
|
||||
if(dname_pkt_compare(pkt, alias, t) == 0) {
|
||||
/* it's OK and better capitalized */
|
||||
prev = rrset;
|
||||
rrset = nx;
|
||||
continue;
|
||||
}
|
||||
/* synth ourselves */
|
||||
}
|
||||
/* synth a CNAME rrset */
|
||||
prev = synth_cname_rrset(&sname, &snamelen, alias,
|
||||
aliaslen, region, msg, rrset, rrset, nx, pkt);
|
||||
if(!prev) {
|
||||
log_err("out of memory synthesizing CNAME");
|
||||
return 0;
|
||||
}
|
||||
/* FIXME: resolve the conflict between synthesized
|
||||
* CNAME ttls and the cache. */
|
||||
rrset = nx;
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
/* The only records in the ANSWER section not allowed to */
|
||||
if(dname_pkt_compare(pkt, sname, rrset->dname) != 0) {
|
||||
remove_rrset("normalize: removing irrelevant RRset:",
|
||||
pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Follow the CNAME chain. */
|
||||
if(rrset->type == LDNS_RR_TYPE_CNAME) {
|
||||
if(!parse_get_cname_target(rrset, &sname, &snamelen))
|
||||
return 0;
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Otherwise, make sure that the RRset matches the qtype. */
|
||||
if(qinfo->qtype != LDNS_RR_TYPE_ANY &&
|
||||
qinfo->qtype != rrset->type) {
|
||||
remove_rrset("normalize: removing irrelevant RRset:",
|
||||
pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Mark the additional names from relevant rrset as OK. */
|
||||
mark_additional_rrset(pkt, msg, rrset);
|
||||
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
}
|
||||
|
||||
/* Mark additional names from AUTHORITY */
|
||||
while(rrset && rrset->section == LDNS_SECTION_AUTHORITY) {
|
||||
mark_additional_rrset(pkt, msg, rrset);
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
}
|
||||
|
||||
/* For each record in the additional section, remove it if it is an
|
||||
* address record and not in the collection of additional names
|
||||
* found in ANSWER and AUTHORITY. */
|
||||
/* These records have not been marked OK previously */
|
||||
while(rrset && rrset->section == LDNS_SECTION_ADDITIONAL) {
|
||||
/* FIXME: what about other types? */
|
||||
if(rrset->type==LDNS_RR_TYPE_A ||
|
||||
rrset->type==LDNS_RR_TYPE_AAAA)
|
||||
{
|
||||
if((rrset->flags & RRSET_SCRUB_OK)) {
|
||||
/* remove flag to clean up flags variable */
|
||||
rrset->flags &= ~RRSET_SCRUB_OK;
|
||||
} else {
|
||||
remove_rrset("normalize: removing irrelevant "
|
||||
"RRset:", pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store potential poison in the cache (only if hardening disabled).
|
||||
* The rrset is stored in the cache but removed from the message.
|
||||
* So that it will be used for infrastructure purposes, but not be
|
||||
* returned to the client.
|
||||
* @param pkt: packet
|
||||
* @param msg: message parsed
|
||||
* @param env: environment with cache
|
||||
* @param rrset: to store.
|
||||
*/
|
||||
static void
|
||||
store_rrset(ldns_buffer* pkt, struct msg_parse* msg, struct module_env* env,
|
||||
struct rrset_parse* rrset)
|
||||
{
|
||||
struct ub_packed_rrset_key* k;
|
||||
struct packed_rrset_data* d;
|
||||
struct rrset_ref ref;
|
||||
uint32_t now = time(NULL);
|
||||
|
||||
k = alloc_special_obtain(env->alloc);
|
||||
if(!k)
|
||||
return;
|
||||
k->entry.data = NULL;
|
||||
if(!parse_copy_decompress_rrset(pkt, msg, rrset, NULL, k)) {
|
||||
alloc_special_release(env->alloc, k);
|
||||
return;
|
||||
}
|
||||
d = (struct packed_rrset_data*)k->entry.data;
|
||||
packed_rrset_ttl_add(d, now);
|
||||
ref.key = k;
|
||||
ref.id = k->id;
|
||||
/*ignore ret: it was in the cache, ref updated */
|
||||
(void)rrset_cache_update(env->rrset_cache, &ref,
|
||||
env->alloc, now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a response event, remove suspect RRsets from the response.
|
||||
* "Suspect" rrsets are potentially poison. Note that this routine expects
|
||||
* the response to be in a "normalized" state -- that is, all "irrelevant"
|
||||
* RRsets have already been removed, CNAMEs are in order, etc.
|
||||
*
|
||||
* @param pkt: packet.
|
||||
* @param msg: msg to normalize.
|
||||
* @param zonename: name of server zone.
|
||||
* @param env: module environment with config and cache.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
static int
|
||||
scrub_sanitize(ldns_buffer* pkt, struct msg_parse* msg, uint8_t* zonename,
|
||||
struct module_env* env)
|
||||
{
|
||||
struct rrset_parse* rrset, *prev;
|
||||
prev = NULL;
|
||||
rrset = msg->rrset_first;
|
||||
|
||||
/* At this point, we brutally remove ALL rrsets that aren't
|
||||
* children of the originating zone. The idea here is that,
|
||||
* as far as we know, the server that we contacted is ONLY
|
||||
* authoritative for the originating zone. It, of course, MAY
|
||||
* be authoriative for any other zones, and of course, MAY
|
||||
* NOT be authoritative for some subdomains of the originating
|
||||
* zone. */
|
||||
while(rrset) {
|
||||
|
||||
/* skip DNAME records -- they will always be followed by a
|
||||
* synthesized CNAME, which will be relevant.
|
||||
* FIXME: should this do something differently with DNAME
|
||||
* rrsets NOT in Section.ANSWER? */
|
||||
/* But since DNAME records are also subdomains of the zone,
|
||||
* same check can be used */
|
||||
|
||||
if(!pkt_sub(pkt, rrset->dname, zonename)) {
|
||||
if(!env->cfg->harden_glue) {
|
||||
/* store in cache! Since it is relevant
|
||||
* (from normalize) it will be picked up
|
||||
* from the cache to be used later */
|
||||
store_rrset(pkt, msg, env, rrset);
|
||||
remove_rrset("sanitize: storing potential "
|
||||
"poison RRset:", pkt, msg, prev, &rrset);
|
||||
} else
|
||||
remove_rrset("sanitize: removing potential "
|
||||
"poison RRset:", pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
scrub_message(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct query_info* qinfo, uint8_t* zonename, struct region* region,
|
||||
struct module_env* env)
|
||||
{
|
||||
/* basic sanity checks */
|
||||
log_nametypeclass(VERB_ALGO, "scrub for", zonename, LDNS_RR_TYPE_NS,
|
||||
qinfo->qclass);
|
||||
if(msg->qdcount > 1)
|
||||
return 0;
|
||||
if( !(msg->flags&BIT_QR) )
|
||||
return 0;
|
||||
|
||||
/* if a query is echoed back, make sure it is correct. Otherwise,
|
||||
* this may be not a reply to our query. */
|
||||
if(msg->qdcount == 1) {
|
||||
if(dname_pkt_compare(pkt, msg->qname, qinfo->qname) != 0)
|
||||
return 0;
|
||||
if(msg->qtype != qinfo->qtype || msg->qclass != qinfo->qclass)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* normalize the response, this cleans up the additional. */
|
||||
if(!scrub_normalize(pkt, msg, qinfo, region))
|
||||
return 0;
|
||||
/* delete all out-of-zone information */
|
||||
if(!scrub_sanitize(pkt, msg, zonename, env))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* iterator/iter_scrub.h - scrubbing, normalization, sanitization of DNS msgs.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file has routine(s) for cleaning up incoming DNS messages from
|
||||
* possible useless or malicious junk in it.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_SCRUB_H
|
||||
#define ITERATOR_ITER_SCRUB_H
|
||||
struct msg_parse;
|
||||
struct query_info;
|
||||
struct region;
|
||||
struct module_env;
|
||||
|
||||
/**
|
||||
* Cleanup the passed dns message.
|
||||
* @param pkt: the packet itself, for resolving name compression pointers.
|
||||
* the packet buffer is unaltered.
|
||||
* @param msg: the parsed packet, this structure is cleaned up.
|
||||
* @param qinfo: the query info that was sent to the server. Checked.
|
||||
* @param zonename: the name of the last delegation point.
|
||||
* Used to determine out of bailiwick information.
|
||||
* @param region: where to allocate (new) parts of the message.
|
||||
* @param env: module environment with config settings and cache.
|
||||
* @return: false if the message is total waste. true if scrubbed with success.
|
||||
*/
|
||||
int scrub_message(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct query_info* qinfo, uint8_t* zonename, struct region* region,
|
||||
struct module_env* env);
|
||||
|
||||
#endif /* ITERATOR_ITER_SCRUB_H */
|
||||
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* iterator/iter_utils.c - iterative resolver module utility functions.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to assist the iterator module.
|
||||
* Configuration options. Forward zones.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_utils.h"
|
||||
#include "iterator/iterator.h"
|
||||
#include "iterator/iter_hints.h"
|
||||
#include "iterator/iter_fwd.h"
|
||||
#include "iterator/iter_donotq.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/module.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/region-allocator.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/random.h"
|
||||
|
||||
/** count number of integers in fetch policy string */
|
||||
static int
|
||||
fetch_count(const char* s)
|
||||
{
|
||||
/* format ::= (sp num)+ sp */
|
||||
/* num ::= [-](0-9)+ */
|
||||
/* sp ::= (space|tab)* */
|
||||
int num = 0;
|
||||
while(*s) {
|
||||
while(*s && isspace(*s))
|
||||
s++;
|
||||
if(!*s) /* end of string */
|
||||
break;
|
||||
if(*s == '-')
|
||||
s++;
|
||||
if(!*s) /* only - not allowed */
|
||||
return 0;
|
||||
if(!isdigit(*s)) /* bad character */
|
||||
return 0;
|
||||
while(*s && isdigit(*s))
|
||||
s++;
|
||||
num++;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/** fillup fetch policy array */
|
||||
static void
|
||||
fetch_fill(struct iter_env* ie, const char* str)
|
||||
{
|
||||
char* s = (char*)str, *e;
|
||||
int i;
|
||||
for(i=0; i<ie->max_dependency_depth+1; i++) {
|
||||
ie->target_fetch_policy[i] = strtol(s, &e, 10);
|
||||
log_assert(s != e); /* parsed syntax already */
|
||||
s = e;
|
||||
}
|
||||
}
|
||||
|
||||
/** Read config string that represents the target fetch policy */
|
||||
static int
|
||||
read_fetch_policy(struct iter_env* ie, const char* str)
|
||||
{
|
||||
int count = fetch_count(str);
|
||||
if(count < 1) {
|
||||
log_err("Cannot parse target fetch policy: \"%s\"", str);
|
||||
return 0;
|
||||
}
|
||||
ie->max_dependency_depth = count - 1;
|
||||
ie->target_fetch_policy = (int*)calloc(
|
||||
(size_t)ie->max_dependency_depth+1, sizeof(int));
|
||||
if(!ie->target_fetch_policy) {
|
||||
log_err("alloc fetch policy: out of memory");
|
||||
return 0;
|
||||
}
|
||||
fetch_fill(ie, str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
|
||||
{
|
||||
int i;
|
||||
/* target fetch policy */
|
||||
if(!read_fetch_policy(iter_env, cfg->target_fetch_policy))
|
||||
return 0;
|
||||
for(i=0; i<iter_env->max_dependency_depth+1; i++)
|
||||
verbose(VERB_DETAIL, "target fetch policy for level %d is %d",
|
||||
i, iter_env->target_fetch_policy[i]);
|
||||
|
||||
if(!iter_env->hints)
|
||||
iter_env->hints = hints_create();
|
||||
if(!iter_env->hints || !hints_apply_cfg(iter_env->hints, cfg)) {
|
||||
log_err("Could not set root or stub hints");
|
||||
return 0;
|
||||
}
|
||||
if(!iter_env->fwds)
|
||||
iter_env->fwds = forwards_create();
|
||||
if(!iter_env->fwds || !forwards_apply_cfg(iter_env->fwds, cfg)) {
|
||||
log_err("Could not set forward zones");
|
||||
return 0;
|
||||
}
|
||||
if(!iter_env->donotq)
|
||||
iter_env->donotq = donotq_create();
|
||||
if(!iter_env->donotq || !donotq_apply_cfg(iter_env->donotq, cfg)) {
|
||||
log_err("Could not set donotqueryaddresses");
|
||||
return 0;
|
||||
}
|
||||
iter_env->supports_ipv6 = cfg->do_ip6;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** filter out unsuitable targets, return rtt or -1 */
|
||||
static int
|
||||
iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env,
|
||||
uint8_t* name, size_t namelen, time_t now, struct delegpt_addr* a)
|
||||
{
|
||||
int rtt;
|
||||
int lame;
|
||||
if(donotq_lookup(iter_env->donotq, &a->addr, a->addrlen)) {
|
||||
return -1; /* server is on the donotquery list */
|
||||
}
|
||||
if(!iter_env->supports_ipv6 && addr_is_ip6(&a->addr)) {
|
||||
return -1; /* there is no ip6 available */
|
||||
}
|
||||
/* check lameness - need zone , class info */
|
||||
if(infra_get_lame_rtt(env->infra_cache, &a->addr, a->addrlen,
|
||||
name, namelen, &lame, &rtt, now)) {
|
||||
if(lame)
|
||||
return -1; /* server is lame */
|
||||
else if(rtt >= USEFUL_SERVER_TOP_TIMEOUT)
|
||||
return -1; /* server is unresponsive */
|
||||
else return rtt;
|
||||
}
|
||||
/* no server information present */
|
||||
return UNKNOWN_SERVER_NICENESS;
|
||||
}
|
||||
|
||||
/** filter the addres list, putting best targets at front,
|
||||
* returns number of best targets (or 0, no suitable targets) */
|
||||
static int
|
||||
iter_filter_order(struct iter_env* iter_env, struct module_env* env,
|
||||
uint8_t* name, size_t namelen, time_t now, struct delegpt* dp)
|
||||
{
|
||||
int got_num = 0, got_rtt = 0, thisrtt, swap_to_front;
|
||||
struct delegpt_addr* a, *n, *prev=NULL;
|
||||
|
||||
a = dp->result_list;
|
||||
while(a) {
|
||||
/* filter out unsuitable targets */
|
||||
thisrtt = iter_filter_unsuitable(iter_env, env, name, namelen,
|
||||
now, a);
|
||||
if(thisrtt == -1) {
|
||||
prev = a;
|
||||
a = a->next_result;
|
||||
continue;
|
||||
}
|
||||
/* classify the server address and determine what to do */
|
||||
swap_to_front = 0;
|
||||
if(got_num == 0) {
|
||||
got_rtt = thisrtt;
|
||||
got_num = 1;
|
||||
swap_to_front = 1;
|
||||
} else if(thisrtt == got_rtt) {
|
||||
got_num++;
|
||||
swap_to_front = 1;
|
||||
} else if(thisrtt < got_rtt) {
|
||||
got_rtt = thisrtt;
|
||||
got_num = 1; /* start back at count of 1 */
|
||||
swap_to_front = 1;
|
||||
}
|
||||
/* swap to front if necessary, or move to next result */
|
||||
if(swap_to_front && prev) {
|
||||
n = a->next_result;
|
||||
prev->next_result = n;
|
||||
a->next_result = dp->result_list;
|
||||
dp->result_list = a;
|
||||
a = n;
|
||||
} else {
|
||||
prev = a;
|
||||
a = a->next_result;
|
||||
}
|
||||
}
|
||||
return got_num;
|
||||
}
|
||||
|
||||
struct delegpt_addr*
|
||||
iter_server_selection(struct iter_env* iter_env,
|
||||
struct module_env* env, struct delegpt* dp,
|
||||
uint8_t* name, size_t namelen)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
int sel;
|
||||
struct delegpt_addr* a, *prev;
|
||||
int num = iter_filter_order(iter_env, env, name, namelen, now, dp);
|
||||
|
||||
if(num == 0)
|
||||
return NULL;
|
||||
if(num == 1) {
|
||||
a = dp->result_list;
|
||||
dp->result_list = a->next_result;
|
||||
return a;
|
||||
}
|
||||
/* randomly select a target from the list */
|
||||
log_assert(num > 1);
|
||||
/* we do not need secure random numbers here, but
|
||||
* we do need it to be threadsafe, so we use this */
|
||||
sel = ub_random(env->rnd) % num;
|
||||
a = dp->result_list;
|
||||
prev = NULL;
|
||||
while(sel > 0 && a) {
|
||||
prev = a;
|
||||
a = a->next_result;
|
||||
sel--;
|
||||
}
|
||||
if(!a) /* robustness */
|
||||
return NULL;
|
||||
/* remove it from the delegation point result list */
|
||||
if(prev)
|
||||
prev->next_result = a->next_result;
|
||||
else dp->result_list = a->next_result;
|
||||
return a;
|
||||
}
|
||||
|
||||
struct dns_msg*
|
||||
dns_alloc_msg(ldns_buffer* pkt, struct msg_parse* msg, struct region* region)
|
||||
{
|
||||
struct dns_msg* m = (struct dns_msg*)region_alloc(region,
|
||||
sizeof(struct dns_msg));
|
||||
if(!m)
|
||||
return NULL;
|
||||
memset(m, 0, sizeof(*m));
|
||||
if(!parse_create_msg(pkt, msg, NULL, &m->qinfo, &m->rep, region)) {
|
||||
log_err("malloc failure: allocating incoming dns_msg");
|
||||
return NULL;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
struct dns_msg*
|
||||
dns_copy_msg(struct dns_msg* from, struct region* region)
|
||||
{
|
||||
struct dns_msg* m = (struct dns_msg*)region_alloc(region,
|
||||
sizeof(struct dns_msg));
|
||||
if(!m)
|
||||
return NULL;
|
||||
m->qinfo = from->qinfo;
|
||||
if(!(m->qinfo.qname = region_alloc_init(region, from->qinfo.qname,
|
||||
from->qinfo.qname_len)))
|
||||
return NULL;
|
||||
if(!(m->rep = reply_info_copy(from->rep, NULL, region)))
|
||||
return NULL;
|
||||
return m;
|
||||
}
|
||||
|
||||
int
|
||||
iter_dns_store(struct module_env* env, struct query_info* msgqinf,
|
||||
struct reply_info* msgrep, int is_referral)
|
||||
{
|
||||
struct reply_info* rep = NULL;
|
||||
/* alloc, malloc properly (not in region, like msg is) */
|
||||
rep = reply_info_copy(msgrep, env->alloc, NULL);
|
||||
if(!rep)
|
||||
return 0;
|
||||
|
||||
if(is_referral) {
|
||||
/* store rrsets */
|
||||
struct rrset_ref ref;
|
||||
uint32_t now = time(NULL);
|
||||
size_t i;
|
||||
for(i=0; i<rep->rrset_count; i++) {
|
||||
packed_rrset_ttl_add((struct packed_rrset_data*)
|
||||
rep->rrsets[i]->entry.data, now);
|
||||
ref.key = rep->rrsets[i];
|
||||
ref.id = rep->rrsets[i]->id;
|
||||
/*ignore ret: it was in the cache, ref updated */
|
||||
(void)rrset_cache_update(env->rrset_cache, &ref,
|
||||
env->alloc, now);
|
||||
}
|
||||
free(rep);
|
||||
return 1;
|
||||
} else {
|
||||
/* store msg, and rrsets */
|
||||
struct query_info qinf;
|
||||
hashvalue_t h;
|
||||
|
||||
qinf = *msgqinf;
|
||||
qinf.qname = memdup(msgqinf->qname, msgqinf->qname_len);
|
||||
if(!qinf.qname) {
|
||||
reply_info_parsedelete(rep, env->alloc);
|
||||
return 0;
|
||||
}
|
||||
/* fixup flags to be sensible for a reply based on the cache */
|
||||
/* this module means that RA is available. It is an answer QR.
|
||||
* Not AA from cache. Not CD in cache (depends on client bit). */
|
||||
rep->flags |= (BIT_RA | BIT_QR);
|
||||
rep->flags &= ~(BIT_AA | BIT_CD);
|
||||
h = query_info_hash(&qinf);
|
||||
dns_cache_store_msg(env, &qinf, h, rep);
|
||||
/* qname is used inside query_info_entrysetup, and set to
|
||||
* NULL. If it has not been used, free it. free(0) is safe. */
|
||||
free(qinf.qname);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
iter_ns_probability(struct ub_randstate* rnd, int n, int m)
|
||||
{
|
||||
int sel;
|
||||
if(n == m) /* 100% chance */
|
||||
return 1;
|
||||
/* we do not need secure random numbers here, but
|
||||
* we do need it to be threadsafe, so we use this */
|
||||
sel = ub_random(rnd) % m;
|
||||
return (sel < n);
|
||||
}
|
||||
|
||||
/** detect dependency cycle for query and target */
|
||||
static int
|
||||
causes_cycle(struct module_qstate* qstate, uint8_t* name, size_t namelen,
|
||||
uint16_t t, uint16_t c)
|
||||
{
|
||||
struct query_info qinf;
|
||||
qinf.qname = name;
|
||||
qinf.qname_len = namelen;
|
||||
qinf.qtype = t;
|
||||
qinf.qclass = c;
|
||||
return (*qstate->env->detect_cycle)(qstate, &qinf);
|
||||
}
|
||||
|
||||
void
|
||||
iter_mark_cycle_targets(struct module_qstate* qstate, struct delegpt* dp)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
if(ns->resolved)
|
||||
continue;
|
||||
/* see if this ns as target causes dependency cycle */
|
||||
if(causes_cycle(qstate, ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_AAAA, qstate->qinfo.qclass) ||
|
||||
causes_cycle(qstate, ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_A, qstate->qinfo.qclass)) {
|
||||
log_nametypeclass(VERB_DETAIL, "skipping target due "
|
||||
"to dependency cycle (harden-glue: no may "
|
||||
"fix this)", ns->name, LDNS_RR_TYPE_A,
|
||||
qstate->qinfo.qclass);
|
||||
ns->resolved = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* iterator/iter_utils.h - iterative resolver module utility functions.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to assist the iterator module.
|
||||
* Configuration options. Forward zones.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_UTILS_H
|
||||
#define ITERATOR_ITER_UTILS_H
|
||||
struct iter_env;
|
||||
struct config_file;
|
||||
struct module_env;
|
||||
struct delegpt_addr;
|
||||
struct delegpt;
|
||||
struct region;
|
||||
struct msg_parse;
|
||||
struct ub_randstate;
|
||||
struct query_info;
|
||||
struct reply_info;
|
||||
struct module_qstate;
|
||||
|
||||
/**
|
||||
* Process config options and set iterator module state.
|
||||
* Sets default values if no config is found.
|
||||
* @param iter_env: iterator module state.
|
||||
* @param cfg: config options.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Select a valid, nice target to send query to.
|
||||
* Sorting and removing unsuitable targets is combined.
|
||||
*
|
||||
* @param iter_env: iterator module global state, with ip6 enabled and
|
||||
* do-not-query-addresses.
|
||||
* @param env: environment with infra cache (lameness, rtt info).
|
||||
* @param dp: delegation point with result list.
|
||||
* @param name: zone name (for lameness check).
|
||||
* @param namelen: length of name.
|
||||
* @return best target or NULL if no target.
|
||||
* if not null, that target is removed from the result list in the dp.
|
||||
*/
|
||||
struct delegpt_addr* iter_server_selection(struct iter_env* iter_env,
|
||||
struct module_env* env, struct delegpt* dp, uint8_t* name,
|
||||
size_t namelen);
|
||||
|
||||
/**
|
||||
* Allocate dns_msg from parsed msg, in region.
|
||||
* @param pkt: packet.
|
||||
* @param msg: parsed message (cleaned and ready for region allocation).
|
||||
* @param region: region to use for allocation.
|
||||
* @return newly allocated dns_msg, or NULL on memory error.
|
||||
*/
|
||||
struct dns_msg* dns_alloc_msg(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct region* region);
|
||||
|
||||
/**
|
||||
* Copy a dns_msg to this region.
|
||||
* @param from: dns message, also in region.
|
||||
* @param region: region to use for allocation.
|
||||
* @return newly allocated dns_msg, or NULL on memory error.
|
||||
*/
|
||||
struct dns_msg* dns_copy_msg(struct dns_msg* from, struct region* region);
|
||||
|
||||
/**
|
||||
* Allocate a dns_msg with malloc/alloc structure and store in dns cache.
|
||||
* @param env: environment, with alloc structure and dns cache.
|
||||
* @param qinf: query info, the query for which answer is stored.
|
||||
* @param rep: reply in dns_msg from dns_alloc_msg for example.
|
||||
* @param is_referral: If true, then the given message to be stored is a
|
||||
* referral. The cache implementation may use this as a hint.
|
||||
* @return 0 on alloc error (out of memory).
|
||||
*/
|
||||
int iter_dns_store(struct module_env* env, struct query_info* qinf,
|
||||
struct reply_info* rep, int is_referral);
|
||||
|
||||
/**
|
||||
* Select randomly with n/m probability.
|
||||
* For shuffle NS records for address fetching.
|
||||
* @param rnd: random table
|
||||
* @param n: probability.
|
||||
* @param m: divisor for probability.
|
||||
* @return true with n/m probability.
|
||||
*/
|
||||
int iter_ns_probability(struct ub_randstate* rnd, int n, int m);
|
||||
|
||||
/**
|
||||
* Mark targets that result in a dependency cycle as done, so they
|
||||
* will not get selected as targets.
|
||||
* @param qstate: query state.
|
||||
* @param dp: delegpt to mark ns in.
|
||||
*/
|
||||
void iter_mark_cycle_targets(struct module_qstate* qstate, struct delegpt* dp);
|
||||
|
||||
#endif /* ITERATOR_ITER_UTILS_H */
|
||||
+1620
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* iterator/iterator.h - iterative resolver DNS query response module
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains a module that performs recusive iterative DNS query
|
||||
* processing.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITERATOR_H
|
||||
#define ITERATOR_ITERATOR_H
|
||||
#include "services/outbound_list.h"
|
||||
#include "util/data/msgreply.h"
|
||||
struct module_func_block;
|
||||
struct delegpt;
|
||||
struct iter_hints;
|
||||
struct iter_forwards;
|
||||
struct iter_donotq;
|
||||
struct iter_prep_list;
|
||||
|
||||
/** 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 */
|
||||
#define MAX_REFERRAL_COUNT 30
|
||||
/** how nice is a server without further information, in msec
|
||||
* Equals rtt initial timeout value.
|
||||
*/
|
||||
#define UNKNOWN_SERVER_NICENESS 3000
|
||||
/** 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
|
||||
*/
|
||||
#define USEFUL_SERVER_TOP_TIMEOUT 120000
|
||||
|
||||
/**
|
||||
* Global state for the iterator.
|
||||
*/
|
||||
struct iter_env {
|
||||
/**
|
||||
* The hints -- these aren't stored in the cache because they don't
|
||||
* expire. The hints are always used to "prime" the cache. Note
|
||||
* that both root hints and stub zone "hints" are stored in this
|
||||
* data structure.
|
||||
*/
|
||||
struct iter_hints* hints;
|
||||
|
||||
/** A flag to indicate whether or not we have an IPv6 route */
|
||||
int supports_ipv6;
|
||||
|
||||
/** Mapping of forwarding zones to targets. */
|
||||
struct iter_forwards* fwds;
|
||||
|
||||
/** A set of inetaddrs that should never be queried. */
|
||||
struct iter_donotq* donotq;
|
||||
|
||||
/** The maximum dependency depth that this resolver will pursue. */
|
||||
int max_dependency_depth;
|
||||
|
||||
/**
|
||||
* The target fetch policy for each dependency level. This is
|
||||
* described as a simple number (per dependency level):
|
||||
* negative numbers (usually just -1) mean fetch-all,
|
||||
* 0 means only fetch on demand, and
|
||||
* positive numbers mean to fetch at most that many targets.
|
||||
* array of max_dependency_depth+1 size.
|
||||
*/
|
||||
int* target_fetch_policy;
|
||||
};
|
||||
|
||||
/**
|
||||
* State of the iterator for a query.
|
||||
*/
|
||||
enum iter_state {
|
||||
/**
|
||||
* Externally generated queries start at this state. Query restarts are
|
||||
* reset to this state.
|
||||
*/
|
||||
INIT_REQUEST_STATE = 0,
|
||||
|
||||
/**
|
||||
* Root priming events reactivate here, most other events pass
|
||||
* through this naturally as the 2nd part of the INIT_REQUEST_STATE.
|
||||
*/
|
||||
INIT_REQUEST_2_STATE,
|
||||
|
||||
/**
|
||||
* Stub priming events reactivate here, most other events pass
|
||||
* through this naturally as the 3rd part of the INIT_REQUEST_STATE.
|
||||
*/
|
||||
INIT_REQUEST_3_STATE,
|
||||
|
||||
/**
|
||||
* Each time a delegation point changes for a given query or a
|
||||
* query times out and/or wakes up, this state is (re)visited.
|
||||
* This state is reponsible for iterating through a list of
|
||||
* nameserver targets.
|
||||
*/
|
||||
QUERYTARGETS_STATE,
|
||||
|
||||
/**
|
||||
* Responses to queries start at this state. This state handles
|
||||
* the decision tree associated with handling responses.
|
||||
*/
|
||||
QUERY_RESP_STATE,
|
||||
|
||||
/** Responses to priming queries finish at this state. */
|
||||
PRIME_RESP_STATE,
|
||||
|
||||
/** Responses that are to be returned upstream end at this state.
|
||||
* As well as responses to target queries. */
|
||||
FINISHED_STATE
|
||||
};
|
||||
|
||||
/**
|
||||
* Per query state for the iterator module.
|
||||
*/
|
||||
struct iter_qstate {
|
||||
/**
|
||||
* State of the iterator module.
|
||||
* This is the state that event is in or should sent to -- all
|
||||
* requests should start with the INIT_REQUEST_STATE. All
|
||||
* responses should start with QUERY_RESP_STATE. Subsequent
|
||||
* processing of the event will change this state.
|
||||
*/
|
||||
enum iter_state state;
|
||||
|
||||
/**
|
||||
* Final state for the iterator module.
|
||||
* This is the state that responses should be routed to once the
|
||||
* response is final. For externally initiated queries, this
|
||||
* will be FINISHED_STATE, locally initiated queries will have
|
||||
* different final states.
|
||||
*/
|
||||
enum iter_state final_state;
|
||||
|
||||
/**
|
||||
* The depth of this query, this means the depth of recursion.
|
||||
* This address is needed for another query, which is an address
|
||||
* needed for another query, etc. Original client query has depth 0.
|
||||
*/
|
||||
int depth;
|
||||
|
||||
/**
|
||||
* The response
|
||||
*/
|
||||
struct dns_msg* response;
|
||||
|
||||
/**
|
||||
* This is a list of RRsets that must be prepended to the
|
||||
* ANSWER section of a response before being sent upstream.
|
||||
*/
|
||||
struct iter_prep_list* prepend_list;
|
||||
/** Last element of the prepend list */
|
||||
struct iter_prep_list* prepend_last;
|
||||
|
||||
/** query name used for chasing the results. Initially the same as
|
||||
* the state qinfo, but after CNAMEs this will be different.
|
||||
* The query info used to elicit the results needed. */
|
||||
struct query_info qchase;
|
||||
/** query flags to use when chasing the answer (i.e. RD flag) */
|
||||
uint16_t chase_flags;
|
||||
|
||||
/**
|
||||
* This is the current delegation point for an in-progress query. This
|
||||
* object retains state as to which delegation targets need to be
|
||||
* (sub)queried for vs which ones have already been visited.
|
||||
*/
|
||||
struct delegpt* dp;
|
||||
|
||||
/** Current delegation message - returned for non-RD queries */
|
||||
struct dns_msg* deleg_msg;
|
||||
|
||||
/** number of outstanding target sub queries */
|
||||
int num_target_queries;
|
||||
|
||||
/** outstanding direct queries */
|
||||
int num_current_queries;
|
||||
|
||||
/** the number of times this query has been restarted. */
|
||||
int query_restart_count;
|
||||
|
||||
/** the number of times this query as followed a referral. */
|
||||
int referral_count;
|
||||
|
||||
/**
|
||||
* This flag, if true, means that this event is a priming query.
|
||||
* In that case priming stub may be set as well.
|
||||
*/
|
||||
int priming;
|
||||
|
||||
/**
|
||||
* This is flag that, if true, means that this event is
|
||||
* representing a stub priming query. It is meaningless unless
|
||||
* the finalState is the PRIMING_RESP_STATE.
|
||||
*/
|
||||
int priming_stub;
|
||||
|
||||
/**
|
||||
* This is a flag that, if true, means that this query is
|
||||
* for (re)fetching glue from a zone. Since the address should
|
||||
* have been glue, query again to the servers that should have
|
||||
* been returning it as glue.
|
||||
* The delegation point must be set to the one that should *not*
|
||||
* be used when creating the state. A higher one will be attempted.
|
||||
*/
|
||||
int refetch_glue;
|
||||
|
||||
/** list of pending queries to authoritative servers. */
|
||||
struct outbound_list outlist;
|
||||
};
|
||||
|
||||
/**
|
||||
* List of prepend items
|
||||
*/
|
||||
struct iter_prep_list {
|
||||
/** next in list */
|
||||
struct iter_prep_list* next;
|
||||
/** rrset */
|
||||
struct ub_packed_rrset_key* rrset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the iterator function block.
|
||||
* @return: function block with function pointers to iterator methods.
|
||||
*/
|
||||
struct module_func_block* iter_get_funcblock();
|
||||
|
||||
/**
|
||||
* Get iterator state as a string
|
||||
* @param state: to convert
|
||||
* @return constant string that is printable.
|
||||
*/
|
||||
const char* iter_state_to_string(enum iter_state state);
|
||||
|
||||
/**
|
||||
* See if iterator state is a response state
|
||||
* @param s: to inspect
|
||||
* @return true if response state.
|
||||
*/
|
||||
int iter_state_is_responsestate(enum iter_state s);
|
||||
|
||||
#endif /* ITERATOR_ITERATOR_H */
|
||||
Vendored
+587
@@ -0,0 +1,587 @@
|
||||
/*
|
||||
* services/cache/dns.c - Cache services for DNS using msg and rrset caches.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains the DNS cache.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/module.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/region-allocator.h"
|
||||
|
||||
/** store rrsets in the rrset cache.
|
||||
* @param env: module environment with caches.
|
||||
* @param rep: contains list of rrsets to store.
|
||||
* @param now: current time(NULL).
|
||||
*/
|
||||
static void
|
||||
store_rrsets(struct module_env* env, struct reply_info* rep, uint32_t now)
|
||||
{
|
||||
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;
|
||||
if(rrset_cache_update(env->rrset_cache, &rep->ref[i],
|
||||
env->alloc, now)) /* it was in the cache */
|
||||
rep->rrsets[i] = rep->ref[i].key;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
|
||||
hashvalue_t hash, struct reply_info* rep)
|
||||
{
|
||||
struct msgreply_entry* e;
|
||||
uint32_t now = time(NULL), ttl = rep->ttl;
|
||||
size_t i;
|
||||
|
||||
/* store RRsets */
|
||||
for(i=0; i<rep->rrset_count; i++) {
|
||||
rep->ref[i].key = rep->rrsets[i];
|
||||
rep->ref[i].id = rep->rrsets[i]->id;
|
||||
}
|
||||
reply_info_sortref(rep);
|
||||
reply_info_set_ttls(rep, now);
|
||||
store_rrsets(env, rep, now);
|
||||
if(ttl == 0) {
|
||||
/* we do not store the message, but we did store the RRs,
|
||||
* which could be useful for delegation information */
|
||||
verbose(VERB_ALGO, "TTL 0: dropped msg from cache");
|
||||
free(rep);
|
||||
return;
|
||||
}
|
||||
|
||||
/* store msg in the cache */
|
||||
reply_info_sortref(rep);
|
||||
if(!(e = query_info_entrysetup(qinfo, rep, hash))) {
|
||||
log_err("store_msg: malloc failed");
|
||||
return;
|
||||
}
|
||||
slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc);
|
||||
}
|
||||
|
||||
/** allocate rrset in region - no more locks needed */
|
||||
static struct ub_packed_rrset_key*
|
||||
copy_rrset(struct ub_packed_rrset_key* key, struct region* region,
|
||||
uint32_t now)
|
||||
{
|
||||
struct ub_packed_rrset_key* ck = region_alloc(region,
|
||||
sizeof(struct ub_packed_rrset_key));
|
||||
struct packed_rrset_data* d;
|
||||
struct packed_rrset_data* data = (struct packed_rrset_data*)
|
||||
key->entry.data;
|
||||
size_t dsize, i;
|
||||
if(!ck)
|
||||
return NULL;
|
||||
ck->id = key->id;
|
||||
memset(&ck->entry, 0, sizeof(ck->entry));
|
||||
ck->entry.hash = key->entry.hash;
|
||||
ck->entry.key = ck;
|
||||
ck->rk = key->rk;
|
||||
ck->rk.dname = region_alloc_init(region, key->rk.dname,
|
||||
key->rk.dname_len);
|
||||
if(!ck->rk.dname)
|
||||
return NULL;
|
||||
dsize = packed_rrset_sizeof(data);
|
||||
d = (struct packed_rrset_data*)region_alloc_init(region, data, dsize);
|
||||
if(!d)
|
||||
return NULL;
|
||||
ck->entry.data = d;
|
||||
packed_rrset_ptr_fixup(d);
|
||||
/* make TTLs relative - once per rrset */
|
||||
for(i=0; i<d->count + d->rrsig_count; i++)
|
||||
d->rr_ttl[i] -= now;
|
||||
d->ttl -= now;
|
||||
return ck;
|
||||
}
|
||||
|
||||
/** find closest NS or DNAME and returns the rrset (locked) */
|
||||
static struct ub_packed_rrset_key*
|
||||
find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen,
|
||||
uint16_t qclass, uint32_t now, uint16_t searchtype)
|
||||
{
|
||||
struct ub_packed_rrset_key *rrset;
|
||||
uint8_t lablen;
|
||||
|
||||
/* snip off front part of qname until the type is found */
|
||||
while(qnamelen > 0) {
|
||||
if((rrset = rrset_cache_lookup(env->rrset_cache, qname,
|
||||
qnamelen, searchtype, qclass, 0, now, 0)))
|
||||
return rrset;
|
||||
|
||||
/* snip off front label */
|
||||
lablen = *qname;
|
||||
qname += lablen + 1;
|
||||
qnamelen -= lablen + 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** add addr to additional section */
|
||||
static void
|
||||
addr_to_additional(struct ub_packed_rrset_key* rrset, struct region* region,
|
||||
struct dns_msg* msg, uint32_t now)
|
||||
{
|
||||
if((msg->rep->rrsets[msg->rep->rrset_count] =
|
||||
copy_rrset(rrset, region, now))) {
|
||||
msg->rep->ar_numrrsets++;
|
||||
msg->rep->rrset_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/** find and add A and AAAA records for nameservers in delegpt */
|
||||
static int
|
||||
find_add_addrs(struct module_env* env, uint16_t qclass, struct region* region,
|
||||
struct delegpt* dp, uint32_t now, struct dns_msg** msg)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
struct ub_packed_rrset_key* akey;
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
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)) {
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
return 0;
|
||||
}
|
||||
if(msg)
|
||||
addr_to_additional(akey, region, *msg, now);
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
}
|
||||
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)) {
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
return 0;
|
||||
}
|
||||
if(msg)
|
||||
addr_to_additional(akey, region, *msg, now);
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** find and add A and AAAA records for missing nameservers in delegpt */
|
||||
int
|
||||
cache_fill_missing(struct module_env* env, uint16_t qclass,
|
||||
struct region* region, struct delegpt* dp)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
struct ub_packed_rrset_key* akey;
|
||||
uint32_t now = time(NULL);
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
if(ns->resolved)
|
||||
continue;
|
||||
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)) {
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
return 0;
|
||||
}
|
||||
log_nametypeclass(VERB_ALGO, "found in cache",
|
||||
ns->name, LDNS_RR_TYPE_A, qclass);
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
}
|
||||
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)) {
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
return 0;
|
||||
}
|
||||
log_nametypeclass(VERB_ALGO, "found in cache",
|
||||
ns->name, LDNS_RR_TYPE_AAAA, qclass);
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** find and add DS or NSEC to delegation msg */
|
||||
static void
|
||||
find_add_ds(struct module_env* env, struct region* region,
|
||||
struct dns_msg* msg, struct delegpt* dp, uint32_t now)
|
||||
{
|
||||
/* Lookup the DS or NSEC at the delegation point. */
|
||||
struct ub_packed_rrset_key* rrset = rrset_cache_lookup(
|
||||
env->rrset_cache, dp->name, dp->namelen, LDNS_RR_TYPE_DS,
|
||||
msg->qinfo.qclass, 0, now, 0);
|
||||
if(!rrset) {
|
||||
/* NOTE: this won't work for alternate NSEC schemes
|
||||
* (opt-in, NSEC3) */
|
||||
rrset = rrset_cache_lookup(env->rrset_cache, dp->name,
|
||||
dp->namelen, LDNS_RR_TYPE_NSEC, msg->qinfo.qclass,
|
||||
0, now, 0);
|
||||
/* Note: the PACKED_RRSET_NSEC_AT_APEX flag is not used.
|
||||
* since this is a referral, we need the NSEC at the parent
|
||||
* side of the zone cut, not the NSEC at apex side. */
|
||||
}
|
||||
if(rrset) {
|
||||
/* add it to auth section. This is the second rrset. */
|
||||
if((msg->rep->rrsets[msg->rep->rrset_count] =
|
||||
copy_rrset(rrset, region, now))) {
|
||||
msg->rep->ns_numrrsets++;
|
||||
msg->rep->rrset_count++;
|
||||
}
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
}
|
||||
}
|
||||
|
||||
/** create referral message with NS and query */
|
||||
static struct dns_msg*
|
||||
create_msg(uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
struct region* region, struct ub_packed_rrset_key* nskey,
|
||||
struct packed_rrset_data* nsdata, uint32_t now)
|
||||
{
|
||||
struct dns_msg* msg = (struct dns_msg*)region_alloc(region,
|
||||
sizeof(struct dns_msg));
|
||||
if(!msg)
|
||||
return NULL;
|
||||
msg->qinfo.qname = region_alloc_init(region, qname, qnamelen);
|
||||
if(!msg->qinfo.qname)
|
||||
return NULL;
|
||||
msg->qinfo.qname_len = qnamelen;
|
||||
msg->qinfo.qtype = qtype;
|
||||
msg->qinfo.qclass = qclass;
|
||||
/* non-packed reply_info, because it needs to grow the array */
|
||||
msg->rep = (struct reply_info*)region_alloc(region,
|
||||
sizeof(struct reply_info)-sizeof(struct rrset_ref));
|
||||
if(!msg->rep)
|
||||
return NULL;
|
||||
memset(msg->rep, 0,
|
||||
sizeof(struct reply_info)-sizeof(struct rrset_ref));
|
||||
msg->rep->flags = BIT_QR; /* with QR, no AA */
|
||||
msg->rep->qdcount = 1;
|
||||
/* allocate the array to as much as we could need:
|
||||
* NS rrset + DS/NSEC rrset +
|
||||
* A rrset for every NS RR
|
||||
* AAAA rrset for every NS RR
|
||||
*/
|
||||
msg->rep->rrsets = (struct ub_packed_rrset_key**)region_alloc(region,
|
||||
(2 + nsdata->count*2)*sizeof(struct ub_packed_rrset_key*));
|
||||
if(!msg->rep->rrsets)
|
||||
return NULL;
|
||||
msg->rep->rrsets[0] = copy_rrset(nskey, region, now);
|
||||
if(!msg->rep->rrsets[0])
|
||||
return NULL;
|
||||
msg->rep->ns_numrrsets++;
|
||||
msg->rep->rrset_count++;
|
||||
return msg;
|
||||
}
|
||||
|
||||
struct delegpt*
|
||||
dns_cache_find_delegation(struct module_env* env, uint8_t* qname,
|
||||
size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
struct region* region, struct dns_msg** msg)
|
||||
{
|
||||
/* try to find closest NS rrset */
|
||||
struct ub_packed_rrset_key* nskey;
|
||||
struct packed_rrset_data* nsdata;
|
||||
struct delegpt* dp;
|
||||
uint32_t now = (uint32_t)time(NULL);
|
||||
|
||||
nskey = find_closest_of_type(env, qname, qnamelen, qclass, now,
|
||||
LDNS_RR_TYPE_NS);
|
||||
if(!nskey) /* hope the caller has hints to prime or something */
|
||||
return NULL;
|
||||
nsdata = (struct packed_rrset_data*)nskey->entry.data;
|
||||
/* got the NS key, create delegation point */
|
||||
dp = delegpt_create(region);
|
||||
if(!dp || !delegpt_set_name(dp, region, nskey->rk.dname)) {
|
||||
lock_rw_unlock(&nskey->entry.lock);
|
||||
log_err("find_delegation: out of memory");
|
||||
return NULL;
|
||||
}
|
||||
/* create referral message */
|
||||
if(msg) {
|
||||
*msg = create_msg(qname, qnamelen, qtype, qclass, region,
|
||||
nskey, nsdata, now);
|
||||
if(!*msg) {
|
||||
lock_rw_unlock(&nskey->entry.lock);
|
||||
log_err("find_delegation: out of memory");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if(!delegpt_rrset_add_ns(dp, region, nskey))
|
||||
log_err("find_delegation: addns out of memory");
|
||||
lock_rw_unlock(&nskey->entry.lock); /* first unlock before next lookup*/
|
||||
/* find and add DS/NSEC (if any) */
|
||||
if(msg)
|
||||
find_add_ds(env, region, *msg, dp, now);
|
||||
/* find and add A entries */
|
||||
if(!find_add_addrs(env, qclass, region, dp, now, msg))
|
||||
log_err("find_delegation: addrs out of memory");
|
||||
log_info("dns_cache_find_delegation returns delegpt");
|
||||
delegpt_log(dp);
|
||||
return dp;
|
||||
}
|
||||
|
||||
/** allocate dns_msg from query_info and reply_info */
|
||||
static struct dns_msg*
|
||||
gen_dns_msg(struct region* region, struct query_info* q, size_t num)
|
||||
{
|
||||
struct dns_msg* msg = (struct dns_msg*)region_alloc(region,
|
||||
sizeof(struct dns_msg));
|
||||
if(!msg)
|
||||
return NULL;
|
||||
memcpy(&msg->qinfo, q, sizeof(struct query_info));
|
||||
msg->qinfo.qname = region_alloc_init(region, q->qname, q->qname_len);
|
||||
if(!msg->qinfo.qname)
|
||||
return NULL;
|
||||
/* allocate replyinfo struct and rrset key array separately */
|
||||
msg->rep = (struct reply_info*)region_alloc(region,
|
||||
sizeof(struct reply_info) - sizeof(struct rrset_ref));
|
||||
if(!msg->rep)
|
||||
return NULL;
|
||||
msg->rep->rrsets = (struct ub_packed_rrset_key**)region_alloc(region,
|
||||
num * sizeof(struct ub_packed_rrset_key*));
|
||||
if(!msg->rep->rrsets)
|
||||
return NULL;
|
||||
return msg;
|
||||
}
|
||||
|
||||
/** generate dns_msg from cached message */
|
||||
static struct dns_msg*
|
||||
tomsg(struct module_env* env, struct msgreply_entry* e, struct reply_info* r,
|
||||
struct region* region, uint32_t now, struct region* scratch)
|
||||
{
|
||||
struct dns_msg* msg;
|
||||
size_t i;
|
||||
if(now > r->ttl)
|
||||
return NULL;
|
||||
msg = gen_dns_msg(region, &e->key, r->rrset_count);
|
||||
if(!msg)
|
||||
return NULL;
|
||||
msg->rep->flags = r->flags;
|
||||
msg->rep->qdcount = r->qdcount;
|
||||
msg->rep->ttl = r->ttl;
|
||||
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;
|
||||
if(!rrset_array_lock(r->ref, r->rrset_count, now))
|
||||
return NULL;
|
||||
for(i=0; i<msg->rep->rrset_count; i++) {
|
||||
msg->rep->rrsets[i] = copy_rrset(r->rrsets[i], region, now);
|
||||
if(!msg->rep->rrsets[i]) {
|
||||
rrset_array_unlock(r->ref, r->rrset_count);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref,
|
||||
r->rrset_count);
|
||||
return msg;
|
||||
}
|
||||
|
||||
/** synthesize CNAME response from cached CNAME item */
|
||||
static struct dns_msg*
|
||||
cname_msg(struct ub_packed_rrset_key* rrset, struct region* region,
|
||||
uint32_t now, struct query_info* q)
|
||||
{
|
||||
struct dns_msg* msg;
|
||||
struct packed_rrset_data* d = (struct packed_rrset_data*)
|
||||
rrset->entry.data;
|
||||
if(now > d->ttl)
|
||||
return NULL;
|
||||
msg = gen_dns_msg(region, q, 1); /* only the CNAME RRset */
|
||||
if(!msg)
|
||||
return NULL;
|
||||
msg->rep->flags = BIT_QR; /* reply, no AA, no error */
|
||||
msg->rep->qdcount = 1;
|
||||
msg->rep->ttl = d->ttl - now;
|
||||
msg->rep->an_numrrsets = 1;
|
||||
msg->rep->ns_numrrsets = 0;
|
||||
msg->rep->ar_numrrsets = 0;
|
||||
msg->rep->rrset_count = 1;
|
||||
msg->rep->rrsets[0] = copy_rrset(rrset, region, now);
|
||||
if(!msg->rep->rrsets[0]) /* copy CNAME */
|
||||
return NULL;
|
||||
return msg;
|
||||
}
|
||||
|
||||
/** synthesize DNAME+CNAME response from cached DNAME item */
|
||||
static struct dns_msg*
|
||||
synth_dname_msg(struct ub_packed_rrset_key* rrset, struct region* region,
|
||||
uint32_t now, struct query_info* q)
|
||||
{
|
||||
struct dns_msg* msg;
|
||||
struct ub_packed_rrset_key* ck;
|
||||
struct packed_rrset_data* newd, *d = (struct packed_rrset_data*)
|
||||
rrset->entry.data;
|
||||
uint8_t* newname, *dtarg = NULL;
|
||||
size_t newlen, dtarglen;
|
||||
if(now > d->ttl)
|
||||
return NULL;
|
||||
msg = gen_dns_msg(region, q, 2); /* DNAME + CNAME RRset */
|
||||
if(!msg)
|
||||
return NULL;
|
||||
msg->rep->flags = BIT_QR; /* reply, no AA, no error */
|
||||
msg->rep->qdcount = 1;
|
||||
msg->rep->ttl = d->ttl - now;
|
||||
msg->rep->an_numrrsets = 1;
|
||||
msg->rep->ns_numrrsets = 0;
|
||||
msg->rep->ar_numrrsets = 0;
|
||||
msg->rep->rrset_count = 1;
|
||||
msg->rep->rrsets[0] = copy_rrset(rrset, region, now);
|
||||
if(!msg->rep->rrsets[0]) /* copy DNAME */
|
||||
return NULL;
|
||||
/* synth CNAME rrset */
|
||||
get_cname_target(rrset, &dtarg, &dtarglen);
|
||||
if(!dtarg)
|
||||
return NULL;
|
||||
newlen = q->qname_len + dtarglen - rrset->rk.dname_len;
|
||||
if(newlen > LDNS_MAX_DOMAINLEN) {
|
||||
msg->rep->flags |= LDNS_RCODE_YXDOMAIN;
|
||||
return msg;
|
||||
}
|
||||
newname = (uint8_t*)region_alloc(region, newlen);
|
||||
if(!newname)
|
||||
return NULL;
|
||||
/* new name is concatenation of qname front (without DNAME owner)
|
||||
* and DNAME target name */
|
||||
memcpy(newname, q->qname, q->qname_len-rrset->rk.dname_len);
|
||||
memmove(newname+(q->qname_len-rrset->rk.dname_len), dtarg, dtarglen);
|
||||
/* create rest of CNAME rrset */
|
||||
ck = (struct ub_packed_rrset_key*)region_alloc(region,
|
||||
sizeof(struct ub_packed_rrset_key));
|
||||
if(!ck)
|
||||
return NULL;
|
||||
memset(&ck->entry, 0, sizeof(ck->entry));
|
||||
msg->rep->rrsets[1] = ck;
|
||||
ck->entry.key = ck;
|
||||
ck->rk.type = htons(LDNS_RR_TYPE_CNAME);
|
||||
ck->rk.rrset_class = rrset->rk.rrset_class;
|
||||
ck->rk.flags = 0;
|
||||
ck->rk.dname = region_alloc_init(region, q->qname, q->qname_len);
|
||||
if(!ck->rk.dname)
|
||||
return NULL;
|
||||
ck->rk.dname_len = q->qname_len;
|
||||
ck->entry.hash = rrset_key_hash(&ck->rk);
|
||||
newd = (struct packed_rrset_data*)region_alloc(region,
|
||||
sizeof(struct packed_rrset_data) + sizeof(size_t) +
|
||||
sizeof(uint8_t*) + sizeof(uint32_t) + sizeof(uint16_t)
|
||||
+ newlen);
|
||||
if(!newd)
|
||||
return NULL;
|
||||
ck->entry.data = newd;
|
||||
newd->ttl = 0; /* 0 for synthesized CNAME TTL */
|
||||
newd->count = 1;
|
||||
newd->rrsig_count = 0;
|
||||
newd->trust = rrset_trust_ans_noAA;
|
||||
newd->rr_len = (size_t*)((uint8_t*)newd +
|
||||
sizeof(struct packed_rrset_data));
|
||||
newd->rr_len[0] = newlen + sizeof(uint16_t);
|
||||
packed_rrset_ptr_fixup(newd);
|
||||
newd->rr_ttl[0] = newd->ttl;
|
||||
msg->rep->ttl = newd->ttl;
|
||||
ldns_write_uint16(newd->rr_data[0], newlen);
|
||||
memmove(newd->rr_data[0] + sizeof(uint16_t), newname, newlen);
|
||||
msg->rep->an_numrrsets ++;
|
||||
msg->rep->rrset_count ++;
|
||||
return msg;
|
||||
}
|
||||
|
||||
struct dns_msg*
|
||||
dns_cache_lookup(struct module_env* env,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
struct region* region, struct region* scratch)
|
||||
{
|
||||
struct lruhash_entry* e;
|
||||
struct query_info k;
|
||||
hashvalue_t h;
|
||||
uint32_t now = (uint32_t)time(NULL);
|
||||
struct ub_packed_rrset_key* rrset;
|
||||
|
||||
/* lookup first, this has both NXdomains and ANSWER responses */
|
||||
k.qname = qname;
|
||||
k.qname_len = qnamelen;
|
||||
k.qtype = qtype;
|
||||
k.qclass = qclass;
|
||||
h = query_info_hash(&k);
|
||||
e = slabhash_lookup(env->msg_cache, h, &k, 0);
|
||||
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, data, region, now,
|
||||
scratch);
|
||||
if(msg) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return msg;
|
||||
}
|
||||
/* could be msg==NULL; due to TTL or not all rrsets available */
|
||||
lock_rw_unlock(&e->lock);
|
||||
}
|
||||
|
||||
/* see if a DNAME exists. Checked for first, to enforce that DNAMEs
|
||||
* are more important, the CNAME is resynthesized and thus
|
||||
* consistent with the DNAME */
|
||||
if( (rrset=find_closest_of_type(env, qname, qnamelen, qclass, now,
|
||||
LDNS_RR_TYPE_DNAME))) {
|
||||
/* synthesize a DNAME+CNAME message based on this */
|
||||
struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k);
|
||||
if(msg) {
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
return msg;
|
||||
}
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
}
|
||||
|
||||
/* see if we have CNAME for this domain */
|
||||
if( (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen,
|
||||
LDNS_RR_TYPE_CNAME, qclass, 0, now, 0))) {
|
||||
struct dns_msg* msg = cname_msg(rrset, region, now, &k);
|
||||
if(msg) {
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
return msg;
|
||||
}
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
}
|
||||
|
||||
/* construct DS, DNSKEY messages from rrset cache. TODO */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
Vendored
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* services/cache/dns.h - Cache services for DNS using msg and rrset caches.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains the DNS cache.
|
||||
*/
|
||||
|
||||
#ifndef SERVICES_CACHE_DNS_H
|
||||
#define SERVICES_CACHE_DNS_H
|
||||
#include "util/storage/lruhash.h"
|
||||
#include "util/data/msgreply.h"
|
||||
struct module_env;
|
||||
struct query_info;
|
||||
struct reply_info;
|
||||
struct region;
|
||||
struct delegpt;
|
||||
|
||||
/**
|
||||
* Region allocated message reply
|
||||
*/
|
||||
struct dns_msg {
|
||||
/** query info */
|
||||
struct query_info qinfo;
|
||||
/** reply info - ptr to packed repinfo structure */
|
||||
struct reply_info *rep;
|
||||
};
|
||||
|
||||
/**
|
||||
* Store message in the cache. Stores in message cache and rrset cache.
|
||||
* Both qinfo and rep should be malloced and are put in the cache.
|
||||
* They should not be used after this call, as they are then in shared cache.
|
||||
* Does not return errors, they are logged and only lead to less cache.
|
||||
*
|
||||
* @param env: module environment with the DNS cache.
|
||||
* @param qinfo: query info
|
||||
* @param hash: hash over qinfo.
|
||||
* @param rep: reply info, together with qinfo makes up the message.
|
||||
* Adjusts the reply info TTLs to absolute time.
|
||||
*/
|
||||
void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
|
||||
hashvalue_t hash, struct reply_info* rep);
|
||||
|
||||
/**
|
||||
* Find a delegation from the cache.
|
||||
* @param env: module environment with the DNS cache.
|
||||
* @param qname: query name.
|
||||
* @param qnamelen: length of qname.
|
||||
* @param qtype: query type.
|
||||
* @param qclass: query class.
|
||||
* @param region: where to allocate result delegation.
|
||||
* @param msg: if not NULL, delegation message is returned here, synthesized
|
||||
* from the cache.
|
||||
* @return new delegation or NULL on error or if not found in cache.
|
||||
*/
|
||||
struct delegpt* dns_cache_find_delegation(struct module_env* env,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
struct region* region, struct dns_msg** msg);
|
||||
|
||||
/**
|
||||
* Find cached message
|
||||
* @param env: module environment with the DNS cache.
|
||||
* @param qname: query name.
|
||||
* @param qnamelen: length of qname.
|
||||
* @param qtype: query type.
|
||||
* @param qclass: query class.
|
||||
* @param region: where to allocate result.
|
||||
* @param scratch: where to allocate temporary data.
|
||||
* @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.
|
||||
*/
|
||||
struct dns_msg* dns_cache_lookup(struct module_env* env,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
struct region* region, struct region* scratch);
|
||||
|
||||
/**
|
||||
* find and add A and AAAA records for missing nameservers in delegpt
|
||||
* @param env: module environment with rrset cache
|
||||
* @param qclass: which class to look in.
|
||||
* @param region: where to store new dp info.
|
||||
* @param dp: delegation point to fill missing entries.
|
||||
* @return false on alloc failure.
|
||||
*/
|
||||
int cache_fill_missing(struct module_env* env, uint16_t qclass,
|
||||
struct region* region, struct delegpt* dp);
|
||||
|
||||
/** Find covering DNAME */
|
||||
|
||||
#endif /* SERVICES_CACHE_DNS_H */
|
||||
Vendored
+488
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
* services/cache/infra.c - infrastructure cache, server rtt and capabilities
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains the infrastructure cache.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#include "util/storage/lookup3.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/config_file.h"
|
||||
|
||||
/** calculate size for the hashtable, does not count size of lameness,
|
||||
* so the hashtable is a fixed number of items */
|
||||
static size_t
|
||||
infra_host_sizefunc(void* ATTR_UNUSED(k), void* ATTR_UNUSED(d))
|
||||
{
|
||||
return sizeof(struct infra_host_key) + sizeof(struct infra_host_data);
|
||||
}
|
||||
|
||||
/** compare two addresses, returns -1, 0, or +1 */
|
||||
static int
|
||||
infra_host_compfunc(void* key1, void* key2)
|
||||
{
|
||||
struct infra_host_key* k1 = (struct infra_host_key*)key1;
|
||||
struct infra_host_key* k2 = (struct infra_host_key*)key2;
|
||||
return sockaddr_cmp(&k1->addr, k1->addrlen, &k2->addr, k2->addrlen);
|
||||
}
|
||||
|
||||
/** delete key, and destroy the lock */
|
||||
static void
|
||||
infra_host_delkeyfunc(void* k, void* ATTR_UNUSED(arg), int il)
|
||||
{
|
||||
struct infra_host_key* key = (struct infra_host_key*)k;
|
||||
if(!key)
|
||||
return;
|
||||
if(il) { lock_rw_unlock(&key->entry.lock); }
|
||||
lock_rw_destroy(&key->entry.lock);
|
||||
free(key);
|
||||
}
|
||||
|
||||
/** delete data and destroy the lameness hashtable */
|
||||
static void
|
||||
infra_host_deldatafunc(void* d, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct infra_host_data* data = (struct infra_host_data*)d;
|
||||
lruhash_delete(data->lameness);
|
||||
free(data);
|
||||
}
|
||||
|
||||
struct infra_cache*
|
||||
infra_create(struct config_file* cfg)
|
||||
{
|
||||
struct infra_cache* infra = (struct infra_cache*)calloc(1,
|
||||
sizeof(struct infra_cache));
|
||||
/* the size of the lameness tables are not counted */
|
||||
size_t maxmem = cfg->infra_cache_numhosts *
|
||||
(sizeof(struct infra_host_key)+sizeof(struct infra_host_data));
|
||||
infra->hosts = slabhash_create(cfg->infra_cache_slabs,
|
||||
INFRA_HOST_STARTSIZE, maxmem, &infra_host_sizefunc,
|
||||
&infra_host_compfunc, &infra_host_delkeyfunc,
|
||||
&infra_host_deldatafunc, NULL);
|
||||
if(!infra->hosts) {
|
||||
free(infra);
|
||||
return NULL;
|
||||
}
|
||||
infra->host_ttl = cfg->host_ttl;
|
||||
infra->lame_ttl = cfg->lame_ttl;
|
||||
infra->max_lame = cfg->infra_cache_numlame;
|
||||
return infra;
|
||||
}
|
||||
|
||||
void
|
||||
infra_delete(struct infra_cache* infra)
|
||||
{
|
||||
if(!infra)
|
||||
return;
|
||||
slabhash_delete(infra->hosts);
|
||||
free(infra);
|
||||
}
|
||||
|
||||
struct infra_cache*
|
||||
infra_adjust(struct infra_cache* infra, struct config_file* cfg)
|
||||
{
|
||||
size_t maxmem;
|
||||
if(!infra)
|
||||
return infra_create(cfg);
|
||||
infra->host_ttl = cfg->host_ttl;
|
||||
infra->lame_ttl = cfg->lame_ttl;
|
||||
infra->max_lame = cfg->infra_cache_numlame;
|
||||
maxmem = cfg->infra_cache_numhosts *
|
||||
(sizeof(struct infra_host_key)+sizeof(struct infra_host_data));
|
||||
if(maxmem != slabhash_get_size(infra->hosts) ||
|
||||
cfg->infra_cache_slabs != infra->hosts->size) {
|
||||
infra_delete(infra);
|
||||
infra = infra_create(cfg);
|
||||
}
|
||||
return infra;
|
||||
}
|
||||
|
||||
/** calculate the hash value for a host key */
|
||||
static hashvalue_t
|
||||
hash_addr(struct sockaddr_storage* addr, socklen_t addrlen)
|
||||
{
|
||||
hashvalue_t h = 0xab;
|
||||
h = hashlittle(&addrlen, sizeof(addrlen), h);
|
||||
h = hashlittle(addr, addrlen, h);
|
||||
return h;
|
||||
}
|
||||
|
||||
/** lookup version that does not check host ttl (you check it) */
|
||||
static struct lruhash_entry*
|
||||
infra_lookup_host_nottl(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int wr)
|
||||
{
|
||||
struct infra_host_key k;
|
||||
k.addrlen = addrlen;
|
||||
memcpy(&k.addr, addr, addrlen);
|
||||
k.entry.hash = hash_addr(addr, addrlen);
|
||||
k.entry.key = (void*)&k;
|
||||
k.entry.data = NULL;
|
||||
return slabhash_lookup(infra->hosts, k.entry.hash, &k, wr);
|
||||
}
|
||||
|
||||
struct infra_host_data*
|
||||
infra_lookup_host(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int wr,
|
||||
time_t timenow, struct infra_host_key** key)
|
||||
{
|
||||
struct infra_host_data* data;
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, wr);
|
||||
*key = NULL;
|
||||
if(!e)
|
||||
return NULL;
|
||||
/* check TTL */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
if(data->ttl < timenow) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return NULL;
|
||||
}
|
||||
*key = (struct infra_host_key*)e->key;
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and init a new entry for a host
|
||||
* @param infra: infra structure with config parameters.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param tm: time now.
|
||||
* @return: the new entry or NULL on malloc failure.
|
||||
*/
|
||||
static struct lruhash_entry*
|
||||
new_host_entry(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, time_t tm)
|
||||
{
|
||||
struct infra_host_data* data;
|
||||
struct infra_host_key* key = (struct infra_host_key*)malloc(
|
||||
sizeof(struct infra_host_key));
|
||||
if(!key)
|
||||
return NULL;
|
||||
data = (struct infra_host_data*)malloc(
|
||||
sizeof(struct infra_host_data));
|
||||
if(!data) {
|
||||
free(key);
|
||||
return NULL;
|
||||
}
|
||||
lock_rw_init(&key->entry.lock);
|
||||
key->entry.hash = hash_addr(addr, addrlen);
|
||||
key->entry.key = (void*)key;
|
||||
key->entry.data = (void*)data;
|
||||
key->addrlen = addrlen;
|
||||
memcpy(&key->addr, addr, addrlen);
|
||||
data->ttl = tm + infra->host_ttl;
|
||||
data->lameness = NULL;
|
||||
data->edns_version = 0;
|
||||
rtt_init(&data->rtt);
|
||||
return &key->entry;
|
||||
}
|
||||
|
||||
int
|
||||
infra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, time_t timenow, int* edns_vs, int* to)
|
||||
{
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, 0);
|
||||
struct infra_host_data* data;
|
||||
if(e && ((struct infra_host_data*)e->data)->ttl < timenow) {
|
||||
/* it expired, try to reuse existing entry */
|
||||
lock_rw_unlock(&e->lock);
|
||||
e = infra_lookup_host_nottl(infra, addr, addrlen, 1);
|
||||
if(e) {
|
||||
/* if its still there we have a writelock, init */
|
||||
/* re-initialise */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
data->ttl = timenow + infra->host_ttl;
|
||||
rtt_init(&data->rtt);
|
||||
/* do not touch lameness, it may be valid still */
|
||||
data->edns_version = 0;
|
||||
}
|
||||
}
|
||||
if(!e) {
|
||||
/* insert new entry */
|
||||
if(!(e = new_host_entry(infra, addr, addrlen, timenow)))
|
||||
return 0;
|
||||
data = (struct infra_host_data*)e->data;
|
||||
*to = rtt_timeout(&data->rtt);
|
||||
*edns_vs = data->edns_version;
|
||||
slabhash_insert(infra->hosts, e->hash, e, data, NULL);
|
||||
return 1;
|
||||
}
|
||||
/* use existing entry */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
*to = rtt_timeout(&data->rtt);
|
||||
*edns_vs = data->edns_version;
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** hash lameness key */
|
||||
static hashvalue_t
|
||||
hash_lameness(uint8_t* name, size_t namelen)
|
||||
{
|
||||
return hashlittle(name, namelen, 0xab);
|
||||
}
|
||||
|
||||
int
|
||||
infra_lookup_lame(struct infra_host_data* host,
|
||||
uint8_t* name, size_t namelen, time_t timenow)
|
||||
{
|
||||
struct lruhash_entry* e;
|
||||
struct infra_lame_key k;
|
||||
struct infra_lame_data *d;
|
||||
if(!host->lameness)
|
||||
return 0;
|
||||
k.entry.hash = hash_lameness(name, namelen);
|
||||
k.zonename = name;
|
||||
k.namelen = namelen;
|
||||
k.entry.key = (void*)&k;
|
||||
k.entry.data = NULL;
|
||||
e = lruhash_lookup(host->lameness, k.entry.hash, &k, 0);
|
||||
if(!e)
|
||||
return 0;
|
||||
d = (struct infra_lame_data*)e->data;
|
||||
if(d->ttl < timenow) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 0;
|
||||
}
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** calculate size, which is fixed, zonename does not count so that
|
||||
* a fixed number of items is stored */
|
||||
static size_t
|
||||
infra_lame_sizefunc(void* ATTR_UNUSED(k), void* ATTR_UNUSED(d))
|
||||
{
|
||||
return sizeof(struct infra_lame_key)+sizeof(struct infra_lame_data);
|
||||
}
|
||||
|
||||
/** compare zone names, returns -1, 0, +1 */
|
||||
static int
|
||||
infra_lame_compfunc(void* key1, void* key2)
|
||||
{
|
||||
struct infra_lame_key* k1 = (struct infra_lame_key*)key1;
|
||||
struct infra_lame_key* k2 = (struct infra_lame_key*)key2;
|
||||
if(k1->namelen != k2->namelen) {
|
||||
if(k1->namelen < k2->namelen)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
return query_dname_compare(k1->zonename, k2->zonename);
|
||||
}
|
||||
|
||||
/** free key, lock and zonename */
|
||||
static void
|
||||
infra_lame_delkeyfunc(void* k, void* ATTR_UNUSED(arg), int il)
|
||||
{
|
||||
struct infra_lame_key* key = (struct infra_lame_key*)k;
|
||||
if(!key)
|
||||
return;
|
||||
if(il) { lock_rw_unlock(&key->entry.lock); }
|
||||
lock_rw_destroy(&key->entry.lock);
|
||||
free(key->zonename);
|
||||
free(key);
|
||||
}
|
||||
|
||||
/** free the lameness data */
|
||||
static void
|
||||
infra_lame_deldatafunc(void* d, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
if(!d)
|
||||
return;
|
||||
free(d);
|
||||
}
|
||||
|
||||
int
|
||||
infra_set_lame(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* name, size_t namelen, time_t timenow)
|
||||
{
|
||||
struct infra_host_data* data;
|
||||
struct lruhash_entry* e;
|
||||
int needtoinsert = 0;
|
||||
struct infra_lame_key* k;
|
||||
struct infra_lame_data* d;
|
||||
/* allocate at start, easier cleanup (no locks held) */
|
||||
k = (struct infra_lame_key*)malloc(sizeof(*k));
|
||||
if(!k) {
|
||||
log_err("set_lame: malloc failure");
|
||||
return 0;
|
||||
}
|
||||
d = (struct infra_lame_data*)malloc(sizeof(*d));
|
||||
if(!d) {
|
||||
free(k);
|
||||
log_err("set_lame: malloc failure");
|
||||
return 0;
|
||||
}
|
||||
k->zonename = memdup(name, namelen);
|
||||
if(!k->zonename) {
|
||||
free(d);
|
||||
free(k);
|
||||
log_err("set_lame: malloc failure");
|
||||
return 0;
|
||||
}
|
||||
lock_rw_init(&k->entry.lock);
|
||||
k->entry.hash = hash_lameness(name, namelen);
|
||||
k->entry.key = (void*)k;
|
||||
k->entry.data = (void*)d;
|
||||
d->ttl = timenow + infra->lame_ttl;
|
||||
k->namelen = namelen;
|
||||
e = infra_lookup_host_nottl(infra, addr, addrlen, 1);
|
||||
if(!e) {
|
||||
/* insert it */
|
||||
if(!(e = new_host_entry(infra, addr, addrlen, timenow))) {
|
||||
free(k->zonename);
|
||||
free(k);
|
||||
free(d);
|
||||
log_err("set_lame: malloc failure");
|
||||
return 0;
|
||||
}
|
||||
needtoinsert = 1;
|
||||
}
|
||||
/* got an entry, now set the zone lame */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
if(!data->lameness) {
|
||||
/* create hash table if not there already */
|
||||
data->lameness = lruhash_create(INFRA_LAME_STARTSIZE,
|
||||
infra->max_lame*(sizeof(struct infra_lame_key)+
|
||||
sizeof(struct infra_lame_data)), infra_lame_sizefunc,
|
||||
infra_lame_compfunc, infra_lame_delkeyfunc,
|
||||
infra_lame_deldatafunc, NULL);
|
||||
if(!data->lameness) {
|
||||
log_err("set_lame: malloc failure");
|
||||
if(needtoinsert) slabhash_insert(infra->hosts,
|
||||
e->hash, e, e->data, NULL);
|
||||
else { lock_rw_unlock(&e->lock); }
|
||||
free(k->zonename);
|
||||
free(k);
|
||||
free(d);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* inserts new entry, or updates TTL of older entry */
|
||||
lruhash_insert(data->lameness, k->entry.hash, &k->entry, d, NULL);
|
||||
|
||||
if(needtoinsert)
|
||||
slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
|
||||
else { lock_rw_unlock(&e->lock); }
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
infra_rtt_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int roundtrip, time_t timenow)
|
||||
{
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, 1);
|
||||
struct infra_host_data* data;
|
||||
int needtoinsert = 0;
|
||||
if(!e) {
|
||||
if(!(e = new_host_entry(infra, addr, addrlen, timenow)))
|
||||
return 0;
|
||||
needtoinsert = 1;
|
||||
}
|
||||
/* have an entry, update the rtt, and the ttl */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
data->ttl = timenow + infra->host_ttl;
|
||||
if(roundtrip == -1)
|
||||
rtt_lost(&data->rtt);
|
||||
else rtt_update(&data->rtt, roundtrip);
|
||||
|
||||
if(needtoinsert)
|
||||
slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
|
||||
else { lock_rw_unlock(&e->lock); }
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
infra_edns_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int edns_version, time_t timenow)
|
||||
{
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, 1);
|
||||
struct infra_host_data* data;
|
||||
int needtoinsert = 0;
|
||||
if(!e) {
|
||||
if(!(e = new_host_entry(infra, addr, addrlen, timenow)))
|
||||
return 0;
|
||||
needtoinsert = 1;
|
||||
}
|
||||
/* have an entry, update the rtt, and the ttl */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
data->ttl = timenow + infra->host_ttl;
|
||||
data->edns_version = edns_version;
|
||||
|
||||
if(needtoinsert)
|
||||
slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
|
||||
else { lock_rw_unlock(&e->lock); }
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
infra_get_lame_rtt(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* name, size_t namelen, int* lame, int* rtt, time_t timenow)
|
||||
{
|
||||
struct infra_host_data* host;
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, 0);
|
||||
if(!e)
|
||||
return 0;
|
||||
host = (struct infra_host_data*)e->data;
|
||||
*rtt = rtt_unclamped(&host->rtt);
|
||||
/* check lameness first, if so, ttl on host does not matter anymore */
|
||||
if(infra_lookup_lame(host, name, namelen, timenow)) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
*lame = 1;
|
||||
return 1;
|
||||
}
|
||||
*lame = 0;
|
||||
if(timenow > host->ttl) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 0;
|
||||
}
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 1;
|
||||
}
|
||||
Vendored
+238
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* services/cache/infra.h - infrastructure cache, server rtt and capabilities
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains the infrastructure cache.
|
||||
*/
|
||||
|
||||
#ifndef SERVICES_CACHE_INFRA_H
|
||||
#define SERVICES_CACHE_INFRA_H
|
||||
#include "util/storage/lruhash.h"
|
||||
#include "util/rtt.h"
|
||||
struct slabhash;
|
||||
struct config_file;
|
||||
|
||||
/**
|
||||
* Host information kept for every server.
|
||||
*/
|
||||
struct infra_host_key {
|
||||
/** the host address. */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr. */
|
||||
socklen_t addrlen;
|
||||
/** hash table entry, data of type infra_host_data. */
|
||||
struct lruhash_entry entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* Host information encompasses host capabilities and retransmission timeouts.
|
||||
*/
|
||||
struct infra_host_data {
|
||||
/** TTL value for this entry. absolute time. */
|
||||
time_t ttl;
|
||||
/** round trip times for timeout calculation */
|
||||
struct rtt_info rtt;
|
||||
/** Names of the zones that are lame. NULL=no lame zones. */
|
||||
struct lruhash* lameness;
|
||||
/** edns version that the host supports, -1 means no EDNS */
|
||||
int edns_version;
|
||||
};
|
||||
|
||||
/**
|
||||
* Lameness information, per host, per zone.
|
||||
*/
|
||||
struct infra_lame_key {
|
||||
/** key is zone name in wireformat */
|
||||
uint8_t* zonename;
|
||||
/** length of zonename */
|
||||
size_t namelen;
|
||||
/** lruhash entry */
|
||||
struct lruhash_entry entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* Lameness information. Expires.
|
||||
* This host is lame because it is in the cache.
|
||||
*/
|
||||
struct infra_lame_data {
|
||||
/** TTL of this entry. absolute time. */
|
||||
time_t ttl;
|
||||
};
|
||||
|
||||
/**
|
||||
* Infra cache
|
||||
*/
|
||||
struct infra_cache {
|
||||
/** The hash table with hosts */
|
||||
struct slabhash* hosts;
|
||||
/** TTL value for host information, in seconds */
|
||||
int host_ttl;
|
||||
/** TTL for Lameness information, in seconds */
|
||||
int lame_ttl;
|
||||
/** infra lame cache max memory per host, for this many entries */
|
||||
size_t max_lame;
|
||||
};
|
||||
|
||||
/** infra host cache default hash lookup size */
|
||||
#define INFRA_HOST_STARTSIZE 32
|
||||
/** infra lame cache default hash lookup size */
|
||||
#define INFRA_LAME_STARTSIZE 2
|
||||
|
||||
/**
|
||||
* Create infra cache.
|
||||
* @param cfg: config parameters or NULL for defaults.
|
||||
* @return: new infra cache, or NULL.
|
||||
*/
|
||||
struct infra_cache* infra_create(struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Delete infra cache.
|
||||
* @param infra: infrastructure cache to delete.
|
||||
*/
|
||||
void infra_delete(struct infra_cache* infra);
|
||||
|
||||
/**
|
||||
* Adjust infra cache to use updated configuration settings.
|
||||
* This may clean the cache. Operates a bit like realloc.
|
||||
* There may be no threading or use by other threads.
|
||||
* @param infra: existing cache. If NULL a new infra cache is returned.
|
||||
* @param cfg: config options.
|
||||
* @return the new infra cache pointer or NULL on error.
|
||||
*/
|
||||
struct infra_cache* infra_adjust(struct infra_cache* infra,
|
||||
struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Lookup host data
|
||||
* @param infra: infrastructure cache.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param wr: set to true to get a writelock on the entry.
|
||||
* @param timenow: what time it is now.
|
||||
* @param key: the key for the host, returned so caller can unlock when done.
|
||||
* @return: host data or NULL if not found or expired.
|
||||
*/
|
||||
struct infra_host_data* infra_lookup_host(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int wr,
|
||||
time_t timenow, struct infra_host_key** key);
|
||||
|
||||
/**
|
||||
* Find host information to send a packet. Creates new entry if not found.
|
||||
* Lameness is empty. EDNS is 0 (try with first), and rtt is returned for
|
||||
* the first message to it.
|
||||
* @param infra: infrastructure cache.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param timenow: what time it is now.
|
||||
* @param edns_vs: edns version it supports, is returned.
|
||||
* @param to: timeout to use, is returned.
|
||||
* @return: 0 on error.
|
||||
*/
|
||||
int infra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, time_t timenow, int* edns_vs, int* to);
|
||||
|
||||
/**
|
||||
* Check for lameness of this server for a particular zone.
|
||||
* You must have a lock on the host structure.
|
||||
* @param host: infrastructure cache data for the host. Caller holds lock.
|
||||
* @param name: domain name of zone apex.
|
||||
* @param namelen: length of domain name.
|
||||
* @param timenow: what time it is now.
|
||||
* @return: 0 if not lame or unknown or timed out, true if lame.
|
||||
*/
|
||||
int infra_lookup_lame(struct infra_host_data* host,
|
||||
uint8_t* name, size_t namelen, time_t timenow);
|
||||
|
||||
/**
|
||||
* Set a host to be lame for the given zone.
|
||||
* @param infra: infrastructure cache.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param name: domain name of zone apex.
|
||||
* @param namelen: length of domain name.
|
||||
* @param timenow: what time it is now.
|
||||
* @return: 0 on error.
|
||||
*/
|
||||
int infra_set_lame(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* name, size_t namelen, time_t timenow);
|
||||
|
||||
/**
|
||||
* Update rtt information for the host.
|
||||
* @param infra: infrastructure cache.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param roundtrip: estimate of roundtrip time in milliseconds or -1 for
|
||||
* timeout.
|
||||
* @param timenow: what time it is now.
|
||||
* @return: 0 on error.
|
||||
*/
|
||||
int infra_rtt_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int roundtrip, time_t timenow);
|
||||
|
||||
/**
|
||||
* Update edns information for the host.
|
||||
* @param infra: infrastructure cache.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param edns_version: the version that it publishes.
|
||||
* @param timenow: what time it is now.
|
||||
* @return: 0 on error.
|
||||
*/
|
||||
int infra_edns_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int edns_version, time_t timenow);
|
||||
|
||||
/**
|
||||
* Get Lameness information and average RTT if host is in the cache.
|
||||
* @param infra: infrastructure cache.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param name: zone name.
|
||||
* @param namelen: zone name length.
|
||||
* @param lame: if function returns true, this returns lameness of the zone.
|
||||
* @param rtt: if function returns true, this returns avg rtt of the server.
|
||||
* The rtt value is unclamped and reflects recent timeouts.
|
||||
* @param timenow: what time it is now.
|
||||
* @return if found in cache, or false if not (or TTL bad).
|
||||
*/
|
||||
int infra_get_lame_rtt(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* name, size_t namelen, int* lame, int* rtt, time_t timenow);
|
||||
|
||||
#endif /* SERVICES_CACHE_INFRA_H */
|
||||
Vendored
+267
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* services/cache/rrset.c - Resource record set cache.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains the rrset cache.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/region-allocator.h"
|
||||
|
||||
struct rrset_cache* rrset_cache_create(struct config_file* cfg,
|
||||
struct alloc_cache* alloc)
|
||||
{
|
||||
size_t slabs = (cfg?cfg->rrset_cache_slabs:HASH_DEFAULT_SLABS);
|
||||
size_t startarray = HASH_DEFAULT_STARTARRAY;
|
||||
size_t maxmem = (cfg?cfg->rrset_cache_size:HASH_DEFAULT_MAXMEM);
|
||||
|
||||
struct rrset_cache *r = (struct rrset_cache*)slabhash_create(slabs,
|
||||
startarray, maxmem, ub_rrset_sizefunc, ub_rrset_compare,
|
||||
ub_rrset_key_delete, rrset_data_delete, alloc);
|
||||
return r;
|
||||
}
|
||||
|
||||
void rrset_cache_delete(struct rrset_cache* r)
|
||||
{
|
||||
if(!r)
|
||||
return;
|
||||
slabhash_delete(&r->table);
|
||||
}
|
||||
|
||||
struct rrset_cache* rrset_cache_adjust(struct rrset_cache *r,
|
||||
struct config_file* cfg, struct alloc_cache* alloc)
|
||||
{
|
||||
if(!r || !cfg || cfg->rrset_cache_slabs != r->table.size ||
|
||||
cfg->rrset_cache_size != slabhash_get_size(&r->table))
|
||||
{
|
||||
rrset_cache_delete(r);
|
||||
r = rrset_cache_create(cfg, alloc);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key,
|
||||
hashvalue_t hash, rrset_id_t id)
|
||||
{
|
||||
struct lruhash* table = slabhash_gettable(&r->table, hash);
|
||||
/*
|
||||
* This leads to locking problems, deadlocks, if the caller is
|
||||
* holding any other rrset lock.
|
||||
* Because a lookup through the hashtable does:
|
||||
* tablelock -> entrylock (for that entry caller holds)
|
||||
* And this would do
|
||||
* entrylock(already held) -> tablelock
|
||||
* And if two threads do this, it results in deadlock.
|
||||
* So, the caller must not hold entrylock.
|
||||
*/
|
||||
lock_quick_lock(&table->lock);
|
||||
/* we have locked the hash table, the item can still be deleted.
|
||||
* because it could already have been reclaimed, but not yet set id=0.
|
||||
* This is because some lruhash routines have lazy deletion.
|
||||
* so, we must acquire a lock on the item to verify the id != 0.
|
||||
* also, with hash not changed, we are using the right slab.
|
||||
*/
|
||||
lock_rw_rdlock(&key->entry.lock);
|
||||
if(key->id == id && key->entry.hash == hash) {
|
||||
lru_touch(table, &key->entry);
|
||||
}
|
||||
lock_rw_unlock(&key->entry.lock);
|
||||
lock_quick_unlock(&table->lock);
|
||||
}
|
||||
|
||||
/** see if rrset needs to be updated in the cache */
|
||||
static int
|
||||
need_to_update_rrset(void* nd, void* cd, uint32_t timenow)
|
||||
{
|
||||
struct packed_rrset_data* newd = (struct packed_rrset_data*)nd;
|
||||
struct packed_rrset_data* cached = (struct packed_rrset_data*)cd;
|
||||
/* o if current RRset is more trustworthy - insert it */
|
||||
if( newd->trust > cached->trust )
|
||||
return 1;
|
||||
/* o item in cache has expired */
|
||||
if( cached->ttl < timenow )
|
||||
return 1;
|
||||
/* o same trust, but different in data - insert it */
|
||||
if( newd->trust == cached->trust &&
|
||||
!rrsetdata_equal(newd, cached))
|
||||
return 1;
|
||||
/* o see if TTL is better than TTL in cache. */
|
||||
/* if so, see if rrset+rdata is the same */
|
||||
/* if so, update TTL in cache, even if trust is worse. */
|
||||
if( newd->ttl > cached->ttl &&
|
||||
rrsetdata_equal(newd, cached)) {
|
||||
/* since all else is the same, use the best trust value */
|
||||
if(newd->trust < cached->trust)
|
||||
newd->trust = cached->trust;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref,
|
||||
struct alloc_cache* alloc, uint32_t timenow)
|
||||
{
|
||||
struct lruhash_entry* e;
|
||||
struct ub_packed_rrset_key* k = ref->key;
|
||||
hashvalue_t h = k->entry.hash;
|
||||
/* looks up item with a readlock - no editing! */
|
||||
if((e=slabhash_lookup(&r->table, h, k, 0)) != 0) {
|
||||
/* return id and key as they will be used in the cache
|
||||
* since the lruhash_insert, if item already exists, deallocs
|
||||
* the passed key in favor of the already stored key.
|
||||
* because of the small gap (see below) this key ptr and id
|
||||
* may prove later to be already deleted, which is no problem
|
||||
* as it only makes a cache miss.
|
||||
*/
|
||||
ref->key = (struct ub_packed_rrset_key*)e->key;
|
||||
ref->id = ref->key->id;
|
||||
if(!need_to_update_rrset(k->entry.data, e->data, timenow)) {
|
||||
/* cache is superior, return that value */
|
||||
lock_rw_unlock(&e->lock);
|
||||
ub_packed_rrset_parsedelete(k, alloc);
|
||||
return 1;
|
||||
}
|
||||
lock_rw_unlock(&e->lock);
|
||||
/* Go on and insert the passed item.
|
||||
* small gap here, where entry is not locked.
|
||||
* possibly entry is updated with something else.
|
||||
* we then overwrite that with our data.
|
||||
* this is just too bad, its cache anyway. */
|
||||
/* use insert to update entry to manage lruhash
|
||||
* cache size values nicely. */
|
||||
}
|
||||
slabhash_insert(&r->table, h, &k->entry, k->entry.data, alloc);
|
||||
if(e)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ub_packed_rrset_key*
|
||||
rrset_cache_lookup(struct rrset_cache* r, uint8_t* qname, size_t qnamelen,
|
||||
uint16_t qtype, uint16_t qclass, uint32_t flags, uint32_t timenow,
|
||||
int wr)
|
||||
{
|
||||
struct lruhash_entry* e;
|
||||
struct ub_packed_rrset_key key;
|
||||
|
||||
key.entry.key = &key;
|
||||
key.entry.data = NULL;
|
||||
key.rk.dname = qname;
|
||||
key.rk.dname_len = qnamelen;
|
||||
key.rk.type = htons(qtype);
|
||||
key.rk.rrset_class = htons(qclass);
|
||||
key.rk.flags = flags;
|
||||
|
||||
key.entry.hash = rrset_key_hash(&key.rk);
|
||||
|
||||
if((e = slabhash_lookup(&r->table, key.entry.hash, &key, wr))) {
|
||||
/* check TTL */
|
||||
struct packed_rrset_data* data =
|
||||
(struct packed_rrset_data*)e->data;
|
||||
if(timenow > data->ttl) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return NULL;
|
||||
}
|
||||
/* we're done */
|
||||
return (struct ub_packed_rrset_key*)e->key;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
rrset_array_lock(struct rrset_ref* ref, size_t count, uint32_t timenow)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<count; i++) {
|
||||
if(i>0 && ref[i].key == ref[i-1].key)
|
||||
continue; /* only lock items once */
|
||||
lock_rw_rdlock(&ref[i].key->entry.lock);
|
||||
if(ref[i].id != ref[i].key->id || timenow >
|
||||
((struct packed_rrset_data*)(ref[i].key->entry.data))
|
||||
->ttl) {
|
||||
/* failure! rollback our readlocks */
|
||||
rrset_array_unlock(ref, i+1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
rrset_array_unlock(struct rrset_ref* ref, size_t count)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<count; i++) {
|
||||
if(i>0 && ref[i].key == ref[i-1].key)
|
||||
continue; /* only unlock items once */
|
||||
lock_rw_unlock(&ref[i].key->entry.lock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rrset_array_unlock_touch(struct rrset_cache* r, struct region* scratch,
|
||||
struct rrset_ref* ref, size_t count)
|
||||
{
|
||||
hashvalue_t* h;
|
||||
size_t i;
|
||||
if(!(h = (hashvalue_t*)region_alloc(scratch,
|
||||
sizeof(hashvalue_t)*count)))
|
||||
log_warn("rrset LRU: memory allocation failed");
|
||||
else /* store hash values */
|
||||
for(i=0; i<count; i++)
|
||||
h[i] = ref[i].key->entry.hash;
|
||||
/* unlock */
|
||||
for(i=0; i<count; i++) {
|
||||
if(i>0 && ref[i].key == ref[i-1].key)
|
||||
continue; /* only unlock items once */
|
||||
lock_rw_unlock(&ref[i].key->entry.lock);
|
||||
}
|
||||
if(h) {
|
||||
/* LRU touch, with no rrset locks held */
|
||||
for(i=0; i<count; i++) {
|
||||
if(i>0 && ref[i].key == ref[i-1].key)
|
||||
continue; /* only touch items once */
|
||||
rrset_cache_touch(r, ref[i].key, h[i], ref[i].id);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+187
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* services/cache/rrset.h - Resource record set cache.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains the rrset cache.
|
||||
*/
|
||||
|
||||
#ifndef SERVICES_CACHE_RRSET_H
|
||||
#define SERVICES_CACHE_RRSET_H
|
||||
#include "util/storage/lruhash.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
struct config_file;
|
||||
struct alloc_cache;
|
||||
struct rrset_ref;
|
||||
struct region;
|
||||
|
||||
/**
|
||||
* The rrset cache
|
||||
* Thin wrapper around hashtable, like a typedef.
|
||||
*/
|
||||
struct rrset_cache {
|
||||
/** uses partitioned hash table */
|
||||
struct slabhash table;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create rrset cache
|
||||
* @param cfg: config settings or NULL for defaults.
|
||||
* @param alloc: initial default rrset key allocation.
|
||||
* @return: NULL on error.
|
||||
*/
|
||||
struct rrset_cache* rrset_cache_create(struct config_file* cfg,
|
||||
struct alloc_cache* alloc);
|
||||
|
||||
/**
|
||||
* Delete rrset cache
|
||||
* @param r: rrset cache to delete.
|
||||
*/
|
||||
void rrset_cache_delete(struct rrset_cache* r);
|
||||
|
||||
/**
|
||||
* Adjust settings of the cache to settings from the config file.
|
||||
* May purge the cache. May recreate the cache.
|
||||
* There may be no threading or use by other threads.
|
||||
* @param r: rrset cache to adjust (like realloc).
|
||||
* @param cfg: config settings or NULL for defaults.
|
||||
* @param alloc: initial default rrset key allocation.
|
||||
* @return 0 on error, or new rrset cache pointer on success.
|
||||
*/
|
||||
struct rrset_cache* rrset_cache_adjust(struct rrset_cache* r,
|
||||
struct config_file* cfg, struct alloc_cache* alloc);
|
||||
|
||||
/**
|
||||
* Touch rrset, with given pointer and id.
|
||||
* Caller may not hold a lock on ANY rrset, this could give deadlock.
|
||||
*
|
||||
* This routine is faster than a hashtable lookup:
|
||||
* o no bin_lock is acquired.
|
||||
* o no walk through the bin-overflow-list.
|
||||
* o no comparison of the entry key to find it.
|
||||
*
|
||||
* @param r: rrset cache.
|
||||
* @param key: rrset key. Marked recently used (if it was not deleted
|
||||
* before the lock is acquired, in that case nothing happens).
|
||||
* @param hash: hash value of the item. Please read it from the key when
|
||||
* you have it locked. Used to find slab from slabhash.
|
||||
* @param id: used to check that the item is unchanged and not deleted.
|
||||
*/
|
||||
void rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key,
|
||||
hashvalue_t hash, rrset_id_t id);
|
||||
|
||||
/**
|
||||
* Update an rrset in the rrset cache. Stores the information for later use.
|
||||
* Will lookup if the rrset is in the cache and perform an update if necessary.
|
||||
* If the item was present, and superior, references are returned to that.
|
||||
* The passed item is then deallocated with rrset_parsedelete.
|
||||
*
|
||||
* A superior rrset is:
|
||||
* o rrset with better trust value.
|
||||
* o same trust value, different rdata, newly passed rrset is inserted.
|
||||
* If rdata is the same, TTL in the cache is updated.
|
||||
*
|
||||
* @param r: the rrset cache.
|
||||
* @param ref: reference (ptr and id) to the rrset. Pass reference setup for
|
||||
* the new rrset. The reference may be changed if the cached rrset is
|
||||
* superior.
|
||||
* Before calling the rrset is presumed newly allocated and changeable.
|
||||
* Afer calling you do not hold a lock, and the rrset is inserted in
|
||||
* the hashtable so you need a lock to change it.
|
||||
* @param alloc: how to allocate (and deallocate) the special rrset key.
|
||||
* @param timenow: current time (to see if ttl in cache is expired).
|
||||
* @return: true if the passed reference is updated, false if it is unchanged.
|
||||
*/
|
||||
int rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref,
|
||||
struct alloc_cache* alloc, uint32_t timenow);
|
||||
|
||||
/**
|
||||
* Lookup rrset. You obtain read/write lock. You must unlock before lookup
|
||||
* anything of else.
|
||||
* @param r: the rrset cache.
|
||||
* @param qname: name of rrset to lookup.
|
||||
* @param qnamelen: length of name of rrset to lookup.
|
||||
* @param qtype: type of rrset to lookup (host order).
|
||||
* @param qclass: class of rrset to lookup (host order).
|
||||
* @param flags: rrset flags, or 0.
|
||||
* @param timenow: used to compare with TTL.
|
||||
* @param wr: set true to get writelock.
|
||||
* @return packed rrset key pointer. Remember to unlock the key.entry.lock.
|
||||
* or NULL if could not be found or it was timed out.
|
||||
*/
|
||||
struct ub_packed_rrset_key* rrset_cache_lookup(struct rrset_cache* r,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
uint32_t flags, uint32_t timenow, int wr);
|
||||
|
||||
/**
|
||||
* Obtain readlock on a (sorted) list of rrset references.
|
||||
* Checks TTLs and IDs of the rrsets and rollbacks locking if not Ok.
|
||||
* @param ref: array of rrset references (key pointer and ID value).
|
||||
* duplicate references are allowed and handled.
|
||||
* @param count: size of array.
|
||||
* @param timenow: used to compare with TTL.
|
||||
* @return true on success, false on a failure, which can be that some
|
||||
* RRsets have timed out, or that they do not exist any more, the
|
||||
* RRsets have been purged from the cache.
|
||||
* If true, you hold readlocks on all the ref items.
|
||||
*/
|
||||
int rrset_array_lock(struct rrset_ref* ref, size_t count, uint32_t timenow);
|
||||
|
||||
/**
|
||||
* Unlock array (sorted) of rrset references.
|
||||
* @param ref: array of rrset references (key pointer and ID value).
|
||||
* duplicate references are allowed and handled.
|
||||
* @param count: size of array.
|
||||
*/
|
||||
void rrset_array_unlock(struct rrset_ref* ref, size_t count);
|
||||
|
||||
/**
|
||||
* Unlock array (sorted) of rrset references and at the same time
|
||||
* touch LRU on the rrsets. It needs the scratch region for temporary
|
||||
* storage as it uses the initial locks to obtain hash values.
|
||||
* @param r: the rrset cache. In this cache LRU is updated.
|
||||
* @param scratch: region for temporary storage of hash values.
|
||||
* if memory allocation fails, the lru touch fails silently,
|
||||
* but locks are released. memory errors are logged.
|
||||
* @param ref: array of rrset references (key pointer and ID value).
|
||||
* duplicate references are allowed and handled.
|
||||
* @param count: size of array.
|
||||
*/
|
||||
void rrset_array_unlock_touch(struct rrset_cache* r, struct region* scratch,
|
||||
struct rrset_ref* ref, size_t count);
|
||||
|
||||
#endif /* SERVICES_CACHE_RRSET_H */
|
||||
@@ -452,3 +452,16 @@ void listening_ports_free(struct listen_port* list)
|
||||
list = nx;
|
||||
}
|
||||
}
|
||||
|
||||
size_t listen_get_mem(struct listen_dnsport* listen)
|
||||
{
|
||||
size_t s = sizeof(*listen) + sizeof(*listen->base) +
|
||||
sizeof(*listen->udp_buff) +
|
||||
ldns_buffer_capacity(listen->udp_buff);
|
||||
struct listen_list* p;
|
||||
for(p = listen->cps; p; p = p->next) {
|
||||
s += sizeof(*p);
|
||||
s += comm_point_get_mem(p->com);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -141,9 +141,17 @@ void listen_resume(struct listen_dnsport* listen);
|
||||
|
||||
/**
|
||||
* delete the listening structure
|
||||
* @param listen: listening structure.
|
||||
*/
|
||||
void listen_delete(struct listen_dnsport* listen);
|
||||
|
||||
/**
|
||||
* get memory size used by the listening structs
|
||||
* @param listen: listening structure.
|
||||
* @return: size in bytes.
|
||||
*/
|
||||
size_t listen_get_mem(struct listen_dnsport* listen);
|
||||
|
||||
/**
|
||||
* Create and bind nonblocking UDP socket
|
||||
* @param addr: address info ready to make socket.
|
||||
|
||||
+615
@@ -0,0 +1,615 @@
|
||||
/*
|
||||
* services/mesh.c - deal with mesh of query states and handle events for that.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to assist in dealing with a mesh of
|
||||
* query states. This mesh is supposed to be thread-specific.
|
||||
* It consists of query states (per qname, qtype, qclass) and connections
|
||||
* between query states and the super and subquery states, and replies to
|
||||
* send back to clients.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "services/mesh.h"
|
||||
#include "services/outbound_list.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/module.h"
|
||||
#include "util/region-allocator.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/timehist.h"
|
||||
|
||||
/** compare two mesh_states */
|
||||
static int
|
||||
mesh_state_compare(const void* ap, const void* bp)
|
||||
{
|
||||
struct mesh_state* a = (struct mesh_state*)ap;
|
||||
struct mesh_state* b = (struct mesh_state*)bp;
|
||||
|
||||
if(a->is_priming && !b->is_priming)
|
||||
return -1;
|
||||
if(!a->is_priming && b->is_priming)
|
||||
return 1;
|
||||
|
||||
if((a->s.query_flags&BIT_RD) && !(b->s.query_flags&BIT_RD))
|
||||
return -1;
|
||||
if(!(a->s.query_flags&BIT_RD) && (b->s.query_flags&BIT_RD))
|
||||
return 1;
|
||||
|
||||
return query_info_compare(&a->s.qinfo, &b->s.qinfo);
|
||||
}
|
||||
|
||||
/** compare two mesh references */
|
||||
static int
|
||||
mesh_state_ref_compare(const void* ap, const void* bp)
|
||||
{
|
||||
struct mesh_state_ref* a = (struct mesh_state_ref*)ap;
|
||||
struct mesh_state_ref* b = (struct mesh_state_ref*)bp;
|
||||
return mesh_state_compare(a->s, b->s);
|
||||
}
|
||||
|
||||
struct mesh_area*
|
||||
mesh_create(int num_modules, struct module_func_block** modfunc,
|
||||
struct module_env* env)
|
||||
{
|
||||
struct mesh_area* mesh = calloc(1, sizeof(struct mesh_area));
|
||||
if(!mesh) {
|
||||
log_err("mesh area alloc: out of memory");
|
||||
return NULL;
|
||||
}
|
||||
mesh->histogram = timehist_setup();
|
||||
if(!mesh->histogram) {
|
||||
free(mesh);
|
||||
log_err("mesh area alloc: out of memory");
|
||||
return NULL;
|
||||
}
|
||||
mesh->num_modules = num_modules;
|
||||
mesh->modfunc = modfunc;
|
||||
mesh->env = env;
|
||||
rbtree_init(&mesh->run, &mesh_state_compare);
|
||||
rbtree_init(&mesh->all, &mesh_state_compare);
|
||||
mesh->num_reply_addrs = 0;
|
||||
mesh->num_reply_states = 0;
|
||||
mesh->num_detached_states = 0;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
/** help mesh delete delete mesh states */
|
||||
static void
|
||||
mesh_delete_helper(rbnode_t* n, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct mesh_state* mstate = (struct mesh_state*)n->key;
|
||||
mesh_state_cleanup(mstate);
|
||||
}
|
||||
|
||||
void
|
||||
mesh_delete(struct mesh_area* mesh)
|
||||
{
|
||||
if(!mesh)
|
||||
return;
|
||||
/* free all query states */
|
||||
traverse_postorder(&mesh->all, &mesh_delete_helper, NULL);
|
||||
timehist_delete(mesh->histogram);
|
||||
free(mesh);
|
||||
}
|
||||
|
||||
void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
uint16_t qflags, struct edns_data* edns, struct comm_reply* rep,
|
||||
uint16_t qid)
|
||||
{
|
||||
struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags, 0);
|
||||
int was_detached = 0;
|
||||
int was_noreply = 0;
|
||||
int added = 0;
|
||||
/* see if it already exists, if not, create one */
|
||||
if(!s) {
|
||||
struct rbnode_t* n;
|
||||
s = mesh_state_create(mesh->env,qinfo, qflags, 0);
|
||||
if(!s) {
|
||||
log_err("mesh_state_create: out of memory; SERVFAIL");
|
||||
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
qinfo, qid, qflags, edns);
|
||||
comm_point_send_reply(rep);
|
||||
return;
|
||||
}
|
||||
n = rbtree_insert(&mesh->all, &s->node);
|
||||
log_assert(n != NULL);
|
||||
/* set detached (it is now) */
|
||||
mesh->num_detached_states++;
|
||||
added = 1;
|
||||
}
|
||||
if(!s->reply_list && s->super_set.count == 0)
|
||||
was_detached = 1;
|
||||
if(!s->reply_list)
|
||||
was_noreply = 1;
|
||||
/* add reply to s */
|
||||
if(!mesh_state_add_reply(s, edns, rep, qid, qflags)) {
|
||||
log_err("mesh_new_client: out of memory; SERVFAIL");
|
||||
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;
|
||||
}
|
||||
/* update statistics */
|
||||
if(was_detached) {
|
||||
log_assert(mesh->num_detached_states > 0);
|
||||
mesh->num_detached_states--;
|
||||
}
|
||||
if(was_noreply) {
|
||||
mesh->num_reply_states ++;
|
||||
}
|
||||
mesh->num_reply_addrs++;
|
||||
if(added)
|
||||
mesh_run(mesh, s, module_event_new, NULL);
|
||||
}
|
||||
|
||||
void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
|
||||
int is_ok, struct comm_reply* reply)
|
||||
{
|
||||
e->qstate->reply = reply;
|
||||
mesh_run(mesh, e->qstate->mesh_info,
|
||||
is_ok?module_event_reply:module_event_noreply, e);
|
||||
}
|
||||
|
||||
struct mesh_state*
|
||||
mesh_state_create(struct module_env* env, struct query_info* qinfo,
|
||||
uint16_t qflags, int prime)
|
||||
{
|
||||
region_type* region = region_create(malloc, free);
|
||||
struct mesh_state* mstate;
|
||||
int i;
|
||||
if(!region)
|
||||
return NULL;
|
||||
mstate = (struct mesh_state*)region_alloc(region,
|
||||
sizeof(struct mesh_state));
|
||||
if(!mstate) {
|
||||
region_destroy(region);
|
||||
return NULL;
|
||||
}
|
||||
memset(mstate, 0, sizeof(*mstate));
|
||||
mstate->node = *RBTREE_NULL;
|
||||
mstate->run_node = *RBTREE_NULL;
|
||||
mstate->node.key = mstate;
|
||||
mstate->run_node.key = mstate;
|
||||
mstate->debug_flags = 0;
|
||||
mstate->is_priming = prime;
|
||||
mstate->reply_list = NULL;
|
||||
rbtree_init(&mstate->super_set, &mesh_state_ref_compare);
|
||||
rbtree_init(&mstate->sub_set, &mesh_state_ref_compare);
|
||||
/* init module qstate */
|
||||
mstate->s.qinfo.qtype = qinfo->qtype;
|
||||
mstate->s.qinfo.qclass = qinfo->qclass;
|
||||
mstate->s.qinfo.qname_len = qinfo->qname_len;
|
||||
mstate->s.qinfo.qname = region_alloc_init(region, qinfo->qname,
|
||||
qinfo->qname_len);
|
||||
if(!mstate->s.qinfo.qname) {
|
||||
region_destroy(region);
|
||||
return NULL;
|
||||
}
|
||||
/* remove all weird bits from qflags */
|
||||
mstate->s.query_flags = (qflags & BIT_RD);
|
||||
mstate->s.reply = NULL;
|
||||
mstate->s.region = region;
|
||||
mstate->s.curmod = 0;
|
||||
mstate->s.env = env;
|
||||
mstate->s.mesh_info = mstate;
|
||||
/* init modules */
|
||||
for(i=0; i<env->mesh->num_modules; i++) {
|
||||
mstate->s.minfo[i] = NULL;
|
||||
mstate->s.ext_state[i] = module_state_initial;
|
||||
}
|
||||
return mstate;
|
||||
}
|
||||
|
||||
void
|
||||
mesh_state_cleanup(struct mesh_state* mstate)
|
||||
{
|
||||
struct mesh_area* mesh;
|
||||
int i;
|
||||
if(!mstate)
|
||||
return;
|
||||
/* de-init modules */
|
||||
mesh = mstate->s.env->mesh;
|
||||
for(i=0; i<mesh->num_modules; i++) {
|
||||
(*mesh->modfunc[i]->clear)(&mstate->s, i);
|
||||
mstate->s.minfo[i] = NULL;
|
||||
mstate->s.ext_state[i] = module_finished;
|
||||
}
|
||||
region_destroy(mstate->s.region);
|
||||
}
|
||||
|
||||
void
|
||||
mesh_state_delete(struct module_qstate* qstate)
|
||||
{
|
||||
struct mesh_area* mesh;
|
||||
struct mesh_state_ref* super, ref;
|
||||
struct mesh_state* mstate;
|
||||
if(!qstate)
|
||||
return;
|
||||
mstate = qstate->mesh_info;
|
||||
mesh = mstate->s.env->mesh;
|
||||
mesh_detach_subs(&mstate->s);
|
||||
if(!mstate->reply_list && mstate->super_set.count == 0) {
|
||||
log_assert(mesh->num_detached_states > 0);
|
||||
mesh->num_detached_states--;
|
||||
}
|
||||
if(mstate->reply_list) {
|
||||
log_assert(mesh->num_reply_states > 0);
|
||||
mesh->num_reply_states--;
|
||||
}
|
||||
ref.node.key = &ref;
|
||||
ref.s = mstate;
|
||||
RBTREE_FOR(super, struct mesh_state_ref*, &mstate->super_set) {
|
||||
(void)rbtree_delete(&super->s->sub_set, &ref);
|
||||
}
|
||||
(void)rbtree_delete(&mesh->run, mstate);
|
||||
(void)rbtree_delete(&mesh->all, mstate);
|
||||
mesh_state_cleanup(mstate);
|
||||
}
|
||||
|
||||
void mesh_detach_subs(struct module_qstate* qstate)
|
||||
{
|
||||
struct mesh_area* mesh = qstate->env->mesh;
|
||||
struct mesh_state_ref* ref, lookup;
|
||||
struct rbnode_t* n;
|
||||
lookup.node.key = &lookup;
|
||||
lookup.s = qstate->mesh_info;
|
||||
RBTREE_FOR(ref, struct mesh_state_ref*, &qstate->mesh_info->sub_set) {
|
||||
n = rbtree_delete(&ref->s->super_set, &lookup);
|
||||
log_assert(n != NULL); /* must have been present */
|
||||
if(!ref->s->reply_list && ref->s->super_set.count == 0) {
|
||||
mesh->num_detached_states++;
|
||||
log_assert(mesh->num_detached_states +
|
||||
mesh->num_reply_states <= mesh->all.count);
|
||||
}
|
||||
}
|
||||
rbtree_init(&qstate->mesh_info->sub_set, &mesh_state_ref_compare);
|
||||
}
|
||||
|
||||
int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
|
||||
uint16_t qflags, int prime, struct module_qstate** newq)
|
||||
{
|
||||
/* find it, if not, create it */
|
||||
struct mesh_area* mesh = qstate->env->mesh;
|
||||
struct mesh_state* sub = mesh_area_find(mesh, qinfo, qflags, prime);
|
||||
if(!sub) {
|
||||
struct rbnode_t* n;
|
||||
/* create a new one */
|
||||
sub = mesh_state_create(qstate->env, qinfo, qflags, prime);
|
||||
if(!sub) {
|
||||
log_err("mesh_attach_sub: out of memory");
|
||||
return 0;
|
||||
}
|
||||
n = rbtree_insert(&mesh->all, &sub->node);
|
||||
log_assert(n != NULL);
|
||||
/* set detached (it is now) */
|
||||
mesh->num_detached_states++;
|
||||
/* set new query state to run */
|
||||
n = rbtree_insert(&mesh->run, &sub->run_node);
|
||||
log_assert(n != NULL);
|
||||
*newq = &sub->s;
|
||||
} else
|
||||
*newq = NULL;
|
||||
if(!mesh_state_attachment(qstate->mesh_info, sub))
|
||||
return 0;
|
||||
if(!sub->reply_list && sub->super_set.count == 1) {
|
||||
/* it used to be detached, before this one got added */
|
||||
log_assert(mesh->num_detached_states > 0);
|
||||
mesh->num_detached_states--;
|
||||
}
|
||||
/* *newq will be run when inited after the current module stops */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub)
|
||||
{
|
||||
struct rbnode_t* n;
|
||||
struct mesh_state_ref* subref; /* points to sub, inserted in super */
|
||||
struct mesh_state_ref* superref; /* points to super, inserted in sub */
|
||||
if( !(subref = region_alloc(super->s.region,
|
||||
sizeof(struct mesh_state_ref))) ||
|
||||
!(superref = region_alloc(sub->s.region,
|
||||
sizeof(struct mesh_state_ref))) ) {
|
||||
log_err("mesh_state_attachment: out of memory");
|
||||
return 0;
|
||||
}
|
||||
superref->node.key = superref;
|
||||
superref->s = super;
|
||||
subref->node.key = subref;
|
||||
subref->s = sub;
|
||||
n = rbtree_insert(&sub->super_set, &superref->node);
|
||||
log_assert(n != NULL);
|
||||
n = rbtree_insert(&super->sub_set, &subref->node);
|
||||
log_assert(n != NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** subtract timers and the values do not overflow or become negative */
|
||||
static void
|
||||
timeval_subtract(struct timeval* d, struct timeval* end, struct timeval* start)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
d->tv_sec = end->tv_sec - start->tv_sec;
|
||||
while(end->tv_usec < start->tv_usec) {
|
||||
end->tv_usec += 1000000;
|
||||
d->tv_sec--;
|
||||
}
|
||||
d->tv_usec = end->tv_usec - start->tv_usec;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** add timers and the values do not overflow or become negative */
|
||||
static void
|
||||
timeval_add(struct timeval* d, struct timeval* add)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
d->tv_sec += add->tv_sec;
|
||||
d->tv_usec += add->tv_usec;
|
||||
while(d->tv_usec > 1000000 ) {
|
||||
d->tv_usec -= 1000000;
|
||||
d->tv_sec++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** divide sum of timers to get average */
|
||||
static void
|
||||
timeval_divide(struct timeval* avg, struct timeval* sum, size_t d)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
size_t leftover;
|
||||
if(d == 0) {
|
||||
avg->tv_sec = 0;
|
||||
avg->tv_usec = 0;
|
||||
return;
|
||||
}
|
||||
avg->tv_sec = sum->tv_sec / d;
|
||||
avg->tv_usec = sum->tv_usec / d;
|
||||
/* handle fraction from seconds divide */
|
||||
leftover = sum->tv_sec - avg->tv_sec*d;
|
||||
avg->tv_usec += (leftover*1000000)/d;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Send reply to mesh reply entry
|
||||
* @param m: mesh state to send it for.
|
||||
* @param rcode: if not 0, error code.
|
||||
* @param rep: reply to send (or NULL if rcode is set).
|
||||
* @param r: reply entry
|
||||
*/
|
||||
static void
|
||||
mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
struct mesh_reply* r)
|
||||
{
|
||||
struct timeval end_time;
|
||||
/* send the reply */
|
||||
if(rcode) {
|
||||
error_encode(r->query_reply.c->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;
|
||||
r->edns.edns_version = EDNS_ADVERTISED_VERSION;
|
||||
r->edns.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
r->edns.ext_rcode = 0;
|
||||
r->edns.bits &= EDNS_DO;
|
||||
if(!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)))
|
||||
{
|
||||
error_encode(r->query_reply.c->buffer,
|
||||
LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid,
|
||||
r->qflags, &r->edns);
|
||||
}
|
||||
comm_point_send_reply(&r->query_reply);
|
||||
}
|
||||
/* account */
|
||||
m->s.env->mesh->num_reply_addrs--;
|
||||
if(gettimeofday(&end_time, NULL) < 0) {
|
||||
log_err("gettimeofday: %s", strerror(errno));
|
||||
return;
|
||||
} else {
|
||||
struct timeval duration;
|
||||
timeval_subtract(&duration, &end_time, &r->start_time);
|
||||
verbose(VERB_ALGO, "query took %d.%6.6d sec",
|
||||
(int)duration.tv_sec, (int)duration.tv_usec);
|
||||
m->s.env->mesh->replies_sent++;
|
||||
timeval_add(&m->s.env->mesh->replies_sum_wait, &duration);
|
||||
timehist_insert(m->s.env->mesh->histogram, &duration);
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_query_done(struct module_qstate* qstate, int rcode,
|
||||
struct reply_info* rep)
|
||||
{
|
||||
struct mesh_state* m = qstate->mesh_info;
|
||||
struct mesh_reply* r;
|
||||
log_assert(!(m->debug_flags&1)); /* not twice! */
|
||||
m->debug_flags |= 1;
|
||||
for(r = m->reply_list; r; r = r->next) {
|
||||
mesh_send_reply(m, rcode, rep, r);
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_walk_supers(struct module_qstate* qstate, int id,
|
||||
void (*cb)(struct module_qstate*, int, struct module_qstate*))
|
||||
{
|
||||
struct mesh_state* m = qstate->mesh_info;
|
||||
struct mesh_area* mesh = m->s.env->mesh;
|
||||
struct mesh_state_ref* ref;
|
||||
log_assert(!(m->debug_flags&2)); /* not twice! */
|
||||
m->debug_flags |= 2;
|
||||
RBTREE_FOR(ref, struct mesh_state_ref*, &qstate->mesh_info->super_set)
|
||||
{
|
||||
/* make super runnable */
|
||||
(void)rbtree_insert(&mesh->run, &ref->s->run_node);
|
||||
/* callback */
|
||||
(*cb)(qstate, id, &ref->s->s);
|
||||
}
|
||||
}
|
||||
|
||||
struct mesh_state* mesh_area_find(struct mesh_area* mesh,
|
||||
struct query_info* qinfo, uint16_t qflags, int prime)
|
||||
{
|
||||
struct mesh_state key;
|
||||
struct mesh_state* result;
|
||||
|
||||
key.node.key = &key;
|
||||
key.is_priming = prime;
|
||||
key.s.qinfo = *qinfo;
|
||||
key.s.query_flags = qflags;
|
||||
|
||||
result = (struct mesh_state*)rbtree_search(&mesh->all, &key);
|
||||
return result;
|
||||
}
|
||||
|
||||
int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
|
||||
struct comm_reply* rep, uint16_t qid, uint16_t qflags)
|
||||
{
|
||||
struct mesh_reply* r = region_alloc(s->s.region,
|
||||
sizeof(struct mesh_reply));
|
||||
if(!r)
|
||||
return 0;
|
||||
r->query_reply = *rep;
|
||||
r->edns = *edns;
|
||||
r->qid = qid;
|
||||
r->qflags = qflags;
|
||||
if(gettimeofday(&r->start_time, NULL) < 0) {
|
||||
log_err("addrep: gettimeofday: %s", strerror(errno));
|
||||
memset(&r->start_time, 0, sizeof(r->start_time));
|
||||
}
|
||||
r->next = s->reply_list;
|
||||
s->reply_list = r;
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
|
||||
enum module_ev ev, struct outbound_entry* e)
|
||||
{
|
||||
enum module_ext_state s;
|
||||
verbose(VERB_ALGO, "mesh_run: start");
|
||||
while(mstate) {
|
||||
/* run the module */
|
||||
(*mesh->modfunc[mstate->s.curmod]->operate)
|
||||
(&mstate->s, ev, mstate->s.curmod, e);
|
||||
|
||||
/* examine results */
|
||||
mstate->s.reply = NULL;
|
||||
region_free_all(mstate->s.env->scratch);
|
||||
s = mstate->s.ext_state[mstate->s.curmod];
|
||||
verbose(VERB_ALGO, "mesh_run: %s module exit state is %s",
|
||||
mesh->modfunc[mstate->s.curmod]->name, strextstate(s));
|
||||
if(s == module_error || s == module_finished) {
|
||||
/* must have called _done and _supers */
|
||||
log_assert(mstate->debug_flags == 3);
|
||||
mesh_state_delete(&mstate->s);
|
||||
}
|
||||
|
||||
/* run more modules */
|
||||
ev = module_event_pass;
|
||||
e = NULL;
|
||||
if(mesh->run.count > 0) {
|
||||
/* pop random element off the runnable tree */
|
||||
mstate = (struct mesh_state*)mesh->run.root->key;
|
||||
(void)rbtree_delete(&mesh->run, mstate);
|
||||
} else mstate = NULL;
|
||||
}
|
||||
if(verbosity >= VERB_ALGO)
|
||||
mesh_stats(mesh, "mesh_run: end");
|
||||
}
|
||||
|
||||
void
|
||||
mesh_stats(struct mesh_area* mesh, const char* str)
|
||||
{
|
||||
log_info("%s %u states (%u with reply, %u detached), "
|
||||
"%u waiting replies", str, (unsigned)mesh->all.count,
|
||||
(unsigned)mesh->num_reply_states,
|
||||
(unsigned)mesh->num_detached_states,
|
||||
(unsigned)mesh->num_reply_addrs);
|
||||
if(mesh->replies_sent > 0) {
|
||||
struct timeval avg;
|
||||
timeval_divide(&avg, &mesh->replies_sum_wait,
|
||||
mesh->replies_sent);
|
||||
log_info("sent %u replies, with average wait "
|
||||
"of %d.%6.6d sec", (unsigned)mesh->replies_sent,
|
||||
(int)avg.tv_sec, (int)avg.tv_usec);
|
||||
log_info("histogram of reply wait times");
|
||||
timehist_log(mesh->histogram);
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
mesh_get_mem(struct mesh_area* mesh)
|
||||
{
|
||||
struct mesh_state* m;
|
||||
size_t s = sizeof(*mesh) + sizeof(struct timehist) +
|
||||
sizeof(struct th_buck)*mesh->histogram->num;
|
||||
RBTREE_FOR(m, struct mesh_state*, &mesh->all) {
|
||||
/* all, including m itself allocated in qstate region */
|
||||
s += region_get_mem(m->s.region);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/** helper recursive rbtree find routine */
|
||||
static int
|
||||
find_in_subsub(struct mesh_state* m, struct mesh_state* tofind)
|
||||
{
|
||||
struct mesh_state_ref* r;
|
||||
RBTREE_FOR(r, struct mesh_state_ref*, &m->sub_set) {
|
||||
if(r->s == tofind || find_in_subsub(r->s, tofind))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo)
|
||||
{
|
||||
struct mesh_area* mesh = qstate->env->mesh;
|
||||
struct mesh_state* cyc_m = qstate->mesh_info;
|
||||
struct mesh_state* dep_m = mesh_area_find(mesh, qinfo, BIT_RD, 0);
|
||||
if(!dep_m)
|
||||
return 0;
|
||||
if(dep_m == cyc_m || find_in_subsub(dep_m, cyc_m))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
+371
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* services/mesh.h - deal with mesh of query states and handle events for that.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to assist in dealing with a mesh of
|
||||
* query states. This mesh is supposed to be thread-specific.
|
||||
* It consists of query states (per qname, qtype, qclass) and connections
|
||||
* between query states and the super and subquery states, and replies to
|
||||
* send back to clients.
|
||||
*/
|
||||
|
||||
#ifndef SERVICES_MESH_H
|
||||
#define SERVICES_MESH_H
|
||||
|
||||
#include "util/rbtree.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/module.h"
|
||||
struct mesh_state;
|
||||
struct mesh_reply;
|
||||
struct query_info;
|
||||
struct reply_info;
|
||||
struct outbound_entry;
|
||||
struct timehist;
|
||||
|
||||
/**
|
||||
* Mesh of query states
|
||||
*/
|
||||
struct mesh_area {
|
||||
/** the number of modules */
|
||||
int num_modules;
|
||||
/** the module callbacks, array of num_modules length (ref only) */
|
||||
struct module_func_block** modfunc;
|
||||
/** environment for new states */
|
||||
struct module_env* env;
|
||||
|
||||
/** set of runnable queries (mesh_state.run_node) */
|
||||
rbtree_t run;
|
||||
/** rbtree of all current queries (mesh_state.node)*/
|
||||
rbtree_t all;
|
||||
|
||||
/** count of the total number of mesh_reply entries */
|
||||
size_t num_reply_addrs;
|
||||
/** count of the number of mesh_states that have mesh_replies
|
||||
* Because a state can send results to multiple reply addresses,
|
||||
* this number must be equal or lower than num_reply_addrs. */
|
||||
size_t num_reply_states;
|
||||
/** number of mesh_states that have no mesh_replies, and also
|
||||
* an empty set of super-states, thus are 'toplevel' or detached
|
||||
* internal opportunistic queries */
|
||||
size_t num_detached_states;
|
||||
|
||||
/** number of replies sent */
|
||||
size_t replies_sent;
|
||||
/** sum of waiting times for the replies */
|
||||
struct timeval replies_sum_wait;
|
||||
/** histogram of time values */
|
||||
struct timehist* histogram;
|
||||
};
|
||||
|
||||
/**
|
||||
* A mesh query state
|
||||
* Unique per qname, qtype, qclass (from the qstate).
|
||||
* And RD flag; in case a client turns it off.
|
||||
* And priming queries are different from ordinary queries (because of hints).
|
||||
*
|
||||
* The entire structure is allocated in a region, this region is the qstate
|
||||
* region. All parts (rbtree nodes etc) are also allocated in the region.
|
||||
*/
|
||||
struct mesh_state {
|
||||
/** node in mesh_area all tree, key is this struct. Must be first. */
|
||||
rbnode_t node;
|
||||
/** node in mesh_area runnable tree, key is this struct */
|
||||
rbnode_t run_node;
|
||||
/** if this is a (stub or root) priming query (with hints) */
|
||||
int is_priming;
|
||||
/** the query state. Note that the qinfo and query_flags
|
||||
* may not change. */
|
||||
struct module_qstate s;
|
||||
/** the list of replies to clients for the results */
|
||||
struct mesh_reply* reply_list;
|
||||
/** debug flags */
|
||||
int debug_flags;
|
||||
/** set of superstates (that want this state's result)
|
||||
* contains struct mesh_state_ref* */
|
||||
rbtree_t super_set;
|
||||
/** set of substates (that this state needs to continue)
|
||||
* contains struct mesh_state_ref* */
|
||||
rbtree_t sub_set;
|
||||
};
|
||||
|
||||
/**
|
||||
* Rbtree reference to a mesh_state.
|
||||
* Used in super_set and sub_set.
|
||||
*/
|
||||
struct mesh_state_ref {
|
||||
/** node in rbtree for set, key is this structure */
|
||||
rbnode_t node;
|
||||
/** the mesh state */
|
||||
struct mesh_state* s;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reply to a client
|
||||
*/
|
||||
struct mesh_reply {
|
||||
/** next in reply list */
|
||||
struct mesh_reply* next;
|
||||
/** the query reply destination, packet buffer and where to send. */
|
||||
struct comm_reply query_reply;
|
||||
/** edns data from query */
|
||||
struct edns_data edns;
|
||||
/** the time when request was entered */
|
||||
struct timeval start_time;
|
||||
/** id of query, in network byteorder. */
|
||||
uint16_t qid;
|
||||
/** flags of query, for reply flags */
|
||||
uint16_t qflags;
|
||||
};
|
||||
|
||||
/* ------------------- Functions for worker -------------------- */
|
||||
|
||||
/**
|
||||
* Allocate mesh, to empty.
|
||||
* @param num_modules: number of modules that are present.
|
||||
* @param modfunc: array passed (alloced and deleted by caller), that has
|
||||
* num_modules function callbacks for the modules.
|
||||
* @param env: environment for new queries.
|
||||
* @return mesh: the new mesh or NULL on error.
|
||||
*/
|
||||
struct mesh_area* mesh_create(int num_modules,
|
||||
struct module_func_block** modfunc, struct module_env* env);
|
||||
|
||||
/**
|
||||
* Delete mesh, and all query states and replies in it.
|
||||
* @param mesh: the mesh to delete.
|
||||
*/
|
||||
void mesh_delete(struct mesh_area* mesh);
|
||||
|
||||
/**
|
||||
* New query incoming from clients. Create new query state if needed, and
|
||||
* add mesh_reply to it. Returns error to client on malloc failures.
|
||||
* Will run the mesh area queries to process if a new query state is created.
|
||||
*
|
||||
* @param mesh: the mesh.
|
||||
* @param qinfo: query from client.
|
||||
* @param qflags: flags from client query.
|
||||
* @param edns: edns data from client query.
|
||||
* @param rep: where to reply to.
|
||||
* @param qid: query id to reply with.
|
||||
*/
|
||||
void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
uint16_t qflags, struct edns_data* edns, struct comm_reply* rep,
|
||||
uint16_t qid);
|
||||
|
||||
/**
|
||||
* Handle new event from the wire. A serviced query has returned.
|
||||
* The query state will be made runnable, and the mesh_area will process
|
||||
* query states until processing is complete.
|
||||
*
|
||||
* @param mesh: the query mesh.
|
||||
* @param e: outbound entry, with query state to run and reply pointer.
|
||||
* @param is_ok: if true, reply is OK, otherwise a timeout happened.
|
||||
* @param reply: the comm point reply info.
|
||||
*/
|
||||
void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
|
||||
int is_ok, struct comm_reply* reply);
|
||||
|
||||
/* ------------------- Functions for module environment --------------- */
|
||||
|
||||
/**
|
||||
* Detach-subqueries.
|
||||
* Remove all sub-query references from this query state.
|
||||
* Keeps super-references of those sub-queries correct.
|
||||
* Updates stat items in mesh_area structure.
|
||||
* @param qstate: used to find mesh state.
|
||||
*/
|
||||
void mesh_detach_subs(struct module_qstate* qstate);
|
||||
|
||||
/**
|
||||
* Attach subquery.
|
||||
* Creates it if it does not exist already.
|
||||
* Keeps sub and super references correct.
|
||||
* Updates stat items in mesh_area structure.
|
||||
* Pass if it is priming query or not.
|
||||
* return:
|
||||
* o if error (malloc) happened.
|
||||
* o need to initialise the new state (module init; it is a new state).
|
||||
* so that the next run of the query with this module is successful.
|
||||
* o no init needed, attachment successful.
|
||||
*
|
||||
* @param qstate: the state to find mesh state, and that wants to receive
|
||||
* the results from the new subquery.
|
||||
* @param qinfo: what to query for (copied).
|
||||
* @param qflags: what flags to use (RD flag or not).
|
||||
* @param prime: if it is a (stub) priming query.
|
||||
* @param newq: If the new subquery needs initialisation, it is returned,
|
||||
* otherwise NULL is returned.
|
||||
* @return: false on error, true if success (and init may be needed).
|
||||
*/
|
||||
int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
|
||||
uint16_t qflags, int prime, struct module_qstate** newq);
|
||||
|
||||
/**
|
||||
* Query state is done, send messages to reply entries.
|
||||
* Encode messages using reply entry values and the querystate (with original
|
||||
* qinfo), using given reply_info.
|
||||
* Pass errcode != 0 if an error reply is needed.
|
||||
* If no reply entries, nothing is done.
|
||||
* Must be called before a module can module_finished or return module_error.
|
||||
* The module must handle the super query states itself as well.
|
||||
*
|
||||
* @param qstate: used for original query info. And to find mesh info.
|
||||
* @param rcode: if not 0 (NOERROR) an error is sent back (and rep ignored).
|
||||
* @param rep: reply to encode and send back to clients.
|
||||
*/
|
||||
void mesh_query_done(struct module_qstate* qstate, int rcode,
|
||||
struct reply_info* rep);
|
||||
|
||||
/**
|
||||
* Get a callback for the super query states that are interested in the
|
||||
* results from this query state. These can then be changed for error
|
||||
* or results.
|
||||
* Must be called befor a module can module_finished or return module_error.
|
||||
* After finishing or module error, the super query states become runnable
|
||||
* with event module_event_pass.
|
||||
*
|
||||
* @param qstate: the state that has results, used to find mesh state.
|
||||
* @param id: module id.
|
||||
* @param cb: callback function. Called as
|
||||
* cb(qstate, id, super_qstate) for every super query state.
|
||||
*/
|
||||
void mesh_walk_supers(struct module_qstate* qstate, int id,
|
||||
void (*cb)(struct module_qstate*, int, struct module_qstate*));
|
||||
|
||||
/**
|
||||
* Delete mesh state, cleanup and also rbtrees and so on.
|
||||
* Will detach from all super/subnodes.
|
||||
* @param qstate: to remove.
|
||||
*/
|
||||
void mesh_state_delete(struct module_qstate* qstate);
|
||||
|
||||
/* ------------------- Functions for mesh -------------------- */
|
||||
|
||||
/**
|
||||
* Create and initialize a new mesh state and its query state
|
||||
* Does not put the mesh state into rbtrees and so on.
|
||||
* @param env: module environment to set.
|
||||
* @param qinfo: query info that the mesh is for.
|
||||
* @param qflags: flags for query (RD flag).
|
||||
* @param prime: if true, it is a priming query, set is_priming on mesh state.
|
||||
* @return: new mesh state or NULL on allocation error.
|
||||
*/
|
||||
struct mesh_state* mesh_state_create(struct module_env* env,
|
||||
struct query_info* qinfo, uint16_t qflags, int prime);
|
||||
|
||||
/**
|
||||
* Cleanup a mesh state and its query state. Does not do rbtree or
|
||||
* reference cleanup.
|
||||
* @param mstate: mesh state to cleanup. Its pointer may no longer be used
|
||||
* afterwards. Cleanup rbtrees before calling this function.
|
||||
*/
|
||||
void mesh_state_cleanup(struct mesh_state* mstate);
|
||||
|
||||
/**
|
||||
* Find a mesh state in the mesh area. Pass relevant flags.
|
||||
*
|
||||
* @param mesh: the mesh area to look in.
|
||||
* @param qinfo: what query
|
||||
* @param qflags: if RD bit is set or not.
|
||||
* @param prime: if it is a priming query.
|
||||
* @return: mesh state or NULL if not found.
|
||||
*/
|
||||
struct mesh_state* mesh_area_find(struct mesh_area* mesh,
|
||||
struct query_info* qinfo, uint16_t qflags, int prime);
|
||||
|
||||
/**
|
||||
* Setup attachment super/sub relation between super and sub mesh state.
|
||||
* The relation must not be present when calling the function.
|
||||
* Does not update stat items in mesh_area.
|
||||
* @param super: super state.
|
||||
* @param sub: sub state.
|
||||
* @return: 0 on alloc error.
|
||||
*/
|
||||
int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub);
|
||||
|
||||
/**
|
||||
* Create new reply structure and attach it to a mesh state.
|
||||
* Does not update stat items in mesh area.
|
||||
* @param s: the mesh state.
|
||||
* @param edns: edns data for reply (bufsize).
|
||||
* @param rep: comm point reply info.
|
||||
* @param qid: ID of reply.
|
||||
* @param qflags: original query flags.
|
||||
* @return: 0 on alloc error.
|
||||
*/
|
||||
int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
|
||||
struct comm_reply* rep, uint16_t qid, uint16_t qflags);
|
||||
|
||||
/**
|
||||
* Run the mesh. Run all runnable mesh states. Which can create new
|
||||
* runnable mesh states. Until completion. Automatically called by
|
||||
* mesh_report_reply and mesh_new_client as needed.
|
||||
* @param mesh: mesh area.
|
||||
* @param mstate: first mesh state to run.
|
||||
* @param ev: event the mstate. Others get event_pass.
|
||||
* @param e: if a reply, its outbound entry.
|
||||
*/
|
||||
void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
|
||||
enum module_ev ev, struct outbound_entry* e);
|
||||
|
||||
/**
|
||||
* Print some stats about the mesh to the log.
|
||||
* @param mesh: the mesh to print it for.
|
||||
* @param str: descriptive string to go with it.
|
||||
*/
|
||||
void mesh_stats(struct mesh_area* mesh, const char* str);
|
||||
|
||||
/**
|
||||
* Calculate memory size in use by mesh and all queries inside it.
|
||||
* @param mesh: the mesh to examine.
|
||||
* @return size in bytes.
|
||||
*/
|
||||
size_t mesh_get_mem(struct mesh_area* mesh);
|
||||
|
||||
/**
|
||||
* Find cycle; see if the given mesh is in the targets sub, or sub-sub, ...
|
||||
* trees.
|
||||
* @param qstate: given mesh querystate.
|
||||
* @param qinfo: query info for dependency.
|
||||
* @return true if the name,type,class exists and the given qstate mesh exists
|
||||
* as a dependency of that name. Thus if qstate becomes dependent on
|
||||
* name,type,class then a cycle is created.
|
||||
*/
|
||||
int mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo);
|
||||
|
||||
#endif /* SERVICES_MESH_H */
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* services/outbound_list.c - keep list of outbound serviced queries.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to help a module keep track of the
|
||||
* queries it has outstanding to authoritative servers.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "services/outbound_list.h"
|
||||
#include "services/outside_network.h"
|
||||
|
||||
void
|
||||
outbound_list_init(struct outbound_list* list)
|
||||
{
|
||||
list->first = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
outbound_list_clear(struct outbound_list* list)
|
||||
{
|
||||
struct outbound_entry *p, *np;
|
||||
p = list->first;
|
||||
while(p) {
|
||||
np = p->next;
|
||||
outnet_serviced_query_stop(p->qsent, p);
|
||||
free(p);
|
||||
p = np;
|
||||
}
|
||||
outbound_list_init(list);
|
||||
}
|
||||
|
||||
void
|
||||
outbound_list_insert(struct outbound_list* list, struct outbound_entry* e)
|
||||
{
|
||||
if(list->first)
|
||||
list->first->prev = e;
|
||||
e->next = list->first;
|
||||
e->prev = NULL;
|
||||
list->first = e;
|
||||
}
|
||||
|
||||
void
|
||||
outbound_list_remove(struct outbound_list* list, struct outbound_entry* e)
|
||||
{
|
||||
if(!e)
|
||||
return;
|
||||
outnet_serviced_query_stop(e->qsent, e);
|
||||
if(e->next)
|
||||
e->next->prev = e->prev;
|
||||
if(e->prev)
|
||||
e->prev->next = e->next;
|
||||
else list->first = e->next;
|
||||
free(e);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* services/outbound_list.h - keep list of outbound serviced queries.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to help a module keep track of the
|
||||
* queries it has outstanding to authoritative servers.
|
||||
*/
|
||||
#ifndef SERVICES_OUTBOUND_LIST_H
|
||||
#define SERVICES_OUTBOUND_LIST_H
|
||||
struct outbound_entry;
|
||||
struct serviced_query;
|
||||
struct module_qstate;
|
||||
|
||||
/**
|
||||
* The outbound list. This structure is part of the module specific query
|
||||
* state.
|
||||
*/
|
||||
struct outbound_list {
|
||||
/** The linked list of outbound query entries. */
|
||||
struct outbound_entry* first;
|
||||
};
|
||||
|
||||
/**
|
||||
* Outbound list entry. A serviced query sent by a module processing the
|
||||
* query from the qstate. Double linked list to aid removal.
|
||||
*/
|
||||
struct outbound_entry {
|
||||
/** next in list */
|
||||
struct outbound_entry* next;
|
||||
/** prev in list */
|
||||
struct outbound_entry* prev;
|
||||
/** The query that was sent out */
|
||||
struct serviced_query* qsent;
|
||||
/** the module query state that sent it */
|
||||
struct module_qstate* qstate;
|
||||
};
|
||||
|
||||
/**
|
||||
* Init the user allocated outbound list structure
|
||||
* @param list: the list structure.
|
||||
*/
|
||||
void outbound_list_init(struct outbound_list* list);
|
||||
|
||||
/**
|
||||
* Clear the user owner outbound list structure.
|
||||
* Deletes serviced queries.
|
||||
* @param list: the list structure. It is cleared, but the list struct itself
|
||||
* is callers responsability to delete.
|
||||
*/
|
||||
void outbound_list_clear(struct outbound_list* list);
|
||||
|
||||
/**
|
||||
* Insert new entry into the list. Caller must allocate the entry with malloc.
|
||||
* qstate and qsent are set by caller.
|
||||
* @param list: the list to add to.
|
||||
* @param e: entry to add, it is only half initialised at call start, fully
|
||||
* initialised at call end.
|
||||
*/
|
||||
void outbound_list_insert(struct outbound_list* list,
|
||||
struct outbound_entry* e);
|
||||
|
||||
/**
|
||||
* Remove an entry from the list, and deletes it.
|
||||
* Deletes serviced query in the entry.
|
||||
* @param list: the list to remove from.
|
||||
* @param e: the entry to remove.
|
||||
*/
|
||||
void outbound_list_remove(struct outbound_list* list,
|
||||
struct outbound_entry* e);
|
||||
|
||||
#endif /* SERVICES_OUTBOUND_LIST_H */
|
||||
+584
-100
@@ -42,6 +42,10 @@
|
||||
|
||||
#include "services/outside_network.h"
|
||||
#include "services/listen_dnsport.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
@@ -55,10 +59,15 @@
|
||||
|
||||
/** number of times to retry making a random ID that is unique. */
|
||||
#define MAX_ID_RETRY 1000
|
||||
/** byte size of ip4 address */
|
||||
#define INET_SIZE 4
|
||||
/** byte size of ip6 address */
|
||||
#define INET6_SIZE 16
|
||||
/** number of retries on outgoing UDP queries */
|
||||
#define OUTBOUND_UDP_RETRY 4
|
||||
|
||||
/** callback for serviced query UDP answers */
|
||||
static int serviced_udp_callback(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* rep);
|
||||
/** initiate TCP transaction for serviced query */
|
||||
static void serviced_tcp_initiate(struct outside_network* outnet,
|
||||
struct serviced_query* sq, ldns_buffer* buff);
|
||||
|
||||
/** compare function of pending rbtree */
|
||||
static int
|
||||
@@ -66,47 +75,35 @@ pending_cmp(const void* key1, const void* key2)
|
||||
{
|
||||
struct pending *p1 = (struct pending*)key1;
|
||||
struct pending *p2 = (struct pending*)key2;
|
||||
struct sockaddr_in* p1_in = (struct sockaddr_in*)&p1->addr;
|
||||
struct sockaddr_in* p2_in = (struct sockaddr_in*)&p2->addr;
|
||||
struct sockaddr_in6* p1_in6 = (struct sockaddr_in6*)&p1->addr;
|
||||
struct sockaddr_in6* p2_in6 = (struct sockaddr_in6*)&p2->addr;
|
||||
if(p1->id < p2->id)
|
||||
return -1;
|
||||
if(p1->id > p2->id)
|
||||
return 1;
|
||||
log_assert(p1->id == p2->id);
|
||||
if(p1->addrlen < p2->addrlen)
|
||||
return sockaddr_cmp(&p1->addr, p1->addrlen, &p2->addr, p2->addrlen);
|
||||
}
|
||||
|
||||
/** compare function of serviced query rbtree */
|
||||
static int
|
||||
serviced_cmp(const void* key1, const void* key2)
|
||||
{
|
||||
struct serviced_query* q1 = (struct serviced_query*)key1;
|
||||
struct serviced_query* q2 = (struct serviced_query*)key2;
|
||||
int r;
|
||||
if(q1->qbuflen < q2->qbuflen)
|
||||
return -1;
|
||||
if(p1->addrlen > p2->addrlen)
|
||||
if(q1->qbuflen > q2->qbuflen)
|
||||
return 1;
|
||||
log_assert(p1->addrlen == p2->addrlen);
|
||||
if( p1_in->sin_family < p2_in->sin_family)
|
||||
return -1;
|
||||
if( p1_in->sin_family > p2_in->sin_family)
|
||||
log_assert(q1->qbuflen == q2->qbuflen);
|
||||
/* FIXME: will not detect alternate casing of qname */
|
||||
if((r = memcmp(q1->qbuf, q2->qbuf, q1->qbuflen)) != 0)
|
||||
return r;
|
||||
if(q1->dnssec != q2->dnssec) {
|
||||
if(q1->dnssec < q2->dnssec)
|
||||
return -1;
|
||||
return 1;
|
||||
log_assert( p1_in->sin_family == p2_in->sin_family );
|
||||
/* compare ip4 */
|
||||
if( p1_in->sin_family == AF_INET ) {
|
||||
/* just order it, ntohs not required */
|
||||
if(p1_in->sin_port < p2_in->sin_port)
|
||||
return -1;
|
||||
if(p1_in->sin_port > p2_in->sin_port)
|
||||
return 1;
|
||||
log_assert(p1_in->sin_port == p2_in->sin_port);
|
||||
return memcmp(&p1_in->sin_addr, &p2_in->sin_addr, INET_SIZE);
|
||||
} else if (p1_in6->sin6_family == AF_INET6) {
|
||||
/* just order it, ntohs not required */
|
||||
if(p1_in6->sin6_port < p2_in6->sin6_port)
|
||||
return -1;
|
||||
if(p1_in6->sin6_port > p2_in6->sin6_port)
|
||||
return 1;
|
||||
log_assert(p1_in6->sin6_port == p2_in6->sin6_port);
|
||||
return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr,
|
||||
INET6_SIZE);
|
||||
} else {
|
||||
/* eek unknown type, perform this comparison for sanity. */
|
||||
return memcmp(&p1->addr, &p2->addr, p1->addrlen);
|
||||
}
|
||||
return sockaddr_cmp(&q1->addr, q1->addrlen, &q2->addr, q2->addrlen);
|
||||
}
|
||||
|
||||
/** delete waiting_tcp entry. Does not unlink from waiting list.
|
||||
@@ -122,7 +119,7 @@ waiting_tcp_delete(struct waiting_tcp* w)
|
||||
}
|
||||
|
||||
/** use next free buffer to service a tcp query */
|
||||
static void
|
||||
static int
|
||||
outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt)
|
||||
{
|
||||
struct pending_tcp* pend = w->outnet->tcp_free;
|
||||
@@ -138,20 +135,16 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt)
|
||||
s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if(s == -1) {
|
||||
log_err("outgoing tcp: socket: %s", strerror(errno));
|
||||
log_addr(&w->addr, w->addrlen);
|
||||
(void)(*w->cb)(NULL, w->cb_arg, NETEVENT_CLOSED, NULL);
|
||||
waiting_tcp_delete(w);
|
||||
return;
|
||||
log_addr("failed address", &w->addr, w->addrlen);
|
||||
return 0;
|
||||
}
|
||||
fd_set_nonblock(s);
|
||||
if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) {
|
||||
if(errno != EINPROGRESS) {
|
||||
log_err("outgoing tcp: connect: %s", strerror(errno));
|
||||
log_addr(&w->addr, w->addrlen);
|
||||
log_addr("failed address", &w->addr, w->addrlen);
|
||||
close(s);
|
||||
(void)(*w->cb)(NULL, w->cb_arg, NETEVENT_CLOSED, NULL);
|
||||
waiting_tcp_delete(w);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
w->pkt = NULL;
|
||||
@@ -166,10 +159,10 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt)
|
||||
pend->c->tcp_is_reading = 0;
|
||||
pend->c->tcp_byte_count = 0;
|
||||
comm_point_start_listening(pend->c, s, -1);
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** see if buffers can be used to service TCP queries. */
|
||||
/** see if buffers can be used to service TCP queries */
|
||||
static void
|
||||
use_free_buffer(struct outside_network* outnet)
|
||||
{
|
||||
@@ -179,10 +172,26 @@ use_free_buffer(struct outside_network* outnet)
|
||||
outnet->tcp_wait_first = w->next_waiting;
|
||||
if(outnet->tcp_wait_last == w)
|
||||
outnet->tcp_wait_last = NULL;
|
||||
outnet_tcp_take_into_use(w, w->pkt);
|
||||
if(!outnet_tcp_take_into_use(w, w->pkt)) {
|
||||
(void)(*w->cb)(NULL, w->cb_arg, NETEVENT_CLOSED, NULL);
|
||||
waiting_tcp_delete(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** decomission a tcp buffer, closes commpoint and frees waiting_tcp entry */
|
||||
static void
|
||||
decomission_pending_tcp(struct outside_network* outnet,
|
||||
struct pending_tcp* pend)
|
||||
{
|
||||
comm_point_close(pend->c);
|
||||
pend->next_free = outnet->tcp_free;
|
||||
outnet->tcp_free = pend;
|
||||
waiting_tcp_delete(pend->query);
|
||||
pend->query = NULL;
|
||||
use_free_buffer(outnet);
|
||||
}
|
||||
|
||||
/** callback for pending tcp connections */
|
||||
static int
|
||||
outnet_tcp_cb(struct comm_point* c, void* arg, int error,
|
||||
@@ -192,28 +201,23 @@ outnet_tcp_cb(struct comm_point* c, void* arg, int error,
|
||||
struct outside_network* outnet = pend->query->outnet;
|
||||
verbose(VERB_ALGO, "outnettcp cb");
|
||||
if(error != NETEVENT_NOERROR) {
|
||||
log_info("outnettcp got tcp error %d", error);
|
||||
verbose(VERB_DETAIL, "outnettcp got tcp error %d", error);
|
||||
/* pass error below and exit */
|
||||
} else {
|
||||
/* check ID */
|
||||
if(ldns_buffer_limit(c->buffer) < sizeof(uint16_t) ||
|
||||
LDNS_ID_WIRE(ldns_buffer_begin(c->buffer))!=pend->id) {
|
||||
log_info("outnettcp: bad ID in reply, from:");
|
||||
log_addr(&pend->query->addr, pend->query->addrlen);
|
||||
log_addr("outnettcp: bad ID in reply, from:",
|
||||
&pend->query->addr, pend->query->addrlen);
|
||||
error = NETEVENT_CLOSED;
|
||||
}
|
||||
}
|
||||
(void)(*pend->query->cb)(c, pend->query->cb_arg, error, reply_info);
|
||||
comm_point_close(c);
|
||||
pend->next_free = outnet->tcp_free;
|
||||
outnet->tcp_free = pend;
|
||||
waiting_tcp_delete(pend->query);
|
||||
pend->query = NULL;
|
||||
use_free_buffer(outnet);
|
||||
decomission_pending_tcp(outnet, pend);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** callback for incoming udp answers from the network. */
|
||||
/** callback for incoming udp answers from the network */
|
||||
static int
|
||||
outnet_udp_cb(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply *reply_info)
|
||||
@@ -224,11 +228,11 @@ outnet_udp_cb(struct comm_point* c, void* arg, int error,
|
||||
verbose(VERB_ALGO, "answer cb");
|
||||
|
||||
if(error != NETEVENT_NOERROR) {
|
||||
log_info("outnetudp got udp error %d", error);
|
||||
verbose(VERB_DETAIL, "outnetudp got udp error %d", error);
|
||||
return 0;
|
||||
}
|
||||
if(ldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
|
||||
log_info("outnetudp udp too short");
|
||||
verbose(VERB_DETAIL, "outnetudp udp too short");
|
||||
return 0;
|
||||
}
|
||||
log_assert(reply_info);
|
||||
@@ -237,14 +241,20 @@ outnet_udp_cb(struct comm_point* c, void* arg, int error,
|
||||
key.id = LDNS_ID_WIRE(ldns_buffer_begin(c->buffer));
|
||||
memcpy(&key.addr, &reply_info->addr, reply_info->addrlen);
|
||||
key.addrlen = reply_info->addrlen;
|
||||
verbose(VERB_ALGO, "Incoming reply id=%4.4x addr=", key.id);
|
||||
log_addr(&key.addr, key.addrlen);
|
||||
verbose(VERB_ALGO, "Incoming reply id = %4.4x", key.id);
|
||||
if(verbosity >= VERB_ALGO) {
|
||||
log_addr("Incoming reply addr =", &reply_info->addr, reply_info->addrlen);
|
||||
}
|
||||
|
||||
/* find it, see if this thing is a valid query response */
|
||||
verbose(VERB_ALGO, "lookup size is %d entries", (int)outnet->pending->count);
|
||||
p = (struct pending*)rbtree_search(outnet->pending, &key);
|
||||
if(!p) {
|
||||
verbose(VERB_DETAIL, "received unsolicited udp reply. dropped.");
|
||||
verbose(VERB_DETAIL, "received unwanted or unsolicited udp reply dropped.");
|
||||
if(verbosity >= VERB_ALGO)
|
||||
log_hex("dropped message",
|
||||
ldns_buffer_begin(c->buffer),
|
||||
ldns_buffer_limit(c->buffer));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -256,8 +266,10 @@ outnet_udp_cb(struct comm_point* c, void* arg, int error,
|
||||
}
|
||||
comm_timer_disable(p->timer);
|
||||
verbose(VERB_ALGO, "outnet handle udp reply");
|
||||
(void)(*p->cb)(p->c, p->cb_arg, NETEVENT_NOERROR, NULL);
|
||||
pending_delete(outnet, p);
|
||||
/* delete from tree first in case callback creates a retry */
|
||||
(void)rbtree_delete(outnet->pending, p->node.key);
|
||||
(void)(*p->cb)(p->c, p->cb_arg, NETEVENT_NOERROR, reply_info);
|
||||
pending_delete(NULL, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -336,7 +348,7 @@ make_udp_range(struct comm_point** coms, const char* ifname,
|
||||
return done;
|
||||
}
|
||||
|
||||
/** calculate number of ip4 and ip6 interfaces, times multiplier. */
|
||||
/** calculate number of ip4 and ip6 interfaces, times multiplier */
|
||||
static void
|
||||
calc_num46(char** ifs, int num_ifs, int do_ip4, int do_ip6,
|
||||
size_t multiplier, size_t* num_ip4, size_t* num_ip6)
|
||||
@@ -403,7 +415,8 @@ create_pending_tcp(struct outside_network* outnet, size_t bufsize)
|
||||
struct outside_network*
|
||||
outside_network_create(struct comm_base *base, size_t bufsize,
|
||||
size_t num_ports, char** ifs, int num_ifs, int do_ip4,
|
||||
int do_ip6, int port_base, size_t num_tcp)
|
||||
int do_ip6, int port_base, size_t num_tcp, struct infra_cache* infra,
|
||||
struct ub_randstate* rnd)
|
||||
{
|
||||
struct outside_network* outnet = (struct outside_network*)
|
||||
calloc(1, sizeof(struct outside_network));
|
||||
@@ -414,6 +427,9 @@ outside_network_create(struct comm_base *base, size_t bufsize,
|
||||
}
|
||||
outnet->base = base;
|
||||
outnet->num_tcp = num_tcp;
|
||||
outnet->infra = infra;
|
||||
outnet->rnd = rnd;
|
||||
outnet->udp_second = 0;
|
||||
#ifndef INET6
|
||||
do_ip6 = 0;
|
||||
#endif
|
||||
@@ -426,6 +442,7 @@ outside_network_create(struct comm_base *base, size_t bufsize,
|
||||
!(outnet->udp6_ports = (struct comm_point **)calloc(
|
||||
outnet->num_udp6+1, sizeof(struct comm_point*))) ||
|
||||
!(outnet->pending = rbtree_create(pending_cmp)) ||
|
||||
!(outnet->serviced = rbtree_create(serviced_cmp)) ||
|
||||
!create_pending_tcp(outnet, bufsize)) {
|
||||
log_err("malloc failed");
|
||||
outside_network_delete(outnet);
|
||||
@@ -473,6 +490,13 @@ outside_network_create(struct comm_base *base, size_t bufsize,
|
||||
return outnet;
|
||||
}
|
||||
|
||||
void
|
||||
outside_network_set_secondary_buffer(struct outside_network* outnet,
|
||||
ldns_buffer* buf)
|
||||
{
|
||||
outnet->udp_second = buf;
|
||||
}
|
||||
|
||||
/** helper pending delete */
|
||||
static void
|
||||
pending_node_del(rbnode_t* node, void* arg)
|
||||
@@ -482,6 +506,21 @@ pending_node_del(rbnode_t* node, void* arg)
|
||||
pending_delete(outnet, pend);
|
||||
}
|
||||
|
||||
/** helper serviced delete */
|
||||
static void
|
||||
serviced_node_del(rbnode_t* node, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct serviced_query* sq = (struct serviced_query*)node;
|
||||
struct service_callback* p = sq->cblist, *np;
|
||||
free(sq->qbuf);
|
||||
while(p) {
|
||||
np = p->next;
|
||||
free(p);
|
||||
p = np;
|
||||
}
|
||||
free(sq);
|
||||
}
|
||||
|
||||
void
|
||||
outside_network_delete(struct outside_network* outnet)
|
||||
{
|
||||
@@ -493,6 +532,10 @@ outside_network_delete(struct outside_network* outnet)
|
||||
traverse_postorder(outnet->pending, pending_node_del, NULL);
|
||||
free(outnet->pending);
|
||||
}
|
||||
if(outnet->serviced) {
|
||||
traverse_postorder(outnet->serviced, serviced_node_del, NULL);
|
||||
free(outnet->serviced);
|
||||
}
|
||||
if(outnet->udp_buff)
|
||||
ldns_buffer_free(outnet->udp_buff);
|
||||
if(outnet->udp4_ports) {
|
||||
@@ -586,25 +629,10 @@ new_pending(struct outside_network* outnet, ldns_buffer* packet,
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
verbose(VERB_ALGO, "inserted new pending reply id=%4.4x addr=", pend->id);
|
||||
log_addr(&pend->addr, pend->addrlen);
|
||||
verbose(VERB_ALGO, "inserted new pending reply id=%4.4x", pend->id);
|
||||
return pend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checkout address family.
|
||||
* @param addr: the sockaddr to examine.
|
||||
* return: true if sockaddr is ip6.
|
||||
*/
|
||||
static int
|
||||
addr_is_ip6(struct sockaddr_storage* addr)
|
||||
{
|
||||
short family = *(short*)addr;
|
||||
if(family == AF_INET6)
|
||||
return 1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select outgoing comm point for a query. Fills in c.
|
||||
* @param outnet: network structure that has arrays of ports to choose from.
|
||||
@@ -645,11 +673,11 @@ select_port(struct outside_network* outnet, struct pending* pend,
|
||||
else pend->c = outnet->udp4_ports[chosen];
|
||||
log_assert(pend->c);
|
||||
|
||||
verbose(VERB_ALGO, "query %x outbound %d of %d", pend->id, chosen, nummax);
|
||||
verbose(VERB_ALGO, "query %x outbound on port %d of %d", pend->id, chosen, nummax);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
struct pending*
|
||||
pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
comm_point_callback_t* cb, void* cb_arg, struct ub_randstate* rnd)
|
||||
@@ -660,26 +688,27 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
/* create pending struct and change ID to be unique */
|
||||
if(!(pend=new_pending(outnet, packet, addr, addrlen, cb, cb_arg,
|
||||
rnd))) {
|
||||
/* callback user for the error */
|
||||
(void)(*cb)(NULL, cb_arg, NETEVENT_CLOSED, NULL);
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
select_port(outnet, pend, rnd);
|
||||
if(!pend->c) {
|
||||
pending_delete(outnet, pend);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* send it over the commlink */
|
||||
if(!comm_point_send_udp_msg(pend->c, packet, (struct sockaddr*)addr,
|
||||
addrlen)) {
|
||||
/* callback user for the error */
|
||||
(void)(*pend->cb)(pend->c, pend->cb_arg, NETEVENT_CLOSED, NULL);
|
||||
pending_delete(outnet, pend);
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* system calls to set timeout after sending UDP to make roundtrip
|
||||
smaller. */
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
tv.tv_sec = timeout/1000;
|
||||
tv.tv_usec = (timeout%1000)*1000;
|
||||
comm_timer_set(pend->timer, &tv);
|
||||
return pend;
|
||||
}
|
||||
|
||||
/** callback for outgoing TCP timer event */
|
||||
@@ -714,7 +743,7 @@ outnet_tcptimer(void* arg)
|
||||
use_free_buffer(outnet);
|
||||
}
|
||||
|
||||
void
|
||||
struct waiting_tcp*
|
||||
pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
@@ -728,14 +757,11 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
w = (struct waiting_tcp*)malloc(sizeof(struct waiting_tcp)
|
||||
+ (pend?0:ldns_buffer_limit(packet)));
|
||||
if(!w) {
|
||||
/* callback user for the error */
|
||||
(void)(*callback)(NULL, callback_arg, NETEVENT_CLOSED, NULL);
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
if(!(w->timer = comm_timer_create(outnet->base, outnet_tcptimer, w))) {
|
||||
free(w);
|
||||
(void)(*callback)(NULL, callback_arg, NETEVENT_CLOSED, NULL);
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
w->pkt = NULL;
|
||||
w->pkt_len = ldns_buffer_limit(packet);
|
||||
@@ -752,7 +778,10 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
comm_timer_set(w->timer, &tv);
|
||||
if(pend) {
|
||||
/* we have a buffer available right now */
|
||||
outnet_tcp_take_into_use(w, ldns_buffer_begin(packet));
|
||||
if(!outnet_tcp_take_into_use(w, ldns_buffer_begin(packet))) {
|
||||
waiting_tcp_delete(w);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
/* queue up */
|
||||
w->pkt = (uint8_t*)w + sizeof(struct waiting_tcp);
|
||||
@@ -763,4 +792,459 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
else outnet->tcp_wait_first = w;
|
||||
outnet->tcp_wait_last = w;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
/** create query for serviced queries */
|
||||
static void
|
||||
serviced_gen_query(ldns_buffer* buff, uint8_t* qname, size_t qnamelen,
|
||||
uint16_t qtype, uint16_t qclass, uint16_t flags)
|
||||
{
|
||||
ldns_buffer_clear(buff);
|
||||
/* skip id */
|
||||
ldns_buffer_write_u16(buff, flags);
|
||||
ldns_buffer_write_u16(buff, 1); /* qdcount */
|
||||
ldns_buffer_write_u16(buff, 0); /* ancount */
|
||||
ldns_buffer_write_u16(buff, 0); /* nscount */
|
||||
ldns_buffer_write_u16(buff, 0); /* arcount */
|
||||
ldns_buffer_write(buff, qname, qnamelen);
|
||||
ldns_buffer_write_u16(buff, qtype);
|
||||
ldns_buffer_write_u16(buff, qclass);
|
||||
ldns_buffer_flip(buff);
|
||||
}
|
||||
|
||||
/** lookup serviced query in serviced query rbtree */
|
||||
static struct serviced_query*
|
||||
lookup_serviced(struct outside_network* outnet, ldns_buffer* buff, int dnssec,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen)
|
||||
{
|
||||
struct serviced_query key;
|
||||
key.node.key = &key;
|
||||
key.qbuf = ldns_buffer_begin(buff);
|
||||
key.qbuflen = ldns_buffer_limit(buff);
|
||||
key.dnssec = dnssec;
|
||||
memcpy(&key.addr, addr, addrlen);
|
||||
key.addrlen = addrlen;
|
||||
key.outnet = outnet;
|
||||
return (struct serviced_query*)rbtree_search(outnet->serviced, &key);
|
||||
}
|
||||
|
||||
/** Create new serviced entry */
|
||||
static struct serviced_query*
|
||||
serviced_create(struct outside_network* outnet, ldns_buffer* buff, int dnssec,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen)
|
||||
{
|
||||
struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq));
|
||||
rbnode_t* ins;
|
||||
if(!sq)
|
||||
return NULL;
|
||||
sq->node.key = sq;
|
||||
sq->qbuf = memdup(ldns_buffer_begin(buff), ldns_buffer_limit(buff));
|
||||
if(!sq->qbuf) {
|
||||
free(sq);
|
||||
return NULL;
|
||||
}
|
||||
sq->qbuflen = ldns_buffer_limit(buff);
|
||||
sq->dnssec = dnssec;
|
||||
memcpy(&sq->addr, addr, addrlen);
|
||||
sq->addrlen = addrlen;
|
||||
sq->outnet = outnet;
|
||||
sq->cblist = NULL;
|
||||
sq->pending = NULL;
|
||||
sq->status = serviced_initial;
|
||||
sq->retry = 0;
|
||||
sq->to_be_deleted = 0;
|
||||
ins = rbtree_insert(outnet->serviced, &sq->node);
|
||||
log_assert(ins != NULL); /* must not be already present */
|
||||
return sq;
|
||||
}
|
||||
|
||||
/** remove waiting tcp from the outnet waiting list */
|
||||
static void
|
||||
waiting_list_remove(struct outside_network* outnet, struct waiting_tcp* w)
|
||||
{
|
||||
struct waiting_tcp* p = outnet->tcp_wait_first, *prev = NULL;
|
||||
while(p) {
|
||||
if(p == w) {
|
||||
/* remove w */
|
||||
if(prev)
|
||||
prev->next_waiting = w->next_waiting;
|
||||
else outnet->tcp_wait_first = w->next_waiting;
|
||||
if(outnet->tcp_wait_last == w)
|
||||
outnet->tcp_wait_last = prev;
|
||||
return;
|
||||
}
|
||||
prev = p;
|
||||
p = p->next_waiting;
|
||||
}
|
||||
}
|
||||
|
||||
/** cleanup serviced query entry */
|
||||
static void
|
||||
serviced_delete(struct serviced_query* sq)
|
||||
{
|
||||
if(sq->pending) {
|
||||
/* clear up the pending query */
|
||||
if(sq->status == serviced_query_UDP_EDNS ||
|
||||
sq->status == serviced_query_UDP) {
|
||||
struct pending* p = (struct pending*)sq->pending;
|
||||
pending_delete(sq->outnet, p);
|
||||
} else {
|
||||
struct waiting_tcp* p = (struct waiting_tcp*)
|
||||
sq->pending;
|
||||
if(p->pkt == NULL) {
|
||||
decomission_pending_tcp(sq->outnet,
|
||||
(struct pending_tcp*)p->next_waiting);
|
||||
} else {
|
||||
waiting_list_remove(sq->outnet, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* does not delete from tree, caller has to do that */
|
||||
serviced_node_del(&sq->node, NULL);
|
||||
}
|
||||
|
||||
/** put serviced query into a buffer */
|
||||
static void
|
||||
serviced_encode(struct serviced_query* sq, ldns_buffer* buff, int with_edns)
|
||||
{
|
||||
/* generate query */
|
||||
ldns_buffer_clear(buff);
|
||||
ldns_buffer_write_u16(buff, 0); /* id placeholder */
|
||||
ldns_buffer_write(buff, sq->qbuf, sq->qbuflen);
|
||||
ldns_buffer_flip(buff);
|
||||
if(with_edns) {
|
||||
/* add edns section */
|
||||
struct edns_data edns;
|
||||
edns.edns_present = 1;
|
||||
edns.ext_rcode = 0;
|
||||
edns.edns_version = EDNS_ADVERTISED_VERSION;
|
||||
edns.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns.bits = 0;
|
||||
if(sq->dnssec)
|
||||
edns.bits = EDNS_DO;
|
||||
attach_edns_record(buff, &edns);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform serviced query UDP sending operation.
|
||||
* Sends UDP with EDNS, unless infra host marked non EDNS.
|
||||
* @param sq: query to send.
|
||||
* @param buff: buffer scratch space.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
static int
|
||||
serviced_udp_send(struct serviced_query* sq, ldns_buffer* buff)
|
||||
{
|
||||
int rtt, vs;
|
||||
time_t now = time(0);
|
||||
|
||||
if(!infra_host(sq->outnet->infra, &sq->addr, sq->addrlen, now, &vs,
|
||||
&rtt))
|
||||
return 0;
|
||||
if(sq->status == serviced_initial) {
|
||||
if(vs != -1)
|
||||
sq->status = serviced_query_UDP_EDNS;
|
||||
else sq->status = serviced_query_UDP;
|
||||
}
|
||||
serviced_encode(sq, buff, sq->status == serviced_query_UDP_EDNS);
|
||||
if(gettimeofday(&sq->last_sent_time, NULL) < 0) {
|
||||
log_err("gettimeofday: %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
verbose(VERB_ALGO, "serviced query UDP timeout=%d msec", rtt);
|
||||
sq->pending = pending_udp_query(sq->outnet, buff, &sq->addr,
|
||||
sq->addrlen, rtt, serviced_udp_callback, sq, sq->outnet->rnd);
|
||||
if(!sq->pending)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** call the callbacks for a serviced query */
|
||||
static void
|
||||
serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c,
|
||||
struct comm_reply* rep)
|
||||
{
|
||||
struct service_callback* p = sq->cblist, *n;
|
||||
int dobackup = (sq->cblist && sq->cblist->next); /* >1 cb*/
|
||||
rbnode_t* rem;
|
||||
/* remove from tree, and schedule for deletion, so that callbacks
|
||||
* can safely deregister themselves and even create new serviced
|
||||
* queries that are identical to this one. */
|
||||
rem = rbtree_delete(sq->outnet->serviced, sq);
|
||||
log_assert(rem); /* should have been present */
|
||||
sq->to_be_deleted = 1;
|
||||
verbose(VERB_ALGO, "svcd callbacks start");
|
||||
if(dobackup && c) {
|
||||
/* make a backup of the query, since the querystate processing
|
||||
* may send outgoing queries that overwrite the buffer.
|
||||
* use secondary buffer to store the query.
|
||||
* This is a data copy, but faster than packet to server */
|
||||
ldns_buffer_copy(sq->outnet->udp_second, c->buffer);
|
||||
}
|
||||
while(p) {
|
||||
n = p->next;
|
||||
if(dobackup && c) {
|
||||
ldns_buffer_copy(c->buffer, sq->outnet->udp_second);
|
||||
}
|
||||
(void)(*p->cb)(c, p->cb_arg, error, rep);
|
||||
p = n;
|
||||
}
|
||||
verbose(VERB_ALGO, "svcd callbacks end");
|
||||
log_assert(sq->cblist == NULL);
|
||||
serviced_delete(sq);
|
||||
}
|
||||
|
||||
/** TCP reply or error callback for serviced queries */
|
||||
static int
|
||||
serviced_tcp_callback(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* rep)
|
||||
{
|
||||
struct serviced_query* sq = (struct serviced_query*)arg;
|
||||
struct comm_reply r2;
|
||||
sq->pending = NULL; /* removed after this callback */
|
||||
if(error != NETEVENT_NOERROR && verbosity >= VERB_DETAIL)
|
||||
log_addr("tcp error for address", &sq->addr, sq->addrlen);
|
||||
if(error==NETEVENT_NOERROR && LDNS_RCODE_WIRE(ldns_buffer_begin(
|
||||
c->buffer)) == LDNS_RCODE_FORMERR &&
|
||||
sq->status == serviced_query_TCP_EDNS) {
|
||||
if(!infra_edns_update(sq->outnet->infra, &sq->addr,
|
||||
sq->addrlen, -1, time(0)))
|
||||
log_err("Out of memory caching no edns for host");
|
||||
sq->status = serviced_query_TCP;
|
||||
serviced_tcp_initiate(sq->outnet, sq, c->buffer);
|
||||
return 0;
|
||||
}
|
||||
/* insert address into reply info */
|
||||
if(!rep) {
|
||||
/* create one if there isn't (on errors) */
|
||||
rep = &r2;
|
||||
r2.c = c;
|
||||
}
|
||||
memcpy(&rep->addr, &sq->addr, sq->addrlen);
|
||||
rep->addrlen = sq->addrlen;
|
||||
serviced_callbacks(sq, error, c, rep);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
serviced_tcp_initiate(struct outside_network* outnet,
|
||||
struct serviced_query* sq, ldns_buffer* buff)
|
||||
{
|
||||
serviced_encode(sq, buff, sq->status == serviced_query_TCP_EDNS);
|
||||
sq->pending = pending_tcp_query(outnet, buff, &sq->addr,
|
||||
sq->addrlen, TCP_QUERY_TIMEOUT, serviced_tcp_callback,
|
||||
sq, outnet->rnd);
|
||||
if(!sq->pending) {
|
||||
/* delete from tree so that a retry by above layer does not
|
||||
* clash with this entry */
|
||||
log_err("serviced_tcp_initiate: failed to send tcp query");
|
||||
serviced_callbacks(sq, NETEVENT_CLOSED, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
serviced_udp_callback(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* rep)
|
||||
{
|
||||
struct serviced_query* sq = (struct serviced_query*)arg;
|
||||
struct outside_network* outnet = sq->outnet;
|
||||
struct timeval now;
|
||||
if(gettimeofday(&now, NULL) < 0) {
|
||||
log_err("gettimeofday: %s", strerror(errno));
|
||||
/* this option does not need current time */
|
||||
error = NETEVENT_CLOSED;
|
||||
}
|
||||
sq->pending = NULL; /* removed after callback */
|
||||
if(error == NETEVENT_TIMEOUT) {
|
||||
sq->retry++;
|
||||
if(!infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen,
|
||||
-1, (time_t)now.tv_sec))
|
||||
log_err("out of memory in UDP exponential backoff");
|
||||
if(sq->retry < OUTBOUND_UDP_RETRY) {
|
||||
log_name_addr(VERB_ALGO, "retry query", sq->qbuf+10,
|
||||
&sq->addr, sq->addrlen);
|
||||
if(!serviced_udp_send(sq, c->buffer)) {
|
||||
serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
error = NETEVENT_TIMEOUT;
|
||||
/* UDP does not work, fallback to TCP below */
|
||||
}
|
||||
if(error == NETEVENT_NOERROR && sq->status == serviced_query_UDP_EDNS
|
||||
&& LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer))
|
||||
== LDNS_RCODE_FORMERR) {
|
||||
/* note no EDNS, fallback without EDNS */
|
||||
if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen,
|
||||
-1, (time_t)now.tv_sec)) {
|
||||
log_err("Out of memory caching no edns for host");
|
||||
}
|
||||
sq->status = serviced_query_UDP;
|
||||
sq->retry = 0;
|
||||
if(!serviced_udp_send(sq, c->buffer)) {
|
||||
serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if(error != NETEVENT_NOERROR ||
|
||||
LDNS_TC_WIRE(ldns_buffer_begin(c->buffer))) {
|
||||
/* fallback to TCP */
|
||||
/* this discards partial UDP contents */
|
||||
if(sq->status == serviced_query_UDP_EDNS)
|
||||
sq->status = serviced_query_TCP_EDNS;
|
||||
else sq->status = serviced_query_TCP;
|
||||
serviced_tcp_initiate(outnet, sq, c->buffer);
|
||||
return 0;
|
||||
}
|
||||
/* yay! an answer */
|
||||
if(now.tv_sec > sq->last_sent_time.tv_sec ||
|
||||
(now.tv_sec == sq->last_sent_time.tv_sec &&
|
||||
now.tv_usec > sq->last_sent_time.tv_usec)) {
|
||||
/* convert from microseconds to milliseconds */
|
||||
int roundtime = (now.tv_sec - sq->last_sent_time.tv_sec)*1000
|
||||
+ ((int)now.tv_usec - (int)sq->last_sent_time.tv_usec)/1000;
|
||||
verbose(VERB_ALGO, "measured roundtrip at %d msec", roundtime);
|
||||
if(!infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen,
|
||||
roundtime, (time_t)now.tv_sec))
|
||||
log_err("out of memory noting rtt.");
|
||||
}
|
||||
serviced_callbacks(sq, error, c, rep);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** find callback in list */
|
||||
static struct service_callback*
|
||||
callback_list_find(struct serviced_query* sq, void* cb_arg,
|
||||
int (*arg_compare)(void*,void*))
|
||||
{
|
||||
struct service_callback* p;
|
||||
for(p = sq->cblist; p; p = p->next) {
|
||||
if(arg_compare(p->cb_arg, cb_arg))
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct serviced_query*
|
||||
outnet_serviced_query(struct outside_network* outnet,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
uint16_t flags, int dnssec, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, comm_point_callback_t* callback,
|
||||
void* callback_arg, ldns_buffer* buff,
|
||||
int (*arg_compare)(void*,void*))
|
||||
{
|
||||
struct serviced_query* sq;
|
||||
struct service_callback* cb;
|
||||
serviced_gen_query(buff, qname, qnamelen, qtype, qclass, flags);
|
||||
sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen);
|
||||
if(sq) {
|
||||
/* see if it is a duplicate notification request for cb_arg */
|
||||
if((cb = callback_list_find(sq, callback_arg, arg_compare))) {
|
||||
return sq;
|
||||
}
|
||||
}
|
||||
if(!(cb = (struct service_callback*)malloc(sizeof(*cb))))
|
||||
return NULL;
|
||||
if(!sq) {
|
||||
/* make new serviced query entry */
|
||||
sq = serviced_create(outnet, buff, dnssec, addr, addrlen);
|
||||
if(!sq) {
|
||||
free(cb);
|
||||
return NULL;
|
||||
}
|
||||
/* perform first network action */
|
||||
if(!serviced_udp_send(sq, buff)) {
|
||||
(void)rbtree_delete(outnet->serviced, sq);
|
||||
free(sq->qbuf);
|
||||
free(sq);
|
||||
free(cb);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/* add callback to list of callbacks */
|
||||
cb->cb = callback;
|
||||
cb->cb_arg = callback_arg;
|
||||
cb->next = sq->cblist;
|
||||
sq->cblist = cb;
|
||||
return sq;
|
||||
}
|
||||
|
||||
/** remove callback from list */
|
||||
static void
|
||||
callback_list_remove(struct serviced_query* sq, void* cb_arg)
|
||||
{
|
||||
struct service_callback** pp = &sq->cblist;
|
||||
while(*pp) {
|
||||
if((*pp)->cb_arg == cb_arg) {
|
||||
struct service_callback* del = *pp;
|
||||
*pp = del->next;
|
||||
free(del);
|
||||
return;
|
||||
}
|
||||
pp = &(*pp)->next;
|
||||
}
|
||||
}
|
||||
|
||||
void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg)
|
||||
{
|
||||
if(!sq)
|
||||
return;
|
||||
callback_list_remove(sq, cb_arg);
|
||||
/* if callbacks() routine scheduled deletion, let it do that */
|
||||
if(!sq->cblist && !sq->to_be_deleted) {
|
||||
rbnode_t* rem;
|
||||
rem = rbtree_delete(sq->outnet->serviced, sq);
|
||||
log_assert(rem); /* should be present */
|
||||
serviced_delete(sq);
|
||||
}
|
||||
}
|
||||
|
||||
/** get memory used by waiting tcp entry (in use or not) */
|
||||
static size_t
|
||||
waiting_tcp_get_mem(struct waiting_tcp* w)
|
||||
{
|
||||
size_t s;
|
||||
if(!w) return 0;
|
||||
s = sizeof(*w) + w->pkt_len;
|
||||
if(w->timer)
|
||||
s += comm_timer_get_mem(w->timer);
|
||||
return s;
|
||||
}
|
||||
|
||||
size_t outnet_get_mem(struct outside_network* outnet)
|
||||
{
|
||||
size_t i;
|
||||
struct waiting_tcp* w;
|
||||
struct serviced_query* sq;
|
||||
struct service_callback* sb;
|
||||
size_t s = sizeof(*outnet) + sizeof(*outnet->base) +
|
||||
sizeof(*outnet->udp_buff) +
|
||||
ldns_buffer_capacity(outnet->udp_buff);
|
||||
/* second buffer is not ours */
|
||||
s += sizeof(struct comm_point*)*outnet->num_udp4;
|
||||
for(i=0; i<outnet->num_udp4; i++)
|
||||
s += comm_point_get_mem(outnet->udp4_ports[i]);
|
||||
s += sizeof(struct comm_point*)*outnet->num_udp6;
|
||||
for(i=0; i<outnet->num_udp6; i++)
|
||||
s += comm_point_get_mem(outnet->udp6_ports[i]);
|
||||
s += sizeof(struct pending_tcp*)*outnet->num_tcp;
|
||||
for(i=0; i<outnet->num_tcp; i++) {
|
||||
s += sizeof(struct pending_tcp);
|
||||
s += comm_point_get_mem(outnet->tcp_conns[i]->c);
|
||||
if(outnet->tcp_conns[i]->query)
|
||||
s += waiting_tcp_get_mem(outnet->tcp_conns[i]->query);
|
||||
}
|
||||
for(w=outnet->tcp_wait_first; w; w = w->next_waiting)
|
||||
s += waiting_tcp_get_mem(w);
|
||||
s += sizeof(*outnet->pending);
|
||||
s += sizeof(struct pending) * outnet->pending->count;
|
||||
s += sizeof(*outnet->serviced);
|
||||
RBTREE_FOR(sq, struct serviced_query*, outnet->serviced) {
|
||||
s += sizeof(*sq) + sq->qbuflen;
|
||||
for(sb = sq->cblist; sb; sb = sb->next)
|
||||
s += sizeof(*sb);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
+147
-19
@@ -51,6 +51,7 @@ struct pending_timeout;
|
||||
struct ub_randstate;
|
||||
struct pending_tcp;
|
||||
struct waiting_tcp;
|
||||
struct infra_cache;
|
||||
|
||||
/**
|
||||
* Send queries to outside servers and wait for answers from servers.
|
||||
@@ -64,29 +65,41 @@ struct outside_network {
|
||||
datagram at any time. */
|
||||
ldns_buffer* udp_buff;
|
||||
|
||||
/** buffer for storage. (buffer for incoming connections, since
|
||||
* either an event to outside or incoming happens, but not both
|
||||
* This buffer is used during callbacks, so that the datagram
|
||||
* that just arrived does not collide with new datagrams sent out. */
|
||||
ldns_buffer* udp_second;
|
||||
|
||||
/**
|
||||
* Array of udp comm point* that are used to listen to pending events.
|
||||
* Each is on a different port. This is for ip4 ports.
|
||||
*/
|
||||
struct comm_point **udp4_ports;
|
||||
struct comm_point** udp4_ports;
|
||||
/** number of udp4 ports */
|
||||
size_t num_udp4;
|
||||
|
||||
/**
|
||||
* The opened ip6 ports.
|
||||
*/
|
||||
struct comm_point **udp6_ports;
|
||||
struct comm_point** udp6_ports;
|
||||
/** number of udp6 ports */
|
||||
size_t num_udp6;
|
||||
|
||||
/** pending udp answers. sorted by id, addr */
|
||||
rbtree_t *pending;
|
||||
rbtree_t* pending;
|
||||
/** serviced queries, sorted by qbuf, addr, dnssec */
|
||||
rbtree_t* serviced;
|
||||
/** host cache, pointer but not owned by outnet. */
|
||||
struct infra_cache* infra;
|
||||
/** where to get random numbers */
|
||||
struct ub_randstate* rnd;
|
||||
|
||||
/**
|
||||
* Array of tcp pending used for outgoing TCP connections.
|
||||
* Each can be used to establish a TCP connection with a server.
|
||||
* The file descriptors are -1 if its free, need to be opened for
|
||||
* the tcp connection. Can be used for ip4 and ip6.
|
||||
* The file descriptors are -1 if they are free, and need to be
|
||||
* opened for the tcp connection. Can be used for ip4 and ip6.
|
||||
*/
|
||||
struct pending_tcp **tcp_conns;
|
||||
/** number of tcp communication points. */
|
||||
@@ -170,6 +183,66 @@ struct waiting_tcp {
|
||||
void* cb_arg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to party interested in serviced query results.
|
||||
*/
|
||||
struct service_callback {
|
||||
/** next in callback list */
|
||||
struct service_callback* next;
|
||||
/** callback function */
|
||||
comm_point_callback_t* cb;
|
||||
/** user argument for callback function */
|
||||
void* cb_arg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Query service record.
|
||||
* Contains query and destination. UDP, TCP, EDNS are all tried.
|
||||
* complete with retries and timeouts. A number of interested parties can
|
||||
* receive a callback.
|
||||
*/
|
||||
struct serviced_query {
|
||||
/** The rbtree node, key is this record */
|
||||
rbnode_t node;
|
||||
/** The query that needs to be answered. Starts with flags u16,
|
||||
* then qdcount, ..., including qname, qtype, qclass. Does not include
|
||||
* EDNS record. */
|
||||
uint8_t* qbuf;
|
||||
/** length of qbuf. */
|
||||
size_t qbuflen;
|
||||
/** If an EDNS section is included, the DO bit will be turned on. */
|
||||
int dnssec;
|
||||
/** where to send it */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr field in use. */
|
||||
socklen_t addrlen;
|
||||
/** current status */
|
||||
enum serviced_query_status {
|
||||
/** initial status */
|
||||
serviced_initial,
|
||||
/** UDP with EDNS sent */
|
||||
serviced_query_UDP_EDNS,
|
||||
/** UDP without EDNS sent */
|
||||
serviced_query_UDP,
|
||||
/** TCP with EDNS sent */
|
||||
serviced_query_TCP_EDNS,
|
||||
/** TCP without EDNS sent */
|
||||
serviced_query_TCP
|
||||
} status;
|
||||
/** true if serviced_query is scheduled for deletion already */
|
||||
int to_be_deleted;
|
||||
/** number of UDP retries */
|
||||
int retry;
|
||||
/** time last UDP was sent */
|
||||
struct timeval last_sent_time;
|
||||
/** outside network this is part of */
|
||||
struct outside_network* outnet;
|
||||
/** list of interested parties that need callback on results. */
|
||||
struct service_callback* cblist;
|
||||
/** the UDP or TCP query that is pending, see status which */
|
||||
void* pending;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create outside_network structure with N udp ports.
|
||||
* @param base: the communication base to use for event handling.
|
||||
@@ -183,11 +256,14 @@ struct waiting_tcp {
|
||||
* @param port_base: if -1 system assigns ports, otherwise try to get
|
||||
* the ports numbered from this starting number.
|
||||
* @param num_tcp: number of outgoing tcp buffers to preallocate.
|
||||
* @param infra: pointer to infra cached used for serviced queries.
|
||||
* @param rnd: stored to create random numbers for serviced queries.
|
||||
* @return: the new structure (with no pending answers) or NULL on error.
|
||||
*/
|
||||
struct outside_network* outside_network_create(struct comm_base* base,
|
||||
size_t bufsize, size_t num_ports, char** ifs, int num_ifs,
|
||||
int do_ip4, int do_ip6, int port_base, size_t num_tcp);
|
||||
int do_ip4, int do_ip6, int port_base, size_t num_tcp,
|
||||
struct infra_cache* infra, struct ub_randstate* rnd);
|
||||
|
||||
/**
|
||||
* Delete outside_network structure.
|
||||
@@ -195,6 +271,16 @@ struct outside_network* outside_network_create(struct comm_base* base,
|
||||
*/
|
||||
void outside_network_delete(struct outside_network* outnet);
|
||||
|
||||
/**
|
||||
* Set secondary UDP buffer. Make sure it is not used during outside network
|
||||
* callbacks. Such as the incoming network UDP buffer. Caller responsible
|
||||
* for deletion.
|
||||
* @param outnet: outside network.
|
||||
* @param buf: buffer to use as secondary buffer.
|
||||
*/
|
||||
void outside_network_set_secondary_buffer(struct outside_network* outnet,
|
||||
ldns_buffer* buf);
|
||||
|
||||
/**
|
||||
* Send UDP query, create pending answer.
|
||||
* Changes the ID for the query to be random and unique for that destination.
|
||||
@@ -202,17 +288,16 @@ void outside_network_delete(struct outside_network* outnet);
|
||||
* @param packet: wireformat query to send to destination.
|
||||
* @param addr: address to send to.
|
||||
* @param addrlen: length of addr.
|
||||
* @param timeout: in seconds from now.
|
||||
* @param timeout: in milliseconds from now.
|
||||
* @param callback: function to call on error, timeout or reply.
|
||||
* The routine does not return an error, instead it calls the callback,
|
||||
* with an error code if an error happens.
|
||||
* @param callback_arg: user argument for callback function.
|
||||
* @param rnd: random state for generating ID and port.
|
||||
* @return: NULL on error for malloc or socket. Else the pending query object.
|
||||
*/
|
||||
void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
struct ub_randstate* rnd);
|
||||
struct pending* pending_udp_query(struct outside_network* outnet,
|
||||
ldns_buffer* packet, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, int timeout, comm_point_callback_t* callback,
|
||||
void* callback_arg, struct ub_randstate* rnd);
|
||||
|
||||
/**
|
||||
* Send TCP query. May wait for TCP buffer. Selects ID to be random, and
|
||||
@@ -225,15 +310,14 @@ void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
* Timer starts running now. Timer may expire if all buffers are used,
|
||||
* without any query been sent to the server yet.
|
||||
* @param callback: function to call on error, timeout or reply.
|
||||
* The routine does not return an error, instead it calls the callback,
|
||||
* with an error code if an error happens.
|
||||
* @param callback_arg: user argument for callback function.
|
||||
* @param rnd: random state for generating ID.
|
||||
* @return: false on error for malloc or socket. Else the pending TCP object.
|
||||
*/
|
||||
void pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
struct ub_randstate* rnd);
|
||||
struct waiting_tcp* pending_tcp_query(struct outside_network* outnet,
|
||||
ldns_buffer* packet, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, int timeout, comm_point_callback_t* callback,
|
||||
void* callback_arg, struct ub_randstate* rnd);
|
||||
|
||||
/**
|
||||
* Delete pending answer.
|
||||
@@ -243,4 +327,48 @@ void pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
*/
|
||||
void pending_delete(struct outside_network* outnet, struct pending* p);
|
||||
|
||||
/**
|
||||
* Perform a serviced query to the authoritative servers.
|
||||
* Duplicate efforts are detected, and EDNS, TCP and UDP retry is performed.
|
||||
* @param outnet: outside network, with rbtree of serviced queries.
|
||||
* @param qname: what qname to query.
|
||||
* @param qnamelen: length of qname in octets including 0 root label.
|
||||
* @param qtype: rrset type to query (host format)
|
||||
* @param qclass: query class. (host format)
|
||||
* @param flags: flags u16 (host format), includes opcode, CD bit.
|
||||
* @param dnssec: if set, DO bit is set in EDNS queries.
|
||||
* @param callback: callback function.
|
||||
* @param callback_arg: user argument to callback function.
|
||||
* @param addr: to which server to send the query.
|
||||
* @param addrlen: length of addr.
|
||||
* @param buff: scratch buffer to create query contents in. Empty on exit.
|
||||
* @param arg_compare: function to compare callback args, return true if
|
||||
* identical. It is given the callback_arg and args that are listed.
|
||||
* @return 0 on error, or pointer to serviced query that is used to answer
|
||||
* this serviced query may be shared with other callbacks as well.
|
||||
*/
|
||||
struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
uint16_t flags, int dnssec, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, comm_point_callback_t* callback,
|
||||
void* callback_arg, ldns_buffer* buff,
|
||||
int (*arg_compare)(void*,void*));
|
||||
|
||||
/**
|
||||
* Remove service query callback.
|
||||
* If that leads to zero callbacks, the query is completely cancelled.
|
||||
* @param sq: serviced query to adjust.
|
||||
* @param cb_arg: callback argument of callback that needs removal.
|
||||
* same as the callback_arg to outnet_serviced_query().
|
||||
*/
|
||||
void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg);
|
||||
|
||||
/**
|
||||
* Get memory size in use by outside network.
|
||||
* Counts buffers and outstanding query (serviced queries) malloced data.
|
||||
* @param outnet: outside network structure.
|
||||
* @return size in bytes.
|
||||
*/
|
||||
size_t outnet_get_mem(struct outside_network* outnet);
|
||||
|
||||
#endif /* OUTSIDE_NETWORK_H */
|
||||
|
||||
+105
-10
@@ -54,7 +54,7 @@
|
||||
/** How long to wait before lock attempt is a failure. */
|
||||
#define CHECK_LOCK_TIMEOUT 30 /* seconds */
|
||||
/** How long to wait before join attempt is a failure. */
|
||||
#define CHECK_JOIN_TIMEOUT 240 /* seconds */
|
||||
#define CHECK_JOIN_TIMEOUT 120 /* seconds */
|
||||
|
||||
/** if key has been created */
|
||||
static int key_created = 0;
|
||||
@@ -63,10 +63,13 @@ static ub_thread_key_t thr_debug_key;
|
||||
/** the list of threads, so all threads can be examined. NULL if unused. */
|
||||
static struct thr_check* thread_infos[THRDEBUG_MAX_THREADS];
|
||||
/** do we check locking order */
|
||||
int check_locking_order = 1;
|
||||
int check_locking_order = 0;
|
||||
/** the pid of this runset, reasonably unique. */
|
||||
static pid_t check_lock_pid;
|
||||
|
||||
/** print all possible debug info on the state of the system */
|
||||
static void total_debug_info();
|
||||
|
||||
/** print pretty lock error and exit */
|
||||
static void lock_error(struct checked_lock* lock,
|
||||
const char* func, const char* file, int line, const char* err)
|
||||
@@ -82,6 +85,8 @@ static void lock_error(struct checked_lock* lock,
|
||||
(lock->type==check_lock_mutex)?"mutex": (
|
||||
(lock->type==check_lock_spinlock)?"spinlock": (
|
||||
(lock->type==check_lock_rwlock)?"rwlock": "badtype")), err);
|
||||
log_err("complete status display:");
|
||||
total_debug_info();
|
||||
fatal_exit("bailing out");
|
||||
}
|
||||
|
||||
@@ -189,7 +194,7 @@ prot_check(struct checked_lock* lock,
|
||||
}
|
||||
}
|
||||
|
||||
/** Copy protected memory region. */
|
||||
/** Copy protected memory region */
|
||||
static void
|
||||
prot_store(struct checked_lock* lock)
|
||||
{
|
||||
@@ -200,7 +205,7 @@ prot_store(struct checked_lock* lock)
|
||||
}
|
||||
}
|
||||
|
||||
/** write lock trace info to file, while you hold those locks. */
|
||||
/** write lock trace info to file, while you hold those locks */
|
||||
static void
|
||||
ordercheck_locklock(struct thr_check* thr, struct checked_lock* lock)
|
||||
{
|
||||
@@ -220,7 +225,7 @@ ordercheck_locklock(struct thr_check* thr, struct checked_lock* lock)
|
||||
log_err("fwrite: %s", strerror(errno));
|
||||
}
|
||||
|
||||
/** write ordercheck lock creation details to file. */
|
||||
/** write ordercheck lock creation details to file */
|
||||
static void
|
||||
ordercheck_lockcreate(struct thr_check* thr, struct checked_lock* lock)
|
||||
{
|
||||
@@ -360,7 +365,7 @@ checklock_destroy(enum check_lock_type type, struct checked_lock** lock,
|
||||
free(e);
|
||||
}
|
||||
|
||||
/** finish acquiring lock, shared between _(rd|wr||)lock() routines. */
|
||||
/** finish acquiring lock, shared between _(rd|wr||)lock() routines */
|
||||
static void
|
||||
finish_acquire_lock(struct thr_check* thr, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
@@ -511,6 +516,7 @@ static int timed_spinlock(void* arg, struct timespec* to)
|
||||
#ifndef S_SPLINT_S
|
||||
if(time(NULL) >= to->tv_sec)
|
||||
return ETIMEDOUT;
|
||||
usleep(1000); /* in 1/1000000s of a second */
|
||||
#endif
|
||||
}
|
||||
return err;
|
||||
@@ -606,7 +612,7 @@ checklock_unlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
}
|
||||
}
|
||||
|
||||
/** open order info debug file, thr->num must be valid. */
|
||||
/** open order info debug file, thr->num must be valid */
|
||||
static void
|
||||
open_lockorder(struct thr_check* thr)
|
||||
{
|
||||
@@ -626,7 +632,7 @@ open_lockorder(struct thr_check* thr)
|
||||
log_err("fwrite: %s", strerror(errno));
|
||||
}
|
||||
|
||||
/** checklock thread main, Inits thread structure. */
|
||||
/** checklock thread main, Inits thread structure */
|
||||
static void* checklock_main(void* arg)
|
||||
{
|
||||
struct thr_check* thr = (struct thr_check*)arg;
|
||||
@@ -699,13 +705,102 @@ checklock_thrcreate(pthread_t* id, void* (*func)(void*), void* arg)
|
||||
LOCKRET(pthread_create(id, NULL, checklock_main, thr));
|
||||
}
|
||||
|
||||
/** signal handler for join timeout, Exits. */
|
||||
/** count number of thread infos */
|
||||
static int
|
||||
count_thread_infos()
|
||||
{
|
||||
int cnt = 0;
|
||||
int i;
|
||||
for(i=0; i<THRDEBUG_MAX_THREADS; i++)
|
||||
if(thread_infos[i])
|
||||
cnt++;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/** print lots of info on a lock */
|
||||
static void
|
||||
lock_debug_info(struct checked_lock* lock)
|
||||
{
|
||||
if(!lock) return;
|
||||
log_info("+++ Lock %x, %d %d create %s %s %d", (int)lock,
|
||||
lock->create_thread, lock->create_instance,
|
||||
lock->create_func, lock->create_file, lock->create_line);
|
||||
log_info("lock type: %s",
|
||||
(lock->type==check_lock_mutex)?"mutex": (
|
||||
(lock->type==check_lock_spinlock)?"spinlock": (
|
||||
(lock->type==check_lock_rwlock)?"rwlock": "badtype")));
|
||||
log_info("lock contention %u, history:%u, hold:%d, wait:%d",
|
||||
(unsigned)lock->contention_count, (unsigned)lock->history_count,
|
||||
lock->hold_count, lock->wait_count);
|
||||
log_info("last touch %s %s %d", lock->holder_func, lock->holder_file,
|
||||
lock->holder_line);
|
||||
log_info("holder thread %d, writeholder thread %d",
|
||||
lock->holder?lock->holder->num:-1,
|
||||
lock->writeholder?lock->writeholder->num:-1);
|
||||
}
|
||||
|
||||
/** print debug locks held by a thread */
|
||||
static void
|
||||
held_debug_info(struct thr_check* thr, struct checked_lock* lock)
|
||||
{
|
||||
if(!lock) return;
|
||||
lock_debug_info(lock);
|
||||
held_debug_info(thr, lock->next_held_lock[thr->num]);
|
||||
}
|
||||
|
||||
/** print debug info for a thread */
|
||||
static void
|
||||
thread_debug_info(struct thr_check* thr)
|
||||
{
|
||||
struct checked_lock* w = NULL;
|
||||
struct checked_lock* f = NULL;
|
||||
struct checked_lock* l = NULL;
|
||||
if(!thr) return;
|
||||
log_info("pthread id is %x", (int)thr->id);
|
||||
log_info("thread func is %x", (int)thr->func);
|
||||
log_info("thread arg is %x (%d)", (int)thr->arg,
|
||||
(thr->arg?*(int*)thr->arg:0));
|
||||
log_info("thread num is %d", thr->num);
|
||||
log_info("locks created %d", thr->locks_created);
|
||||
log_info("open file for lockinfo: %s",
|
||||
thr->order_info?"yes, flushing":"no");
|
||||
fflush(thr->order_info);
|
||||
w = thr->waiting;
|
||||
f = thr->holding_first;
|
||||
l = thr->holding_last;
|
||||
log_info("thread waiting for a lock: %s %x", w?"yes":"no", (int)w);
|
||||
lock_debug_info(w);
|
||||
log_info("thread holding first: %s, last: %s", f?"yes":"no",
|
||||
l?"yes":"no");
|
||||
held_debug_info(thr, f);
|
||||
}
|
||||
|
||||
static void
|
||||
total_debug_info()
|
||||
{
|
||||
int i;
|
||||
log_info("checklocks: supervising %d threads.",
|
||||
count_thread_infos());
|
||||
if(!key_created) {
|
||||
log_info("No thread debug key created yet");
|
||||
}
|
||||
for(i=0; i<THRDEBUG_MAX_THREADS; i++) {
|
||||
if(thread_infos[i]) {
|
||||
log_info("*** Thread %d information: ***", i);
|
||||
thread_debug_info(thread_infos[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** signal handler for join timeout, Exits */
|
||||
static RETSIGTYPE joinalarm(int ATTR_UNUSED(sig))
|
||||
{
|
||||
log_err("join thread timeout. hangup or deadlock. Info follows.");
|
||||
total_debug_info();
|
||||
fatal_exit("join thread timeout. hangup or deadlock.");
|
||||
}
|
||||
|
||||
/** wait for thread with a timeout. */
|
||||
/** wait for thread with a timeout */
|
||||
void
|
||||
checklock_thrjoin(pthread_t thread)
|
||||
{
|
||||
|
||||
+180
-10
@@ -48,6 +48,9 @@
|
||||
#include "testcode/fake_event.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "services/listen_dnsport.h"
|
||||
#include "services/outside_network.h"
|
||||
#include "testcode/replay.h"
|
||||
@@ -70,7 +73,7 @@ fake_event_cleanup()
|
||||
saved_scenario = NULL;
|
||||
}
|
||||
|
||||
/** helper function that logs a ldns_pkt packet to logfile. */
|
||||
/** helper function that logs a ldns_pkt packet to logfile */
|
||||
static void
|
||||
log_pkt(const char* desc, ldns_pkt* pkt)
|
||||
{
|
||||
@@ -140,6 +143,10 @@ pending_matches_current(struct replay_runtime* runtime,
|
||||
return 0;
|
||||
/* see if any of the pending queries matches */
|
||||
for(p = runtime->pending_list; p; p = p->next) {
|
||||
if(runtime->now->addrlen != 0 &&
|
||||
sockaddr_cmp(&p->addr, p->addrlen, &runtime->now->addr,
|
||||
runtime->now->addrlen) != 0)
|
||||
continue;
|
||||
if((e=find_match(runtime->now->match, p->pkt, p->transport))) {
|
||||
*entry = e;
|
||||
*pend = p;
|
||||
@@ -164,10 +171,14 @@ pending_find_match(struct replay_runtime* runtime, struct entry** entry,
|
||||
struct replay_range* p = runtime->scenario->range_list;
|
||||
while(p) {
|
||||
if(p->start_step <= timenow && timenow <= p->end_step &&
|
||||
(p->addrlen == 0 || sockaddr_cmp(&p->addr, p->addrlen,
|
||||
&pend->addr, pend->addrlen) == 0) &&
|
||||
(*entry = find_match(p->match, pend->pkt, pend->transport))) {
|
||||
log_info("matched query time %d in range [%d, %d] "
|
||||
"with entry line %d", timenow,
|
||||
p->start_step, p->end_step, (*entry)->lineno);
|
||||
if(p->addrlen != 0)
|
||||
log_addr("matched ip", &p->addr, p->addrlen);
|
||||
return 1;
|
||||
}
|
||||
p = p->next_range;
|
||||
@@ -189,6 +200,7 @@ pending_matches_range(struct replay_runtime* runtime,
|
||||
struct fake_pending* p = runtime->pending_list;
|
||||
/* slow, O(N*N), but it works as advertised with weird matching */
|
||||
while(p) {
|
||||
log_info("check of pending");
|
||||
if(pending_find_match(runtime, entry, p)) {
|
||||
*pend = p;
|
||||
return 1;
|
||||
@@ -262,6 +274,10 @@ answer_callback_from_entry(struct replay_runtime* runtime,
|
||||
struct entry* entry, struct fake_pending* pend)
|
||||
{
|
||||
struct comm_point c;
|
||||
struct comm_reply repinfo;
|
||||
void* cb_arg = pend->cb_arg;
|
||||
comm_point_callback_t* cb = pend->callback;
|
||||
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.fd = -1;
|
||||
c.buffer = ldns_buffer_new(runtime->bufsize);
|
||||
@@ -269,11 +285,15 @@ answer_callback_from_entry(struct replay_runtime* runtime,
|
||||
if(pend->transport == transport_tcp)
|
||||
c.type = comm_tcp;
|
||||
fill_buffer_with_reply(c.buffer, entry, pend->pkt);
|
||||
if((*pend->callback)(&c, pend->cb_arg, NETEVENT_NOERROR, NULL)) {
|
||||
repinfo.c = &c;
|
||||
repinfo.addrlen = pend->addrlen;
|
||||
memcpy(&repinfo.addr, &pend->addr, pend->addrlen);
|
||||
if(!pend->serviced)
|
||||
pending_list_delete(runtime, pend);
|
||||
if((*cb)(&c, cb_arg, NETEVENT_NOERROR, &repinfo)) {
|
||||
fatal_exit("testbound: unexpected: callback returned 1");
|
||||
}
|
||||
ldns_buffer_free(c.buffer);
|
||||
pending_list_delete(runtime, pend);
|
||||
}
|
||||
|
||||
/** Check the now moment answer check event */
|
||||
@@ -288,7 +308,10 @@ answer_check_it(struct replay_runtime* runtime)
|
||||
enum transport_type tr = transport_tcp;
|
||||
if(ans->repinfo.c->type == comm_udp)
|
||||
tr = transport_udp;
|
||||
if(find_match(runtime->now->match, ans->pkt, tr)) {
|
||||
if((runtime->now->addrlen == 0 || sockaddr_cmp(
|
||||
&runtime->now->addr, runtime->now->addrlen,
|
||||
&ans->repinfo.addr, ans->repinfo.addrlen) == 0) &&
|
||||
find_match(runtime->now->match, ans->pkt, tr)) {
|
||||
struct replay_answer *n = ans->next;
|
||||
log_info("testbound matched event entry from line %d",
|
||||
runtime->now->match->lineno);
|
||||
@@ -323,6 +346,10 @@ fake_front_query(struct replay_runtime* runtime, struct replay_moment *todo)
|
||||
memset(&repinfo, 0, sizeof(repinfo));
|
||||
repinfo.c = (struct comm_point*)calloc(1, sizeof(struct comm_point));
|
||||
repinfo.addrlen = (socklen_t)sizeof(struct sockaddr_in);
|
||||
if(todo->addrlen != 0) {
|
||||
repinfo.addrlen = todo->addrlen;
|
||||
memcpy(&repinfo.addr, &todo->addr, todo->addrlen);
|
||||
}
|
||||
repinfo.c->fd = -1;
|
||||
repinfo.c->ev = (struct internal_event*)runtime;
|
||||
repinfo.c->buffer = ldns_buffer_new(runtime->bufsize);
|
||||
@@ -347,6 +374,7 @@ fake_pending_callback(struct replay_runtime* runtime,
|
||||
struct replay_moment* todo, int error)
|
||||
{
|
||||
struct fake_pending* p = runtime->pending_list;
|
||||
struct comm_reply repinfo;
|
||||
struct comm_point c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
if(!p) fatal_exit("No pending queries.");
|
||||
@@ -357,8 +385,11 @@ fake_pending_callback(struct replay_runtime* runtime,
|
||||
c.type = comm_tcp;
|
||||
if(todo->evt_type == repevt_back_reply && todo->match) {
|
||||
fill_buffer_with_reply(c.buffer, todo->match, p->pkt);
|
||||
}
|
||||
if((*p->callback)(&c, p->cb_arg, error, NULL)) {
|
||||
}
|
||||
repinfo.c = &c;
|
||||
repinfo.addrlen = p->addrlen;
|
||||
memcpy(&repinfo.addr, &p->addr, p->addrlen);
|
||||
if((*p->callback)(&c, p->cb_arg, error, &repinfo)) {
|
||||
fatal_exit("unexpected: pending callback returned 1");
|
||||
}
|
||||
/* delete the pending item. */
|
||||
@@ -473,6 +504,10 @@ run_scenario(struct replay_runtime* runtime)
|
||||
} while(runtime->now);
|
||||
|
||||
if(runtime->pending_list) {
|
||||
struct fake_pending* p;
|
||||
log_err("testbound: there are still messages pending.");
|
||||
for(p = runtime->pending_list; p; p=p->next)
|
||||
log_pkt("pending msg", p->pkt);
|
||||
fatal_exit("testbound: there are still messages pending.");
|
||||
}
|
||||
if(runtime->answer_list) {
|
||||
@@ -634,7 +669,8 @@ outside_network_create(struct comm_base* base, size_t bufsize,
|
||||
size_t ATTR_UNUSED(num_ports), char** ATTR_UNUSED(ifs),
|
||||
int ATTR_UNUSED(num_ifs), int ATTR_UNUSED(do_ip4),
|
||||
int ATTR_UNUSED(do_ip6), int ATTR_UNUSED(port_base),
|
||||
size_t ATTR_UNUSED(num_tcp))
|
||||
size_t ATTR_UNUSED(num_tcp), struct infra_cache* ATTR_UNUSED(infra),
|
||||
struct ub_randstate* ATTR_UNUSED(rnd))
|
||||
{
|
||||
struct outside_network* outnet = calloc(1,
|
||||
sizeof(struct outside_network));
|
||||
@@ -656,7 +692,14 @@ outside_network_delete(struct outside_network* outnet)
|
||||
free(outnet);
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
outside_network_set_secondary_buffer(struct outside_network*
|
||||
ATTR_UNUSED(outnet), ldns_buffer* ATTR_UNUSED(buf))
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
struct pending*
|
||||
pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
@@ -676,9 +719,11 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
pend->addrlen = addrlen;
|
||||
pend->callback = callback;
|
||||
pend->cb_arg = callback_arg;
|
||||
pend->timeout = timeout;
|
||||
pend->timeout = timeout/1000;
|
||||
pend->transport = transport_udp;
|
||||
pend->pkt = NULL;
|
||||
pend->serviced = 0;
|
||||
pend->runtime = runtime;
|
||||
status = ldns_buffer2pkt_wire(&pend->pkt, packet);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
log_err("ldns error parsing udp output packet: %s",
|
||||
@@ -689,6 +734,9 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
|
||||
/* see if it matches the current moment */
|
||||
if(runtime->now && runtime->now->evt_type == repevt_back_query &&
|
||||
(runtime->now->addrlen == 0 || sockaddr_cmp(
|
||||
&runtime->now->addr, runtime->now->addrlen,
|
||||
&pend->addr, pend->addrlen) == 0) &&
|
||||
find_match(runtime->now->match, pend->pkt, pend->transport)) {
|
||||
log_info("testbound: matched pending to event. "
|
||||
"advance time between events.");
|
||||
@@ -701,9 +749,10 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
/* add to list */
|
||||
pend->next = runtime->pending_list;
|
||||
runtime->pending_list = pend;
|
||||
return (struct pending*)pend;
|
||||
}
|
||||
|
||||
void
|
||||
struct waiting_tcp*
|
||||
pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
@@ -726,6 +775,8 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
pend->timeout = timeout;
|
||||
pend->transport = transport_tcp;
|
||||
pend->pkt = NULL;
|
||||
pend->runtime = runtime;
|
||||
pend->serviced = 0;
|
||||
status = ldns_buffer2pkt_wire(&pend->pkt, packet);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
log_err("ldns error parsing tcp output packet: %s",
|
||||
@@ -736,6 +787,9 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
|
||||
/* see if it matches the current moment */
|
||||
if(runtime->now && runtime->now->evt_type == repevt_back_query &&
|
||||
(runtime->now->addrlen == 0 || sockaddr_cmp(
|
||||
&runtime->now->addr, runtime->now->addrlen,
|
||||
&pend->addr, pend->addrlen) == 0) &&
|
||||
find_match(runtime->now->match, pend->pkt, pend->transport)) {
|
||||
log_info("testbound: matched pending to event. "
|
||||
"advance time between events.");
|
||||
@@ -748,6 +802,107 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
/* add to list */
|
||||
pend->next = runtime->pending_list;
|
||||
runtime->pending_list = pend;
|
||||
return (struct waiting_tcp*)pend;
|
||||
}
|
||||
|
||||
struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
uint16_t flags, int dnssec, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, comm_point_callback_t* callback,
|
||||
void* callback_arg, ldns_buffer* ATTR_UNUSED(buff),
|
||||
int (*arg_compare)(void*,void*))
|
||||
{
|
||||
struct replay_runtime* runtime = (struct replay_runtime*)outnet->base;
|
||||
struct fake_pending* pend = (struct fake_pending*)calloc(1,
|
||||
sizeof(struct fake_pending));
|
||||
ldns_status status;
|
||||
(void)arg_compare;
|
||||
log_assert(pend);
|
||||
|
||||
/* create packet with EDNS */
|
||||
pend->buffer = ldns_buffer_new(512);
|
||||
log_assert(pend->buffer);
|
||||
ldns_buffer_write_u16(pend->buffer, 0); /* id */
|
||||
ldns_buffer_write_u16(pend->buffer, flags);
|
||||
ldns_buffer_write_u16(pend->buffer, 1); /* qdcount */
|
||||
ldns_buffer_write_u16(pend->buffer, 0); /* ancount */
|
||||
ldns_buffer_write_u16(pend->buffer, 0); /* nscount */
|
||||
ldns_buffer_write_u16(pend->buffer, 0); /* arcount */
|
||||
ldns_buffer_write(pend->buffer, qname, qnamelen);
|
||||
ldns_buffer_write_u16(pend->buffer, qtype);
|
||||
ldns_buffer_write_u16(pend->buffer, qclass);
|
||||
ldns_buffer_flip(pend->buffer);
|
||||
if(1) {
|
||||
/* add edns */
|
||||
struct edns_data edns;
|
||||
edns.edns_present = 1;
|
||||
edns.ext_rcode = 0;
|
||||
edns.edns_version = EDNS_ADVERTISED_VERSION;
|
||||
edns.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns.bits = 0;
|
||||
if(dnssec)
|
||||
edns.bits = EDNS_DO;
|
||||
attach_edns_record(pend->buffer, &edns);
|
||||
}
|
||||
memcpy(&pend->addr, addr, addrlen);
|
||||
pend->addrlen = addrlen;
|
||||
pend->callback = callback;
|
||||
pend->cb_arg = callback_arg;
|
||||
pend->timeout = UDP_QUERY_TIMEOUT;
|
||||
pend->transport = transport_udp; /* pretend UDP */
|
||||
pend->pkt = NULL;
|
||||
pend->runtime = runtime;
|
||||
pend->serviced = 1;
|
||||
status = ldns_buffer2pkt_wire(&pend->pkt, pend->buffer);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
log_err("ldns error parsing serviced output packet: %s",
|
||||
ldns_get_errorstr_by_id(status));
|
||||
fatal_exit("internal error");
|
||||
}
|
||||
log_pkt("pending serviced query: ", pend->pkt);
|
||||
|
||||
/* see if it matches the current moment */
|
||||
if(runtime->now && runtime->now->evt_type == repevt_back_query &&
|
||||
(runtime->now->addrlen == 0 || sockaddr_cmp(
|
||||
&runtime->now->addr, runtime->now->addrlen,
|
||||
&pend->addr, pend->addrlen) == 0) &&
|
||||
find_match(runtime->now->match, pend->pkt, pend->transport)) {
|
||||
log_info("testbound: matched pending to event. "
|
||||
"advance time between events.");
|
||||
log_info("testbound: do STEP %d %s", runtime->now->time_step,
|
||||
repevt_string(runtime->now->evt_type));
|
||||
advance_moment(runtime);
|
||||
/* still create the pending, because we need it to callback */
|
||||
}
|
||||
log_info("testbound: created fake pending");
|
||||
/* add to list */
|
||||
pend->next = runtime->pending_list;
|
||||
runtime->pending_list = pend;
|
||||
return (struct serviced_query*)pend;
|
||||
}
|
||||
|
||||
void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg)
|
||||
{
|
||||
struct fake_pending* pend = (struct fake_pending*)sq;
|
||||
struct replay_runtime* runtime = pend->runtime;
|
||||
/* delete from the list */
|
||||
struct fake_pending* p = runtime->pending_list, *prev=NULL;
|
||||
while(p) {
|
||||
if(p == pend) {
|
||||
log_assert(p->cb_arg == cb_arg);
|
||||
log_info("serviced pending delete");
|
||||
if(prev)
|
||||
prev->next = p->next;
|
||||
else runtime->pending_list = p->next;
|
||||
ldns_buffer_free(p->buffer);
|
||||
ldns_pkt_free(p->pkt);
|
||||
free(p);
|
||||
return;
|
||||
}
|
||||
prev = p;
|
||||
p = p->next;
|
||||
}
|
||||
log_info("double delete of pending serviced query");
|
||||
}
|
||||
|
||||
struct listen_port* listening_ports_open(struct config_file* ATTR_UNUSED(cfg))
|
||||
@@ -782,4 +937,19 @@ void comm_point_delete(struct comm_point* c)
|
||||
free(c);
|
||||
}
|
||||
|
||||
size_t listen_get_mem(struct listen_dnsport* ATTR_UNUSED(listen))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t outnet_get_mem(struct outside_network* ATTR_UNUSED(outnet))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t comm_point_get_mem(struct comm_point* ATTR_UNUSED(c))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*********** End of Dummy routines ***********/
|
||||
|
||||
@@ -31,7 +31,7 @@ struct sockaddr_storage;
|
||||
/** string to show in warnings and errors */
|
||||
static const char* prog_name = "ldns-testpkts";
|
||||
|
||||
/** logging routine, provided by caller. */
|
||||
/** logging routine, provided by caller */
|
||||
void verbose(int lvl, const char* msg, ...);
|
||||
|
||||
/** print error and exit */
|
||||
@@ -108,6 +108,8 @@ static void matchline(const char* line, struct entry* e)
|
||||
e->match_all = true;
|
||||
} else if(str_keyword(&parse, "ttl")) {
|
||||
e->match_ttl = true;
|
||||
} else if(str_keyword(&parse, "DO")) {
|
||||
e->match_do = true;
|
||||
} else if(str_keyword(&parse, "UDP")) {
|
||||
e->match_transport = transport_udp;
|
||||
} else if(str_keyword(&parse, "TCP")) {
|
||||
@@ -155,6 +157,8 @@ static void replyline(const char* line, ldns_pkt *reply)
|
||||
ldns_pkt_set_rcode(reply, LDNS_RCODE_NXDOMAIN);
|
||||
} else if(str_keyword(&parse, "NOTIMPL")) {
|
||||
ldns_pkt_set_rcode(reply, LDNS_RCODE_NOTIMPL);
|
||||
} else if(str_keyword(&parse, "REFUSED")) {
|
||||
ldns_pkt_set_rcode(reply, LDNS_RCODE_REFUSED);
|
||||
} else if(str_keyword(&parse, "YXDOMAIN")) {
|
||||
ldns_pkt_set_rcode(reply, LDNS_RCODE_YXDOMAIN);
|
||||
} else if(str_keyword(&parse, "YXRRSET")) {
|
||||
@@ -220,6 +224,7 @@ static struct entry* new_entry()
|
||||
e->match_qname = false;
|
||||
e->match_all = false;
|
||||
e->match_ttl = false;
|
||||
e->match_do = false;
|
||||
e->match_serial = false;
|
||||
e->ixfr_soa_serial = 0;
|
||||
e->match_transport = transport_any;
|
||||
@@ -394,7 +399,7 @@ get_origin(const char* name, int lineno, ldns_rdf** origin, char* parse)
|
||||
|
||||
/* Reads one entry from file. Returns entry or NULL on error. */
|
||||
struct entry*
|
||||
read_entry(FILE* in, const char* name, int *lineno, uint16_t* default_ttl,
|
||||
read_entry(FILE* in, const char* name, int *lineno, uint32_t* default_ttl,
|
||||
ldns_rdf** origin, ldns_rdf** prev_rr)
|
||||
{
|
||||
struct entry* current = NULL;
|
||||
@@ -428,7 +433,7 @@ read_entry(FILE* in, const char* name, int *lineno, uint16_t* default_ttl,
|
||||
get_origin(name, *lineno, origin, (char*)parse);
|
||||
continue;
|
||||
} else if(str_keyword(&parse, "$TTL")) {
|
||||
*default_ttl = (uint16_t)atoi(parse);
|
||||
*default_ttl = (uint32_t)atoi(parse);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -502,7 +507,7 @@ read_datafile(const char* name)
|
||||
struct entry* current = NULL;
|
||||
FILE *in;
|
||||
int lineno = 0;
|
||||
uint16_t default_ttl = 0;
|
||||
uint32_t default_ttl = 0;
|
||||
ldns_rdf* origin = NULL;
|
||||
ldns_rdf* prev_rr = NULL;
|
||||
int entry_num = 0;
|
||||
@@ -663,6 +668,10 @@ find_match(struct entry* entries, ldns_pkt* query_pkt,
|
||||
verbose(3, "bad serial\n");
|
||||
continue;
|
||||
}
|
||||
if(p->match_do && !ldns_pkt_edns_do(query_pkt)) {
|
||||
verbose(3, "no DO bit set\n");
|
||||
continue;
|
||||
}
|
||||
if(p->match_transport != transport_any && p->match_transport != transport) {
|
||||
verbose(3, "bad transport\n");
|
||||
continue;
|
||||
|
||||
@@ -45,8 +45,9 @@
|
||||
; 'serial=1023' makes the query match if ixfr serial is 1023.
|
||||
; 'all' has to match header byte for byte and all rrs in packet.
|
||||
; 'ttl' used with all, rrs in packet must also have matching TTLs.
|
||||
; 'DO' will match only queries with DO bit set.
|
||||
MATCH [opcode] [qtype] [qname] [serial=<value>] [all] [ttl]
|
||||
MATCH [UDP|TCP]
|
||||
MATCH [UDP|TCP] DO
|
||||
MATCH ...
|
||||
; Then the REPLY header is specified.
|
||||
REPLY opcode, rcode or flags.
|
||||
@@ -160,6 +161,8 @@ struct entry {
|
||||
bool match_all;
|
||||
/** match ttls in the packet */
|
||||
bool match_ttl;
|
||||
/** match DO bit */
|
||||
bool match_do;
|
||||
/** match query serial with this value. */
|
||||
uint32_t ixfr_soa_serial;
|
||||
/** match on UDP/TCP */
|
||||
@@ -207,7 +210,7 @@ void delete_entry(struct entry* list);
|
||||
* @return: The entry read (malloced) or NULL if no entry could be read.
|
||||
*/
|
||||
struct entry* read_entry(FILE* in, const char* name, int *lineno,
|
||||
uint16_t* default_ttl, ldns_rdf** origin, ldns_rdf** prev_rr);
|
||||
uint32_t* default_ttl, ldns_rdf** origin, ldns_rdf** prev_rr);
|
||||
|
||||
/**
|
||||
* finds entry in list, or returns NULL.
|
||||
|
||||
@@ -139,6 +139,9 @@ read_header(FILE* in)
|
||||
the_time = t;
|
||||
the_pid = p;
|
||||
memset(threads, 0, 256*sizeof(int));
|
||||
if(thrno >= 256) {
|
||||
fatal_exit("Thread number too big. %d", thrno);
|
||||
}
|
||||
threads[thrno] = 1;
|
||||
have_values = 1;
|
||||
printf(" trace %d from pid %u on %s", thrno,
|
||||
@@ -306,7 +309,7 @@ static void found_cycle(struct lock_ref* visit, int level)
|
||||
}
|
||||
}
|
||||
|
||||
/** Detect cycle by comparing visited now with all (unvisited) bigger nodes. */
|
||||
/** Detect cycle by comparing visited now with all (unvisited) bigger nodes */
|
||||
static int detect_cycle(struct lock_ref* visit, struct lock_ref* from)
|
||||
{
|
||||
struct lock_ref* p = from;
|
||||
@@ -332,7 +335,7 @@ static void search_cycle(struct lock_ref* visit, int level,
|
||||
/* check for cycle */
|
||||
if(detect_cycle(visit, from) && level != 0) {
|
||||
found_cycle(visit, level);
|
||||
return;
|
||||
fatal_exit("found lock order cycle");
|
||||
}
|
||||
/* recurse */
|
||||
if(!visit->lock->visited)
|
||||
|
||||
+2
-2
@@ -67,7 +67,7 @@ void read_input(ldns_buffer* pkt, FILE* in)
|
||||
hex_to_buf(pkt, buf);
|
||||
}
|
||||
|
||||
/** analyze domain name in packet, possibly compressed. */
|
||||
/** analyze domain name in packet, possibly compressed */
|
||||
void analyze_dname(ldns_buffer* pkt)
|
||||
{
|
||||
size_t oldpos = ldns_buffer_position(pkt);
|
||||
@@ -117,7 +117,7 @@ void analyze_rdata(ldns_buffer*pkt, const ldns_rr_descriptor* desc,
|
||||
ldns_buffer_skip(pkt, (ssize_t)rdlen);
|
||||
}
|
||||
|
||||
/** analyze rr in packet. */
|
||||
/** analyze rr in packet */
|
||||
void analyze_rr(ldns_buffer* pkt, int q)
|
||||
{
|
||||
uint16_t type, dclass, len;
|
||||
|
||||
+40
-4
@@ -41,6 +41,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "testcode/replay.h"
|
||||
#include "testcode/ldns-testpkts.h"
|
||||
|
||||
@@ -85,6 +86,18 @@ replay_range_delete(struct replay_range* rng)
|
||||
free(rng);
|
||||
}
|
||||
|
||||
/** strip whitespace from end of string */
|
||||
static void
|
||||
strip_end_white(char* p)
|
||||
{
|
||||
size_t i;
|
||||
for(i = strlen(p); i > 0; i--) {
|
||||
if(isspace(p[i-1]))
|
||||
p[i-1] = 0;
|
||||
else return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a range from file.
|
||||
* @param remain: Rest of line (after RANGE keyword).
|
||||
@@ -99,7 +112,7 @@ replay_range_delete(struct replay_range* rng)
|
||||
*/
|
||||
static struct replay_range*
|
||||
replay_range_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
char* line, uint16_t* ttl, ldns_rdf** or, ldns_rdf** prev)
|
||||
char* line, uint32_t* ttl, ldns_rdf** or, ldns_rdf** prev)
|
||||
{
|
||||
struct replay_range* rng = (struct replay_range*)malloc(
|
||||
sizeof(struct replay_range));
|
||||
@@ -124,6 +137,19 @@ replay_range_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
parse++;
|
||||
if(!*parse || *parse == ';')
|
||||
continue;
|
||||
if(parse_keyword(&parse, "ADDRESS")) {
|
||||
while(isspace(*parse))
|
||||
parse++;
|
||||
strip_end_white(parse);
|
||||
if(!extstrtoaddr(parse, &rng->addr, &rng->addrlen)) {
|
||||
log_err("Line %d: could not read ADDRESS: %s",
|
||||
*lineno, parse);
|
||||
free(rng);
|
||||
return NULL;
|
||||
}
|
||||
pos = ftello(in);
|
||||
continue;
|
||||
}
|
||||
if(parse_keyword(&parse, "RANGE_END")) {
|
||||
return rng;
|
||||
}
|
||||
@@ -158,7 +184,7 @@ replay_range_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
*/
|
||||
static struct replay_moment*
|
||||
replay_moment_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
uint16_t* ttl, ldns_rdf** or, ldns_rdf** prev)
|
||||
uint32_t* ttl, ldns_rdf** or, ldns_rdf** prev)
|
||||
{
|
||||
struct replay_moment* mom = (struct replay_moment*)malloc(
|
||||
sizeof(struct replay_moment));
|
||||
@@ -198,6 +224,16 @@ replay_moment_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
free(mom);
|
||||
return NULL;
|
||||
}
|
||||
while(isspace(*remain))
|
||||
remain++;
|
||||
if(parse_keyword(&remain, "ADDRESS")) {
|
||||
if(!extstrtoaddr(remain, &mom->addr, &mom->addrlen)) {
|
||||
log_err("line %d: could not parse ADDRESS: %s",
|
||||
*lineno, remain);
|
||||
free(mom);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if(readentry) {
|
||||
mom->match = read_entry(in, name, lineno, ttl, or, prev);
|
||||
@@ -210,7 +246,7 @@ replay_moment_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
return mom;
|
||||
}
|
||||
|
||||
/** makes scenario with title on rest of line. */
|
||||
/** makes scenario with title on rest of line */
|
||||
static struct replay_scenario*
|
||||
make_scenario(char* line)
|
||||
{
|
||||
@@ -239,7 +275,7 @@ replay_scenario_read(FILE* in, const char* name, int* lineno)
|
||||
char line[MAX_LINE_LEN];
|
||||
char *parse;
|
||||
struct replay_scenario* scen = NULL;
|
||||
uint16_t ttl = 3600;
|
||||
uint32_t ttl = 3600;
|
||||
ldns_rdf* or = NULL;
|
||||
ldns_rdf* prev = NULL;
|
||||
line[MAX_LINE_LEN-1]=0;
|
||||
|
||||
+16
-1
@@ -44,11 +44,13 @@
|
||||
* ; comment line.
|
||||
* SCENARIO_BEGIN name_of_scenario
|
||||
* RANGE_BEGIN start_time end_time
|
||||
* ; give ip of the virtual server, it matches any ip if not present.
|
||||
* ADDRESS ip_address
|
||||
* match_entries
|
||||
* RANGE_END
|
||||
* ; more RANGE items.
|
||||
* ; go to the next moment
|
||||
* STEP time_step event_type
|
||||
* STEP time_step event_type [ADDRESS ip_address]
|
||||
* ; event_type can be:
|
||||
* o NOTHING - nothing
|
||||
* o QUERY - followed by entry
|
||||
@@ -158,6 +160,11 @@ struct replay_moment {
|
||||
/** The sent packet must match this. Incoming events, the data. */
|
||||
struct entry* match;
|
||||
|
||||
/** address that must be matched, or packet remote host address. */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr, if 0, then any address will do */
|
||||
socklen_t addrlen;
|
||||
|
||||
/** what pending query should timeout or is answered. or
|
||||
* NULL for last sent query.
|
||||
* Unused at this time.
|
||||
@@ -173,6 +180,10 @@ struct replay_range {
|
||||
int start_step;
|
||||
/** end step of time range. */
|
||||
int end_step;
|
||||
/** address of where this range is served. */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr, if 0, then any address will do */
|
||||
socklen_t addrlen;
|
||||
|
||||
/** Matching list */
|
||||
struct entry* match;
|
||||
@@ -245,6 +256,10 @@ struct fake_pending {
|
||||
ldns_pkt* pkt;
|
||||
/** by what transport was the query sent out */
|
||||
enum transport_type transport;
|
||||
/** if this is a serviced query */
|
||||
int serviced;
|
||||
/** the runtime structure this is part of */
|
||||
struct replay_runtime* runtime;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -105,7 +105,7 @@ add_opts(char* optarg, int* pass_argc, char* pass_argv[])
|
||||
}
|
||||
}
|
||||
|
||||
/** pretty print commandline for unbound in this test. */
|
||||
/** pretty print commandline for unbound in this test */
|
||||
static void
|
||||
echo_cmdline(int argc, char* argv[])
|
||||
{
|
||||
|
||||
@@ -361,6 +361,78 @@ dname_test_dname_lab_cmp()
|
||||
unit_assert(ml == 4);
|
||||
}
|
||||
|
||||
/** test dname_subdomain_c */
|
||||
static void
|
||||
dname_test_subdomain()
|
||||
{
|
||||
unit_assert(dname_subdomain_c(
|
||||
(uint8_t*)"",
|
||||
(uint8_t*)""));
|
||||
unit_assert(dname_subdomain_c(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)""));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"",
|
||||
(uint8_t*)"\003com"));
|
||||
unit_assert(dname_subdomain_c(
|
||||
(uint8_t*)"\007example\003com",
|
||||
(uint8_t*)"\003com"));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)"\007example\003com"));
|
||||
unit_assert(dname_subdomain_c(
|
||||
(uint8_t*)"\007example\003com",
|
||||
(uint8_t*)""));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\003com"));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\003org"));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\007example\003net",
|
||||
(uint8_t*)"\003org"));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\007example\003org"));
|
||||
}
|
||||
|
||||
/** test dname_strict_subdomain */
|
||||
static void
|
||||
dname_test_strict_subdomain()
|
||||
{
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"", 1,
|
||||
(uint8_t*)"", 1));
|
||||
unit_assert(dname_strict_subdomain(
|
||||
(uint8_t*)"\003com", 2,
|
||||
(uint8_t*)"", 1));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"", 1,
|
||||
(uint8_t*)"\003com", 2));
|
||||
unit_assert(dname_strict_subdomain(
|
||||
(uint8_t*)"\007example\003com", 3,
|
||||
(uint8_t*)"\003com", 2));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\003com", 2,
|
||||
(uint8_t*)"\007example\003com", 3));
|
||||
unit_assert(dname_strict_subdomain(
|
||||
(uint8_t*)"\007example\003com", 3,
|
||||
(uint8_t*)"", 1));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\003net", 2,
|
||||
(uint8_t*)"\003com", 2));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\003net", 2,
|
||||
(uint8_t*)"\003org", 2));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\007example\003net", 3,
|
||||
(uint8_t*)"\003org", 2));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\003net", 2,
|
||||
(uint8_t*)"\007example\003org", 3));
|
||||
}
|
||||
|
||||
void dname_test()
|
||||
{
|
||||
ldns_buffer* buff = ldns_buffer_new(65800);
|
||||
@@ -372,5 +444,7 @@ void dname_test()
|
||||
dname_test_count_size_labels();
|
||||
dname_test_dname_lab_cmp();
|
||||
dname_test_pkt_dname_len(buff);
|
||||
dname_test_strict_subdomain();
|
||||
dname_test_subdomain();
|
||||
ldns_buffer_free(buff);
|
||||
}
|
||||
|
||||
@@ -62,14 +62,14 @@ static size_t test_sizefunc(void*, void*);
|
||||
/** comparefunc for lruhash */
|
||||
static int test_compfunc(void*, void*);
|
||||
/** delkey for lruhash */
|
||||
static void test_delkey(void*, void*);
|
||||
static void test_delkey(void*, void*, int);
|
||||
/** deldata for lruhash */
|
||||
static void test_deldata(void*, void*);
|
||||
/* --- end test representation --- */
|
||||
|
||||
/** hash func, very bad to improve collisions. */
|
||||
/** hash func, very bad to improve collisions */
|
||||
static hashvalue_t myhash(int id) {return (hashvalue_t)id & 0x0f;}
|
||||
/** allocate new key, fill in hash. */
|
||||
/** allocate new key, fill in hash */
|
||||
static struct testkey* newkey(int id) {
|
||||
struct testkey* k = (struct testkey*)calloc(1, sizeof(struct testkey));
|
||||
if(!k) fatal_exit("out of memory");
|
||||
@@ -469,7 +469,7 @@ test_thr_main(void* arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** test hash table access by multiple threads. */
|
||||
/** test hash table access by multiple threads */
|
||||
static void
|
||||
test_threaded_table(struct lruhash* table)
|
||||
{
|
||||
@@ -524,8 +524,9 @@ static int test_compfunc(void* key1, void* key2)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void test_delkey(void* key, void* ATTR_UNUSED(arg))
|
||||
static void test_delkey(void* key, void* ATTR_UNUSED(arg), int l)
|
||||
{
|
||||
if(l) { lock_rw_unlock(&((struct testkey*)key)->entry.lock); }
|
||||
delkey((struct testkey*)key);
|
||||
}
|
||||
|
||||
|
||||
@@ -119,6 +119,84 @@ net_test()
|
||||
unit_assert( !is_pow2(259) );
|
||||
}
|
||||
|
||||
#include "util/rtt.h"
|
||||
/** test RTT code */
|
||||
static void
|
||||
rtt_test()
|
||||
{
|
||||
int i;
|
||||
struct rtt_info r;
|
||||
rtt_init(&r);
|
||||
/* initial value sensible */
|
||||
unit_assert( rtt_timeout(&r) == 3000 );
|
||||
rtt_lost(&r);
|
||||
unit_assert( rtt_timeout(&r) == 6000 );
|
||||
rtt_lost(&r);
|
||||
unit_assert( rtt_timeout(&r) == 12000 );
|
||||
rtt_update(&r, 4000);
|
||||
unit_assert( rtt_timeout(&r) >= 5000 );
|
||||
rtt_lost(&r);
|
||||
for(i=0; i<100; i++) {
|
||||
rtt_lost(&r);
|
||||
unit_assert( rtt_timeout(&r) > RTT_MIN_TIMEOUT-1);
|
||||
unit_assert( rtt_timeout(&r) < RTT_MAX_TIMEOUT+1);
|
||||
}
|
||||
}
|
||||
|
||||
#include "services/cache/infra.h"
|
||||
#include "util/config_file.h"
|
||||
/** test host cache */
|
||||
static void
|
||||
infra_test()
|
||||
{
|
||||
int one = 1;
|
||||
uint8_t* zone = (uint8_t*)"\007example\003com\000";
|
||||
size_t zonelen = 13;
|
||||
struct infra_cache* slab;
|
||||
struct config_file* cfg = config_create();
|
||||
time_t now = 0;
|
||||
int vs, to;
|
||||
struct infra_host_key* k;
|
||||
struct infra_host_data* d;
|
||||
|
||||
slab = infra_create(cfg);
|
||||
unit_assert( infra_host(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), now, &vs, &to) );
|
||||
unit_assert( vs == 0 && to == 3000 );
|
||||
|
||||
unit_assert( infra_rtt_update(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), -1, now) );
|
||||
unit_assert( infra_host(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), now, &vs, &to) );
|
||||
unit_assert( vs == 0 && to == 6000 );
|
||||
|
||||
unit_assert( infra_edns_update(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), -1, now) );
|
||||
unit_assert( infra_host(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), now, &vs, &to) );
|
||||
unit_assert( vs == -1 && to == 6000 );
|
||||
|
||||
now += cfg->host_ttl + 10;
|
||||
unit_assert( infra_host(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), now, &vs, &to) );
|
||||
unit_assert( vs == 0 && to == 3000 );
|
||||
|
||||
unit_assert( infra_set_lame(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), zone, zonelen, now) );
|
||||
unit_assert( (d=infra_lookup_host(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), 0, now, &k)) );
|
||||
unit_assert( d->ttl == now+cfg->host_ttl );
|
||||
unit_assert( d->edns_version == 0 );
|
||||
unit_assert( infra_lookup_lame(d, zone, zonelen, now) );
|
||||
unit_assert( !infra_lookup_lame(d, zone, zonelen,
|
||||
now+cfg->lame_ttl+10) );
|
||||
unit_assert( !infra_lookup_lame(d, (uint8_t*)"\000", 1, now) );
|
||||
lock_rw_unlock(&k->entry.lock);
|
||||
|
||||
infra_delete(slab);
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main unit test program. Setup, teardown and report errors.
|
||||
* @param argc: arg count.
|
||||
@@ -137,9 +215,11 @@ main(int argc, char* argv[])
|
||||
checklock_start();
|
||||
net_test();
|
||||
dname_test();
|
||||
rtt_test();
|
||||
alloc_test();
|
||||
lruhash_test();
|
||||
slabhash_test();
|
||||
infra_test();
|
||||
msgparse_test();
|
||||
checklock_stop();
|
||||
printf("%d checks ok.\n", testcount);
|
||||
|
||||
+12
-10
@@ -43,6 +43,7 @@
|
||||
#include "testcode/unitmain.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/alloc.h"
|
||||
#include "util/region-allocator.h"
|
||||
#include "util/net_help.h"
|
||||
@@ -66,13 +67,13 @@ match_list(ldns_rr_list* q, ldns_rr_list *p)
|
||||
{
|
||||
if(ldns_rr_compare(ldns_rr_list_rr(q, i),
|
||||
ldns_rr_list_rr(p, i)) != 0) {
|
||||
verbose(3, "rr %d different", i);
|
||||
verbose(3, "rr %u different", (unsigned)i);
|
||||
return 0;
|
||||
}
|
||||
/* and check the ttl */
|
||||
if(ldns_rr_ttl(ldns_rr_list_rr(q, i)) !=
|
||||
ldns_rr_ttl(ldns_rr_list_rr(p, i))) {
|
||||
verbose(3, "rr %d ttl different", i);
|
||||
verbose(3, "rr %u ttl different", (unsigned)i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -180,11 +181,11 @@ test_buffers(ldns_buffer* pkt, ldns_buffer* out)
|
||||
if(lim-count < sz) rem = lim-count;
|
||||
if(memcmp(ldns_buffer_at(pkt, count),
|
||||
ldns_buffer_at(out, count), rem) == 0) {
|
||||
log_info("same %d %d", count, rem);
|
||||
log_info("same %d %d", (int)count, (int)rem);
|
||||
log_hex("same: ", ldns_buffer_at(pkt, count),
|
||||
rem);
|
||||
} else {
|
||||
log_info("diff %d %d", count, rem);
|
||||
log_info("diff %d %d", (int)count, (int)rem);
|
||||
log_hex("difp: ", ldns_buffer_at(pkt, count),
|
||||
rem);
|
||||
log_hex("difo: ", ldns_buffer_at(out, count),
|
||||
@@ -223,7 +224,7 @@ test_buffers(ldns_buffer* pkt, ldns_buffer* out)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** check if unbound formerr equals ldns formerr. */
|
||||
/** check if unbound formerr equals ldns formerr */
|
||||
static void
|
||||
checkformerr(ldns_buffer* pkt)
|
||||
{
|
||||
@@ -269,7 +270,7 @@ testpkt(ldns_buffer* pkt, struct alloc_cache* alloc, ldns_buffer* out,
|
||||
} else {
|
||||
const size_t lim = 512;
|
||||
ret = reply_info_encode(&qi, rep, id, flags, out, timenow,
|
||||
region, 65535);
|
||||
region, 65535, (int)(edns.bits & EDNS_DO) );
|
||||
unit_assert(ret != 0); /* udp packets should fit */
|
||||
attach_edns_record(out, &edns);
|
||||
if(vbmp) printf("inlen %u outlen %u\n",
|
||||
@@ -280,7 +281,8 @@ testpkt(ldns_buffer* pkt, struct alloc_cache* alloc, ldns_buffer* out,
|
||||
if(ldns_buffer_limit(out) > lim) {
|
||||
ret = reply_info_encode(&qi, rep, id, flags, out,
|
||||
timenow, region,
|
||||
lim - calc_edns_field_size(&edns));
|
||||
lim - calc_edns_field_size(&edns),
|
||||
(int)(edns.bits & EDNS_DO));
|
||||
unit_assert(ret != 0); /* should fit, but with TC */
|
||||
attach_edns_record(out, &edns);
|
||||
if( LDNS_QDCOUNT(ldns_buffer_begin(out)) !=
|
||||
@@ -301,7 +303,7 @@ testpkt(ldns_buffer* pkt, struct alloc_cache* alloc, ldns_buffer* out,
|
||||
region_destroy(region);
|
||||
}
|
||||
|
||||
/** simple test of parsing. */
|
||||
/** simple test of parsing */
|
||||
static void
|
||||
simpletest(ldns_buffer* pkt, struct alloc_cache* alloc, ldns_buffer* out)
|
||||
{
|
||||
@@ -352,7 +354,7 @@ simpletest(ldns_buffer* pkt, struct alloc_cache* alloc, ldns_buffer* out)
|
||||
" 64 b9 00 04 c0 3a 80 1e ");
|
||||
}
|
||||
|
||||
/** simple test of parsing, pcat file. */
|
||||
/** simple test of parsing, pcat file */
|
||||
static void
|
||||
testfromfile(ldns_buffer* pkt, struct alloc_cache* alloc, ldns_buffer* out,
|
||||
const char* fname)
|
||||
@@ -379,7 +381,7 @@ testfromfile(ldns_buffer* pkt, struct alloc_cache* alloc, ldns_buffer* out,
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
/** simple test of parsing, drill file. */
|
||||
/** simple test of parsing, drill file */
|
||||
static void
|
||||
testfromdrillfile(ldns_buffer* pkt, struct alloc_cache* alloc,
|
||||
ldns_buffer* out, const char* fname)
|
||||
|
||||
@@ -62,19 +62,19 @@ static size_t test_sizefunc(void*, void*);
|
||||
/** comparefunc for lruhash */
|
||||
static int test_compfunc(void*, void*);
|
||||
/** delkey for lruhash */
|
||||
static void test_delkey(void*, void*);
|
||||
static void test_delkey(void*, void*, int);
|
||||
/** deldata for lruhash */
|
||||
static void test_deldata(void*, void*);
|
||||
/* --- end test representation --- */
|
||||
|
||||
/** hash func, very bad to improve collisions, both high and low bits. */
|
||||
/** hash func, very bad to improve collisions, both high and low bits */
|
||||
static hashvalue_t myhash(int id) {
|
||||
hashvalue_t h = (hashvalue_t)id & 0x0f;
|
||||
h |= (h << 28);
|
||||
return h;
|
||||
}
|
||||
|
||||
/** allocate new key, fill in hash. */
|
||||
/** allocate new key, fill in hash */
|
||||
static struct slabtestkey* newkey(int id) {
|
||||
struct slabtestkey* k = (struct slabtestkey*)calloc(1, sizeof(struct slabtestkey));
|
||||
if(!k) fatal_exit("out of memory");
|
||||
@@ -351,7 +351,7 @@ test_thr_main(void* arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** test hash table access by multiple threads. */
|
||||
/** test hash table access by multiple threads */
|
||||
static void
|
||||
test_threaded_table(struct slabhash* table)
|
||||
{
|
||||
@@ -404,8 +404,9 @@ static int test_compfunc(void* key1, void* key2)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void test_delkey(void* key, void* ATTR_UNUSED(arg))
|
||||
static void test_delkey(void* key, void* ATTR_UNUSED(arg), int l)
|
||||
{
|
||||
if(l) { lock_rw_unlock(&((struct slabtestkey*)key)->entry.lock); }
|
||||
delkey((struct slabtestkey*)key);
|
||||
}
|
||||
|
||||
|
||||
Vendored
+149
@@ -0,0 +1,149 @@
|
||||
; config options
|
||||
stub-zone:
|
||||
name: "."
|
||||
stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN Fetch glue when missing or timed out.
|
||||
|
||||
; K.ROOT-SERVERS.NET.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 193.0.14.129
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
. IN NS
|
||||
SECTION ANSWER
|
||||
. IN NS K.ROOT-SERVERS.NET.
|
||||
SECTION ADDITIONAL
|
||||
K.ROOT-SERVERS.NET. IN A 193.0.14.129
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION AUTHORITY
|
||||
com. IN NS a.gtld-servers.net.
|
||||
SECTION ADDITIONAL
|
||||
a.gtld-servers.net. IN A 192.5.6.30
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; a.gtld-servers.net.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 192.5.6.30
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.com.
|
||||
; is missing the glue!
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN A
|
||||
SECTION ANSWER
|
||||
ns.example.com. IN A 1.2.3.4
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.com.
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN AAAA
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.com.
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; ns.example.com.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 1.2.3.4
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.com. IN A 1.2.3.4
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN A
|
||||
SECTION ANSWER
|
||||
ns.example.com. IN A 1.2.3.4
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.com.
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN AAAA
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.com. IN A 1.2.3.4
|
||||
ENTRY_END
|
||||
|
||||
RANGE_END
|
||||
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
; recursion happens here.
|
||||
STEP 10 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RD RA NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.com. IN A 1.2.3.4
|
||||
ENTRY_END
|
||||
|
||||
; due to ordering of answer packets, this is still outstanding, remove it
|
||||
STEP 21 CHECK_OUT_QUERY
|
||||
ENTRY_BEGIN
|
||||
ADJUST copy_id
|
||||
MATCH qname qtype
|
||||
REPLY QR
|
||||
SECTION QUESTION
|
||||
ns.example.com IN AAAA
|
||||
ENTRY_END
|
||||
|
||||
SCENARIO_END
|
||||
Vendored
+1
@@ -1,5 +1,6 @@
|
||||
; This is a comment.
|
||||
; config options go here.
|
||||
forward-zone: name: "." forward-addr: 216.0.0.1
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN Sample of a valid query
|
||||
|
||||
Vendored
+2
-2
@@ -1,5 +1,6 @@
|
||||
; This is a comment.
|
||||
; config options go here.
|
||||
forward-zone: name: "." forward-addr: 216.0.0.1
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN Query receives answer from the cache
|
||||
@@ -35,8 +36,7 @@ ENTRY_END
|
||||
STEP 4 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
; first reply, have AA set.
|
||||
REPLY QR AA RD RA
|
||||
REPLY QR RD RA
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
|
||||
Vendored
+2
-1
@@ -1,4 +1,5 @@
|
||||
; config options go here.
|
||||
forward-zone: name: "." forward-addr: 216.0.0.1
|
||||
CONFIG_END
|
||||
SCENARIO_BEGIN Forwarder and an error happens on server query.
|
||||
STEP 1 QUERY
|
||||
@@ -13,7 +14,7 @@ STEP 4 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qname qtype
|
||||
SECTION QUESTION
|
||||
REPLY SERVFAIL QR
|
||||
REPLY SERVFAIL QR RA
|
||||
MATCH all
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
Vendored
+1
@@ -4,6 +4,7 @@
|
||||
server:
|
||||
msg-cache-size: 1 # one whole byte!
|
||||
msg-cache-slabs: 1
|
||||
forward-zone: name: "." forward-addr: 216.0.0.1
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN Old answer is dropped from the cache
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
+3
@@ -5,6 +5,9 @@
|
||||
; here config file options:
|
||||
server:
|
||||
msg-cache-size: 1024
|
||||
forward-zone:
|
||||
name: "."
|
||||
forward-addr: 216.0.0.1
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN Query receives answer not from the cache
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
+2
-1
@@ -1,4 +1,5 @@
|
||||
; config options go here.
|
||||
forward-zone: name: "." forward-addr: 216.0.0.1
|
||||
CONFIG_END
|
||||
SCENARIO_BEGIN Forwarder and a timeout happens on server query.
|
||||
STEP 1 QUERY
|
||||
@@ -13,7 +14,7 @@ STEP 4 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qname qtype
|
||||
SECTION QUESTION
|
||||
REPLY SERVFAIL QR
|
||||
REPLY SERVFAIL QR RA
|
||||
MATCH all
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
Vendored
-69
@@ -1,69 +0,0 @@
|
||||
; This is a comment.
|
||||
; config options go here.
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN Query forwarded, receives truncated reply.
|
||||
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
; the query is sent to the forwarder - UDP
|
||||
STEP 2 CHECK_OUT_QUERY
|
||||
ENTRY_BEGIN
|
||||
MATCH qname qtype opcode UDP
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
; reply TC bit
|
||||
STEP 3 REPLY
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname UDP
|
||||
ADJUST copy_id
|
||||
; authoritative answer
|
||||
REPLY QR AA RD RA TC NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
ENTRY_END
|
||||
; retry in TCP mode.
|
||||
STEP 4 CHECK_OUT_QUERY
|
||||
ENTRY_BEGIN
|
||||
MATCH qname qtype opcode TCP
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
STEP 5 REPLY
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname TCP
|
||||
ADJUST copy_id
|
||||
; authoritative answer
|
||||
REPLY QR AA RD RA NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
SECTION AUTHORITY
|
||||
www.example.com. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.com. IN A 10.20.30.50
|
||||
ENTRY_END
|
||||
STEP 6 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
; first reply, have AA set.
|
||||
REPLY QR AA RD RA
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
SECTION AUTHORITY
|
||||
www.example.com. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.com. IN A 10.20.30.50
|
||||
ENTRY_END
|
||||
|
||||
SCENARIO_END
|
||||
Vendored
BIN
Binary file not shown.
Vendored
+12
@@ -1,6 +1,9 @@
|
||||
; config options go here.
|
||||
server:
|
||||
num-queries-per-thread: 1
|
||||
forward-zone:
|
||||
name: "."
|
||||
forward-addr: 216.0.0.1
|
||||
CONFIG_END
|
||||
SCENARIO_BEGIN Sample of a valid query
|
||||
|
||||
@@ -68,6 +71,15 @@ www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
ENTRY_END
|
||||
|
||||
STEP 11 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qname qtype
|
||||
SECTION QUESTION
|
||||
www.example.net. IN A
|
||||
SECTION ANSWER
|
||||
www.example.net. IN A 10.20.30.40
|
||||
ENTRY_END
|
||||
SCENARIO_END
|
||||
|
||||
; testbound checks before exit:
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
+151
@@ -0,0 +1,151 @@
|
||||
; config options
|
||||
stub-zone:
|
||||
name: "."
|
||||
stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN Test resolution with dependency cycle
|
||||
; query for ns.example.com, needs ns.example.net, needs ns.example.com.
|
||||
|
||||
; K.ROOT-SERVERS.NET.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 193.0.14.129
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
. IN NS
|
||||
SECTION ANSWER
|
||||
. IN NS K.ROOT-SERVERS.NET.
|
||||
SECTION ADDITIONAL
|
||||
K.ROOT-SERVERS.NET. IN A 193.0.14.129
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN A
|
||||
SECTION AUTHORITY
|
||||
com. IN NS a.gtld-servers.net.
|
||||
SECTION ADDITIONAL
|
||||
a.gtld-servers.net. IN A 192.5.6.30
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN AAAA
|
||||
SECTION AUTHORITY
|
||||
com. IN NS a.gtld-servers.net.
|
||||
SECTION ADDITIONAL
|
||||
a.gtld-servers.net. IN A 192.5.6.30
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN A
|
||||
SECTION AUTHORITY
|
||||
net. IN NS e.gtld-servers.net.
|
||||
SECTION ADDITIONAL
|
||||
e.gtld-servers.net. IN A 192.12.94.30
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN AAAA
|
||||
SECTION AUTHORITY
|
||||
net. IN NS e.gtld-servers.net.
|
||||
SECTION ADDITIONAL
|
||||
e.gtld-servers.net. IN A 192.12.94.30
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; a.gtld-servers.net.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 192.5.6.30
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN A
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.net.
|
||||
SECTION ADDITIONAL
|
||||
; note this will be scrubbed off
|
||||
ns.example.net. IN A 1.2.3.1
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN AAAA
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.net.
|
||||
SECTION ADDITIONAL
|
||||
; note this will be scrubbed off
|
||||
ns.example.net. IN A 1.2.3.1
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; e.gtld-servers.net.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 192.12.94.30
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN A
|
||||
SECTION AUTHORITY
|
||||
example.net. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
; note this will be scrubbed off
|
||||
ns.example.com. IN A 1.2.3.2
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN AAAA
|
||||
SECTION AUTHORITY
|
||||
example.net. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
; note this will be scrubbed off
|
||||
ns.example.com. IN A 1.2.3.2
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
; recursion happens here.
|
||||
; cycle detected and it fails.
|
||||
STEP 20 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RD RA SERVFAIL
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
SCENARIO_END
|
||||
Vendored
+175
@@ -0,0 +1,175 @@
|
||||
; config options
|
||||
server:
|
||||
harden-glue: "no"
|
||||
stub-zone:
|
||||
name: "."
|
||||
stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN Test resolution where disabled scrubber avoids cycle
|
||||
; query for ns.example.com, needs ns.example.net, needs ns.example.com.
|
||||
; scrubber disabled, so extra glue is trusted.
|
||||
|
||||
; K.ROOT-SERVERS.NET.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 193.0.14.129
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
. IN NS
|
||||
SECTION ANSWER
|
||||
. IN NS K.ROOT-SERVERS.NET.
|
||||
SECTION ADDITIONAL
|
||||
K.ROOT-SERVERS.NET. IN A 193.0.14.129
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN A
|
||||
SECTION AUTHORITY
|
||||
com. IN NS a.gtld-servers.net.
|
||||
SECTION ADDITIONAL
|
||||
a.gtld-servers.net. IN A 192.5.6.30
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN AAAA
|
||||
SECTION AUTHORITY
|
||||
com. IN NS a.gtld-servers.net.
|
||||
SECTION ADDITIONAL
|
||||
a.gtld-servers.net. IN A 192.5.6.30
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN A
|
||||
SECTION AUTHORITY
|
||||
net. IN NS e.gtld-servers.net.
|
||||
SECTION ADDITIONAL
|
||||
e.gtld-servers.net. IN A 192.12.94.30
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN AAAA
|
||||
SECTION AUTHORITY
|
||||
net. IN NS e.gtld-servers.net.
|
||||
SECTION ADDITIONAL
|
||||
e.gtld-servers.net. IN A 192.12.94.30
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; a.gtld-servers.net.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 192.5.6.30
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN A
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.net.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.net. IN A 1.2.3.1
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN AAAA
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.net.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.net. IN A 1.2.3.1
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; e.gtld-servers.net.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 192.12.94.30
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN A
|
||||
SECTION AUTHORITY
|
||||
example.net. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.com. IN A 1.2.3.2
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN AAAA
|
||||
SECTION AUTHORITY
|
||||
example.net. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.com. IN A 1.2.3.2
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; ns.example.net.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 1.2.3.1
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN A
|
||||
SECTION ANSWER
|
||||
ns.example.com. IN A 1.2.3.2
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.net.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.net. IN A 1.2.3.1
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
; recursion happens here.
|
||||
; cycle detected and it fails.
|
||||
STEP 20 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RD RA
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN A
|
||||
SECTION ANSWER
|
||||
ns.example.com. IN A 1.2.3.2
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.net.
|
||||
; scrubbed off
|
||||
; SECTION ADDITIONAL
|
||||
; ns.example.net. IN A 1.2.3.1
|
||||
ENTRY_END
|
||||
|
||||
SCENARIO_END
|
||||
Vendored
+178
@@ -0,0 +1,178 @@
|
||||
; config options
|
||||
stub-zone:
|
||||
name: "."
|
||||
stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN Test resolution with recursion for NS target.
|
||||
|
||||
; K.ROOT-SERVERS.NET.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 193.0.14.129
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
. IN NS
|
||||
SECTION ANSWER
|
||||
. IN NS K.ROOT-SERVERS.NET.
|
||||
SECTION ADDITIONAL
|
||||
K.ROOT-SERVERS.NET. IN A 193.0.14.129
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION AUTHORITY
|
||||
com. IN NS a.gtld-servers.net.
|
||||
SECTION ADDITIONAL
|
||||
a.gtld-servers.net. IN A 192.5.6.30
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN A
|
||||
SECTION AUTHORITY
|
||||
net. IN NS e.gtld-servers.net.
|
||||
SECTION ADDITIONAL
|
||||
e.gtld-servers.net. IN A 192.12.94.30
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN AAAA
|
||||
SECTION AUTHORITY
|
||||
net. IN NS e.gtld-servers.net.
|
||||
SECTION ADDITIONAL
|
||||
e.gtld-servers.net. IN A 192.12.94.30
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; a.gtld-servers.net.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 192.5.6.30
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.net.
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; e.gtld-servers.net.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 192.12.94.30
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN A
|
||||
SECTION AUTHORITY
|
||||
example.net. IN NS ns.example.net.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.net. IN A 1.2.3.44
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN AAAA
|
||||
SECTION AUTHORITY
|
||||
example.net. IN NS ns.example.net.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.net. IN A 1.2.3.44
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; ns.example.net.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 1.2.3.44
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN A
|
||||
SECTION ANSWER
|
||||
ns.example.net. IN A 1.2.3.44
|
||||
SECTION AUTHORITY
|
||||
example.net. IN NS ns.example.net.
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.net. IN AAAA
|
||||
SECTION AUTHORITY
|
||||
example.net. IN NS ns.example.net.
|
||||
SECTION ADDITIONAL
|
||||
www.example.net. IN A 1.2.3.44
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.net.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.net IN A 1.2.3.44
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
; recursion happens here.
|
||||
STEP 20 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RD RA NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.net.
|
||||
; scrubbed off
|
||||
;SECTION ADDITIONAL
|
||||
;ns.example.net IN A 1.2.3.44
|
||||
ENTRY_END
|
||||
|
||||
; due to ordering of answer packets, this is still outstanding, remove it
|
||||
STEP 21 CHECK_OUT_QUERY
|
||||
ENTRY_BEGIN
|
||||
ADJUST copy_id
|
||||
MATCH qname qtype
|
||||
REPLY QR
|
||||
SECTION QUESTION
|
||||
ns.example.net IN AAAA
|
||||
ENTRY_END
|
||||
|
||||
SCENARIO_END
|
||||
Vendored
+93
@@ -0,0 +1,93 @@
|
||||
; config options
|
||||
stub-zone:
|
||||
name: "."
|
||||
stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN Test basic iterative resolve of www.example.com.
|
||||
|
||||
; K.ROOT-SERVERS.NET.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 193.0.14.129
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
. IN NS
|
||||
SECTION ANSWER
|
||||
. IN NS K.ROOT-SERVERS.NET.
|
||||
SECTION ADDITIONAL
|
||||
K.ROOT-SERVERS.NET. IN A 193.0.14.129
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION AUTHORITY
|
||||
com. IN NS a.gtld-servers.net.
|
||||
SECTION ADDITIONAL
|
||||
a.gtld-servers.net. IN A 192.5.6.30
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; a.gtld-servers.net.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 192.5.6.30
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.com. IN A 1.2.3.4
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; ns.example.com.
|
||||
RANGE_BEGIN 0 100
|
||||
ADDRESS 1.2.3.4
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.com. IN A 1.2.3.4
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
; recursion happens here.
|
||||
STEP 10 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RD RA NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
SECTION AUTHORITY
|
||||
example.com. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.com. IN A 1.2.3.4
|
||||
ENTRY_END
|
||||
|
||||
SCENARIO_END
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
; config options
|
||||
CONFIG_END
|
||||
SCENARIO_BEGIN Test AXFR and IXFR queries.
|
||||
|
||||
; AXFR
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
SECTION QUESTION
|
||||
nlnetlabs.nl. IN AXFR
|
||||
ENTRY_END
|
||||
STEP 2 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR REFUSED
|
||||
SECTION QUESTION
|
||||
nlnetlabs.nl. IN AXFR
|
||||
ENTRY_END
|
||||
|
||||
; IXFR
|
||||
STEP 3 QUERY
|
||||
ENTRY_BEGIN
|
||||
SECTION QUESTION
|
||||
nlnetlabs.nl. IN IXFR
|
||||
SECTION AUTHORITY
|
||||
nlnetlabs.nl. IN SOA mr. op. 12 0 0 0 0
|
||||
ENTRY_END
|
||||
STEP 4 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR FORMERR
|
||||
SECTION QUESTION
|
||||
nlnetlabs.nl. IN IXFR
|
||||
SECTION AUTHORITY
|
||||
nlnetlabs.nl. IN SOA mr. op. 12 0 0 0 0
|
||||
ENTRY_END
|
||||
|
||||
SCENARIO_END
|
||||
Vendored
+3
-4
@@ -1,5 +1,6 @@
|
||||
; This is a comment.
|
||||
; config options go here.
|
||||
forward-zone: name: "." forward-addr: 216.0.0.1
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN RRset TTL is updated from message.
|
||||
@@ -35,8 +36,7 @@ ENTRY_END
|
||||
STEP 4 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all ttl
|
||||
; first reply, have AA set.
|
||||
REPLY QR AA RD RA
|
||||
REPLY QR RD RA
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
@@ -79,8 +79,7 @@ ENTRY_END
|
||||
STEP 9 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all ttl
|
||||
; first reply, have AA set.
|
||||
REPLY QR AA RD RA
|
||||
REPLY QR RD RA
|
||||
SECTION QUESTION
|
||||
bla.example.com. IN A
|
||||
SECTION ANSWER
|
||||
|
||||
Vendored
+3
-6
@@ -1,5 +1,6 @@
|
||||
; This is a comment.
|
||||
; config options go here.
|
||||
forward-zone: name: "." forward-addr: 216.0.0.1
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN Untrusted rrset not used for update
|
||||
@@ -35,8 +36,7 @@ ENTRY_END
|
||||
STEP 4 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
; first reply, have AA set.
|
||||
REPLY QR AA RD RA
|
||||
REPLY QR RD RA
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
@@ -82,8 +82,7 @@ ENTRY_END
|
||||
STEP 9 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
; first reply, have AA set.
|
||||
REPLY QR AA RD RA
|
||||
REPLY QR RD RA
|
||||
SECTION QUESTION
|
||||
bla.example.com. IN A
|
||||
SECTION ANSWER
|
||||
@@ -92,8 +91,6 @@ ENTRY_BEGIN
|
||||
SECTION ADDITIONAL
|
||||
example.com. IN NS ns.eeeek.com.
|
||||
example.com. IN NS ns2.eeeek.com.
|
||||
ns.eeeek.com. IN A 55.44.33.22
|
||||
ns2.eeeek.com. IN A 55.44.33.24
|
||||
ENTRY_END
|
||||
|
||||
|
||||
|
||||
Vendored
+3
-4
@@ -1,5 +1,6 @@
|
||||
; This is a comment.
|
||||
; config options go here.
|
||||
forward-zone: name: "." forward-addr: 216.0.0.1
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN RRset is updated from other message that passes by.
|
||||
@@ -35,8 +36,7 @@ ENTRY_END
|
||||
STEP 4 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
; first reply, have AA set.
|
||||
REPLY QR AA RD RA
|
||||
REPLY QR RD RA
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
@@ -81,8 +81,7 @@ ENTRY_END
|
||||
STEP 9 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
; first reply, have AA set.
|
||||
REPLY QR AA RD RA
|
||||
REPLY QR RD RA
|
||||
SECTION QUESTION
|
||||
bla.example.com. IN A
|
||||
SECTION ANSWER
|
||||
|
||||
Vendored
+74
@@ -0,0 +1,74 @@
|
||||
; config options
|
||||
server:
|
||||
hide-identity: no
|
||||
hide-version: no
|
||||
identity: "test-identity"
|
||||
version: "test-version"
|
||||
CONFIG_END
|
||||
SCENARIO_BEGIN Test version.bind identity and version queries
|
||||
|
||||
; version.bind.
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
SECTION QUESTION
|
||||
version.bind. CH TXT
|
||||
ENTRY_END
|
||||
STEP 2 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RA
|
||||
SECTION QUESTION
|
||||
version.bind. CH TXT
|
||||
SECTION ANSWER
|
||||
version.bind. 0 CH TXT "test-version"
|
||||
ENTRY_END
|
||||
|
||||
; version.server.
|
||||
STEP 3 QUERY
|
||||
ENTRY_BEGIN
|
||||
SECTION QUESTION
|
||||
version.server. CH TXT
|
||||
ENTRY_END
|
||||
STEP 4 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RA
|
||||
SECTION QUESTION
|
||||
version.server. CH TXT
|
||||
SECTION ANSWER
|
||||
version.server. 0 CH TXT "test-version"
|
||||
ENTRY_END
|
||||
|
||||
; hostname.bind.
|
||||
STEP 5 QUERY
|
||||
ENTRY_BEGIN
|
||||
SECTION QUESTION
|
||||
hostname.bind. CH TXT
|
||||
ENTRY_END
|
||||
STEP 6 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RA
|
||||
SECTION QUESTION
|
||||
hostname.bind. CH TXT
|
||||
SECTION ANSWER
|
||||
hostname.bind. 0 CH TXT "test-identity"
|
||||
ENTRY_END
|
||||
|
||||
; id.server.
|
||||
STEP 7 QUERY
|
||||
ENTRY_BEGIN
|
||||
SECTION QUESTION
|
||||
id.server. CH TXT
|
||||
ENTRY_END
|
||||
STEP 8 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RA
|
||||
SECTION QUESTION
|
||||
id.server. CH TXT
|
||||
SECTION ANSWER
|
||||
id.server. 0 CH TXT "test-identity"
|
||||
ENTRY_END
|
||||
|
||||
SCENARIO_END
|
||||
Vendored
+67
@@ -0,0 +1,67 @@
|
||||
; config options
|
||||
server:
|
||||
hide-identity: yes
|
||||
hide-version: yes
|
||||
identity: "test-identity"
|
||||
version: "test-version"
|
||||
; we rely on the fact that there are no builtin stubs for class CH.
|
||||
CONFIG_END
|
||||
SCENARIO_BEGIN Test config hide options for identity and version queries
|
||||
|
||||
; version.bind.
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
SECTION QUESTION
|
||||
version.bind. CH TXT
|
||||
ENTRY_END
|
||||
STEP 2 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RA REFUSED
|
||||
SECTION QUESTION
|
||||
version.bind. CH TXT
|
||||
ENTRY_END
|
||||
|
||||
; version.server.
|
||||
STEP 3 QUERY
|
||||
ENTRY_BEGIN
|
||||
SECTION QUESTION
|
||||
version.server. CH TXT
|
||||
ENTRY_END
|
||||
STEP 4 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RA REFUSED
|
||||
SECTION QUESTION
|
||||
version.server. CH TXT
|
||||
ENTRY_END
|
||||
|
||||
; hostname.bind.
|
||||
STEP 5 QUERY
|
||||
ENTRY_BEGIN
|
||||
SECTION QUESTION
|
||||
hostname.bind. CH TXT
|
||||
ENTRY_END
|
||||
STEP 6 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RA REFUSED
|
||||
SECTION QUESTION
|
||||
hostname.bind. CH TXT
|
||||
ENTRY_END
|
||||
|
||||
; id.server.
|
||||
STEP 7 QUERY
|
||||
ENTRY_BEGIN
|
||||
SECTION QUESTION
|
||||
id.server. CH TXT
|
||||
ENTRY_END
|
||||
STEP 8 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RA REFUSED
|
||||
SECTION QUESTION
|
||||
id.server. CH TXT
|
||||
ENTRY_END
|
||||
|
||||
SCENARIO_END
|
||||
@@ -52,6 +52,7 @@ alloc_setup_special(alloc_special_t* t)
|
||||
{
|
||||
memset(t, 0, sizeof(*t));
|
||||
lock_rw_init(&t->entry.lock);
|
||||
t->entry.key = t;
|
||||
}
|
||||
|
||||
/** prealloc some entries in the cache. To minimize contention.
|
||||
@@ -233,3 +234,16 @@ alloc_stats(struct alloc_cache* alloc)
|
||||
log_info("%salloc: %d in cache.", alloc->super?"":"sup",
|
||||
(int)alloc->num_quar);
|
||||
}
|
||||
|
||||
size_t alloc_get_mem(struct alloc_cache* alloc)
|
||||
{
|
||||
size_t s = sizeof(*alloc);
|
||||
if(!alloc->super) {
|
||||
lock_quick_lock(&alloc->lock); /* superalloc needs locking */
|
||||
}
|
||||
s += sizeof(alloc_special_t) * alloc->num_quar;
|
||||
if(!alloc->super) {
|
||||
lock_quick_unlock(&alloc->lock);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -119,6 +119,13 @@ alloc_special_t* alloc_special_obtain(struct alloc_cache* alloc);
|
||||
*/
|
||||
void alloc_special_release(struct alloc_cache* alloc, alloc_special_t* mem);
|
||||
|
||||
/**
|
||||
* Get memory size of alloc cache, alloc structure including special types.
|
||||
* @param alloc: on what alloc.
|
||||
* @return size in bytes.
|
||||
*/
|
||||
size_t alloc_get_mem(struct alloc_cache* alloc);
|
||||
|
||||
/**
|
||||
* Print debug information (statistics).
|
||||
* @param alloc: on what alloc.
|
||||
|
||||
+69
-4
@@ -83,23 +83,41 @@ config_create()
|
||||
cfg->num_queries_per_thread = 1024;
|
||||
cfg->rrset_cache_size = 4 * 1024 * 1024;
|
||||
cfg->rrset_cache_slabs = 4;
|
||||
if(!(cfg->fwd_address = strdup(""))) goto error_exit;
|
||||
cfg->host_ttl = 900;
|
||||
cfg->lame_ttl = 900;
|
||||
cfg->infra_cache_slabs = 4;
|
||||
cfg->infra_cache_numhosts = 1000;
|
||||
cfg->infra_cache_numlame = 1000;
|
||||
if(!(cfg->username = strdup(""))) goto error_exit;
|
||||
if(!(cfg->chrootdir = strdup(""))) goto error_exit;
|
||||
if(!(cfg->directory = strdup("/etc/unbound"))) goto error_exit;
|
||||
if(!(cfg->logfile = strdup(""))) goto error_exit;
|
||||
if(!(cfg->pidfile = strdup("unbound.pid"))) goto error_exit;
|
||||
cfg->fwd_port = UNBOUND_DNS_PORT;
|
||||
if(!(cfg->target_fetch_policy = strdup("3 2 1 0 0"))) goto error_exit;
|
||||
cfg->donotqueryaddrs = NULL;
|
||||
if(!cfg_strlist_insert(&cfg->donotqueryaddrs, strdup("127.0.0.1")))
|
||||
goto error_exit;
|
||||
if(!cfg_strlist_insert(&cfg->donotqueryaddrs, strdup("::1")))
|
||||
goto error_exit;
|
||||
cfg->do_daemonize = 1;
|
||||
cfg->num_ifs = 0;
|
||||
cfg->ifs = NULL;
|
||||
cfg->stubs = NULL;
|
||||
cfg->forwards = NULL;
|
||||
cfg->harden_short_bufsize = 0;
|
||||
cfg->harden_large_queries = 0;
|
||||
cfg->harden_glue = 1;
|
||||
cfg->hide_identity = 0;
|
||||
cfg->hide_version = 0;
|
||||
cfg->identity = NULL;
|
||||
cfg->version = NULL;
|
||||
return cfg;
|
||||
error_exit:
|
||||
config_delete(cfg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** initialize the global cfg_parser object. */
|
||||
/** initialize the global cfg_parser object */
|
||||
static void
|
||||
create_cfg_parser(struct config_file* cfg, char* filename)
|
||||
{
|
||||
@@ -136,22 +154,55 @@ config_read(struct config_file* cfg, const char* filename)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** delete config strlist */
|
||||
static void
|
||||
config_delstrlist(struct config_strlist* p)
|
||||
{
|
||||
struct config_strlist *np;
|
||||
while(p) {
|
||||
np = p->next;
|
||||
free(p->str);
|
||||
free(p);
|
||||
p = np;
|
||||
}
|
||||
}
|
||||
|
||||
/** delete config stublist */
|
||||
static void
|
||||
config_delstubs(struct config_stub* p)
|
||||
{
|
||||
struct config_stub* np;
|
||||
while(p) {
|
||||
np = p->next;
|
||||
free(p->name);
|
||||
config_delstrlist(p->hosts);
|
||||
config_delstrlist(p->addrs);
|
||||
free(p);
|
||||
p = np;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
config_delete(struct config_file* cfg)
|
||||
{
|
||||
if(!cfg) return;
|
||||
free(cfg->fwd_address);
|
||||
free(cfg->username);
|
||||
free(cfg->chrootdir);
|
||||
free(cfg->directory);
|
||||
free(cfg->logfile);
|
||||
free(cfg->pidfile);
|
||||
free(cfg->target_fetch_policy);
|
||||
if(cfg->ifs) {
|
||||
int i;
|
||||
for(i=0; i<cfg->num_ifs; i++)
|
||||
free(cfg->ifs[i]);
|
||||
free(cfg->ifs);
|
||||
}
|
||||
config_delstubs(cfg->stubs);
|
||||
config_delstubs(cfg->forwards);
|
||||
config_delstrlist(cfg->donotqueryaddrs);
|
||||
free(cfg->identity);
|
||||
free(cfg->version);
|
||||
free(cfg);
|
||||
}
|
||||
|
||||
@@ -186,3 +237,17 @@ int ub_c_wrap()
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
cfg_strlist_insert(struct config_strlist** head, char* item)
|
||||
{
|
||||
struct config_strlist *s;
|
||||
if(!item || !head)
|
||||
return 0;
|
||||
s = (struct config_strlist*)calloc(1, sizeof(struct config_strlist));
|
||||
if(!s)
|
||||
return 0;
|
||||
s->str = item;
|
||||
s->next = *head;
|
||||
*head = s;
|
||||
return 1;
|
||||
}
|
||||
|
||||
+71
-6
@@ -41,6 +41,8 @@
|
||||
|
||||
#ifndef UTIL_CONFIG_FILE_H
|
||||
#define UTIL_CONFIG_FILE_H
|
||||
struct config_stub;
|
||||
struct config_strlist;
|
||||
|
||||
/**
|
||||
* The configuration options.
|
||||
@@ -81,17 +83,39 @@ struct config_file {
|
||||
size_t rrset_cache_size;
|
||||
/** slabs in the rrset cache */
|
||||
size_t rrset_cache_slabs;
|
||||
/** host cache ttl in seconds */
|
||||
int host_ttl;
|
||||
/** host is lame for a zone ttl, in seconds */
|
||||
int lame_ttl;
|
||||
/** number of slabs in the infra host cache */
|
||||
size_t infra_cache_slabs;
|
||||
/** max number of hosts in the infra cache */
|
||||
size_t infra_cache_numhosts;
|
||||
/** max number of lame zones per host in the infra cache */
|
||||
size_t infra_cache_numlame;
|
||||
|
||||
/** forwarder address. string. If not NULL fwder mode is enabled. */
|
||||
char* fwd_address;
|
||||
/** forwarder port */
|
||||
int fwd_port;
|
||||
/** the target fetch policy for the iterator */
|
||||
char* target_fetch_policy;
|
||||
|
||||
/** number of interfaces to open. If 0 default all interfaces. */
|
||||
int num_ifs;
|
||||
/** interface description strings (IP addresses) */
|
||||
char **ifs;
|
||||
|
||||
/** the stub definitions, linked list */
|
||||
struct config_stub* stubs;
|
||||
/** the forward zone definitions, linked list */
|
||||
struct config_stub* forwards;
|
||||
/** list of donotquery addresses, linked list */
|
||||
struct config_strlist* donotqueryaddrs;
|
||||
|
||||
/** harden against very small edns buffer sizes */
|
||||
int harden_short_bufsize;
|
||||
/** harden against very large query sizes */
|
||||
int harden_large_queries;
|
||||
/** harden against spoofed glue (out of zone data) */
|
||||
int harden_glue;
|
||||
|
||||
/** chrootdir, if not "" or chroot will be done */
|
||||
char* chrootdir;
|
||||
/** username to change to, if not "". */
|
||||
@@ -102,11 +126,44 @@ struct config_file {
|
||||
char* logfile;
|
||||
/** pidfile to write pid to. */
|
||||
char* pidfile;
|
||||
|
||||
/** do not report identity (id.server, hostname.bind) */
|
||||
int hide_identity;
|
||||
/** do not report version (version.server, version.bind) */
|
||||
int hide_version;
|
||||
/** identity, hostname is returned if "". */
|
||||
char* identity;
|
||||
/** version, package version returned if "". */
|
||||
char* version;
|
||||
|
||||
/** daemonize, i.e. fork into the background. */
|
||||
int do_daemonize;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stub config options
|
||||
*/
|
||||
struct config_stub {
|
||||
/** next in list */
|
||||
struct config_stub* next;
|
||||
/** domain name (in text) of the stub apex domain */
|
||||
char* name;
|
||||
/** list of stub nameserver hosts (domain name) */
|
||||
struct config_strlist* hosts;
|
||||
/** list of stub nameserver addresses (IP address) */
|
||||
struct config_strlist* addrs;
|
||||
};
|
||||
|
||||
/**
|
||||
* List of strings for config options
|
||||
*/
|
||||
struct config_strlist {
|
||||
/** next item in list */
|
||||
struct config_strlist* next;
|
||||
/** config option string */
|
||||
char* str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create config file structure. Filled with default values.
|
||||
* @return: the new structure or NULL on memory error.
|
||||
@@ -127,6 +184,14 @@ int config_read(struct config_file* config, const char* filename);
|
||||
*/
|
||||
void config_delete(struct config_file* config);
|
||||
|
||||
/**
|
||||
* Insert string into strlist.
|
||||
* @param head: pointer to strlist head variable.
|
||||
* @param item: new item. malloced by caller. If NULL the insertion fails.
|
||||
* @return: true on success.
|
||||
*/
|
||||
int cfg_strlist_insert(struct config_strlist** head, char* item);
|
||||
|
||||
/**
|
||||
* Used during options parsing
|
||||
*/
|
||||
@@ -145,9 +210,9 @@ struct config_parser_state {
|
||||
|
||||
/** global config parser object used during config parsing */
|
||||
extern struct config_parser_state* cfg_parser;
|
||||
/** parsing helpers: print error with file and line numbers. */
|
||||
/** parsing helpers: print error with file and line numbers */
|
||||
void ub_c_error(const char* msg);
|
||||
/** parsing helpers: print error with file and line numbers. */
|
||||
/** parsing helpers: print error with file and line numbers */
|
||||
void ub_c_error_msg(const char* fmt, ...) ATTR_FORMAT(printf, 1, 2);
|
||||
|
||||
#endif /* UTIL_CONFIG_FILE_H */
|
||||
|
||||
+45
-24
@@ -27,6 +27,8 @@ void ub_c_error(const char *message);
|
||||
#define LEXOUT(s)
|
||||
#endif
|
||||
|
||||
#define YDOUT LEXOUT(("v(%s )", yytext))
|
||||
|
||||
struct inc_state {
|
||||
char* filename;
|
||||
int line;
|
||||
@@ -96,30 +98,49 @@ ANY [^\"\n\r\\]|\\.
|
||||
%%
|
||||
{SPACE}* { LEXOUT(("SP ")); /* ignore */ }
|
||||
{SPACE}*{COMMENT}.* { LEXOUT(("comment(%s) ", yytext)); /* ignore */ }
|
||||
server{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SERVER;}
|
||||
num-threads{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NUM_THREADS;}
|
||||
verbosity{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_VERBOSITY;}
|
||||
port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PORT;}
|
||||
outgoing-port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_OUTGOING_PORT;}
|
||||
outgoing-range{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_OUTGOING_RANGE;}
|
||||
outgoing-num-tcp{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_OUTGOING_NUM_TCP;}
|
||||
do-ip4{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DO_IP4;}
|
||||
do-ip6{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DO_IP6;}
|
||||
do-udp{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DO_UDP;}
|
||||
do-tcp{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DO_TCP;}
|
||||
forward-to{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_FORWARD_TO;}
|
||||
forward-to-port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_FORWARD_TO_PORT;}
|
||||
interface{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_INTERFACE;}
|
||||
chroot{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CHROOT;}
|
||||
username{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_USERNAME;}
|
||||
directory{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DIRECTORY;}
|
||||
logfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_LOGFILE;}
|
||||
pidfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PIDFILE;}
|
||||
msg-cache-size{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MSG_CACHE_SIZE;}
|
||||
msg-cache-slabs{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MSG_CACHE_SLABS;}
|
||||
rrset-cache-size{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRSET_CACHE_SIZE;}
|
||||
rrset-cache-slabs{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRSET_CACHE_SLABS;}
|
||||
num-queries-per-thread{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NUM_QUERIES_PER_THREAD;}
|
||||
server{COLON} { YDOUT; return VAR_SERVER;}
|
||||
num-threads{COLON} { YDOUT; return VAR_NUM_THREADS;}
|
||||
verbosity{COLON} { YDOUT; return VAR_VERBOSITY;}
|
||||
port{COLON} { YDOUT; return VAR_PORT;}
|
||||
outgoing-port{COLON} { YDOUT; return VAR_OUTGOING_PORT;}
|
||||
outgoing-range{COLON} { YDOUT; return VAR_OUTGOING_RANGE;}
|
||||
outgoing-num-tcp{COLON} { YDOUT; return VAR_OUTGOING_NUM_TCP;}
|
||||
do-ip4{COLON} { YDOUT; return VAR_DO_IP4;}
|
||||
do-ip6{COLON} { YDOUT; return VAR_DO_IP6;}
|
||||
do-udp{COLON} { YDOUT; return VAR_DO_UDP;}
|
||||
do-tcp{COLON} { YDOUT; return VAR_DO_TCP;}
|
||||
interface{COLON} { YDOUT; return VAR_INTERFACE;}
|
||||
chroot{COLON} { YDOUT; return VAR_CHROOT;}
|
||||
username{COLON} { YDOUT; return VAR_USERNAME;}
|
||||
directory{COLON} { YDOUT; return VAR_DIRECTORY;}
|
||||
logfile{COLON} { YDOUT; return VAR_LOGFILE;}
|
||||
pidfile{COLON} { YDOUT; return VAR_PIDFILE;}
|
||||
msg-cache-size{COLON} { YDOUT; return VAR_MSG_CACHE_SIZE;}
|
||||
msg-cache-slabs{COLON} { YDOUT; return VAR_MSG_CACHE_SLABS;}
|
||||
rrset-cache-size{COLON} { YDOUT; return VAR_RRSET_CACHE_SIZE;}
|
||||
rrset-cache-slabs{COLON} { YDOUT; return VAR_RRSET_CACHE_SLABS;}
|
||||
infra-host-ttl{COLON} { YDOUT; return VAR_INFRA_HOST_TTL;}
|
||||
infra-lame-ttl{COLON} { YDOUT; return VAR_INFRA_LAME_TTL;}
|
||||
infra-cache-slabs{COLON} { YDOUT; return VAR_INFRA_CACHE_SLABS;}
|
||||
infra-cache-numhosts{COLON} { YDOUT; return VAR_INFRA_CACHE_NUMHOSTS;}
|
||||
infra-cache-numlame{COLON} { YDOUT; return VAR_INFRA_CACHE_NUMLAME;}
|
||||
num-queries-per-thread{COLON} { YDOUT; return VAR_NUM_QUERIES_PER_THREAD;}
|
||||
target-fetch-policy{COLON} { YDOUT; return VAR_TARGET_FETCH_POLICY;}
|
||||
harden-short-bufsize{COLON} { YDOUT; return VAR_HARDEN_SHORT_BUFSIZE;}
|
||||
harden-large-queries{COLON} { YDOUT; return VAR_HARDEN_LARGE_QUERIES;}
|
||||
harden-glue{COLON} { YDOUT; return VAR_HARDEN_GLUE;}
|
||||
stub-zone{COLON} { YDOUT; return VAR_STUB_ZONE;}
|
||||
name{COLON} { YDOUT; return VAR_NAME;}
|
||||
stub-addr{COLON} { YDOUT; return VAR_STUB_ADDR;}
|
||||
stub-host{COLON} { YDOUT; return VAR_STUB_HOST;}
|
||||
forward-zone{COLON} { YDOUT; return VAR_FORWARD_ZONE;}
|
||||
forward-addr{COLON} { YDOUT; return VAR_FORWARD_ADDR;}
|
||||
forward-host{COLON} { YDOUT; return VAR_FORWARD_HOST;}
|
||||
do-not-query-address{COLON} { YDOUT; return VAR_DO_NOT_QUERY_ADDRESS;}
|
||||
hide-identity{COLON} { YDOUT; return VAR_HIDE_IDENTITY;}
|
||||
hide-version{COLON} { YDOUT; return VAR_HIDE_VERSION;}
|
||||
identity{COLON} { YDOUT; return VAR_IDENTITY;}
|
||||
version{COLON} { YDOUT; return VAR_VERSION;}
|
||||
{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;}
|
||||
|
||||
/* Quoted strings. Strip leading and ending quotes */
|
||||
|
||||
+222
-24
@@ -69,15 +69,23 @@ extern struct config_parser_state* cfg_parser;
|
||||
%token <str> STRING
|
||||
%token VAR_SERVER VAR_VERBOSITY VAR_NUM_THREADS VAR_PORT
|
||||
%token VAR_OUTGOING_PORT VAR_OUTGOING_RANGE VAR_INTERFACE
|
||||
%token VAR_DO_IP4 VAR_DO_IP6 VAR_DO_UDP VAR_DO_TCP
|
||||
%token VAR_FORWARD_TO VAR_FORWARD_TO_PORT VAR_CHROOT
|
||||
%token VAR_USERNAME VAR_DIRECTORY VAR_LOGFILE VAR_PIDFILE
|
||||
%token VAR_DO_IP4 VAR_DO_IP6 VAR_DO_UDP VAR_DO_TCP
|
||||
%token VAR_CHROOT VAR_USERNAME VAR_DIRECTORY VAR_LOGFILE VAR_PIDFILE
|
||||
%token VAR_MSG_CACHE_SIZE VAR_MSG_CACHE_SLABS VAR_NUM_QUERIES_PER_THREAD
|
||||
%token VAR_RRSET_CACHE_SIZE VAR_RRSET_CACHE_SLABS VAR_OUTGOING_NUM_TCP
|
||||
%token VAR_INFRA_HOST_TTL VAR_INFRA_LAME_TTL VAR_INFRA_CACHE_SLABS
|
||||
%token VAR_INFRA_CACHE_NUMHOSTS VAR_INFRA_CACHE_NUMLAME VAR_NAME
|
||||
%token VAR_STUB_ZONE VAR_STUB_HOST VAR_STUB_ADDR VAR_TARGET_FETCH_POLICY
|
||||
%token VAR_HARDEN_SHORT_BUFSIZE VAR_HARDEN_LARGE_QUERIES
|
||||
%token VAR_FORWARD_ZONE VAR_FORWARD_HOST VAR_FORWARD_ADDR
|
||||
%token VAR_DO_NOT_QUERY_ADDRESS VAR_HIDE_IDENTITY VAR_HIDE_VERSION
|
||||
%token VAR_IDENTITY VAR_VERSION VAR_HARDEN_GLUE
|
||||
|
||||
%%
|
||||
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
|
||||
toplevelvar: serverstart contents_server ;
|
||||
toplevelvar: serverstart contents_server | stubstart contents_stub |
|
||||
forwardstart contents_forward
|
||||
;
|
||||
|
||||
/* server: declaration */
|
||||
serverstart: VAR_SERVER
|
||||
@@ -92,12 +100,51 @@ contents_server: contents_server content_server
|
||||
| ;
|
||||
content_server: server_num_threads | server_verbosity | server_port |
|
||||
server_outgoing_port | server_outgoing_range | server_do_ip4 |
|
||||
server_do_ip6 | server_do_udp | server_do_tcp | server_forward_to |
|
||||
server_forward_to_port | server_interface | server_chroot |
|
||||
server_username | server_directory | server_logfile | server_pidfile |
|
||||
server_do_ip6 | server_do_udp | server_do_tcp |
|
||||
server_interface | server_chroot | server_username |
|
||||
server_directory | server_logfile | server_pidfile |
|
||||
server_msg_cache_size | server_msg_cache_slabs |
|
||||
server_num_queries_per_thread | server_rrset_cache_size |
|
||||
server_rrset_cache_slabs | server_outgoing_num_tcp
|
||||
server_rrset_cache_slabs | server_outgoing_num_tcp |
|
||||
server_infra_host_ttl | server_infra_lame_ttl |
|
||||
server_infra_cache_slabs | server_infra_cache_numhosts |
|
||||
server_infra_cache_numlame | server_target_fetch_policy |
|
||||
server_harden_short_bufsize | server_harden_large_queries |
|
||||
server_do_not_query_address | server_hide_identity |
|
||||
server_hide_version | server_identity | server_version |
|
||||
server_harden_glue
|
||||
;
|
||||
stubstart: VAR_STUB_ZONE
|
||||
{
|
||||
struct config_stub* s;
|
||||
OUTYY(("\nP(stub_zone:)\n"));
|
||||
s = (struct config_stub*)calloc(1, sizeof(struct config_stub));
|
||||
if(s) {
|
||||
s->next = cfg_parser->cfg->stubs;
|
||||
cfg_parser->cfg->stubs = s;
|
||||
} else
|
||||
yyerror("out of memory");
|
||||
}
|
||||
;
|
||||
contents_stub: contents_stub content_stub
|
||||
| ;
|
||||
content_stub: stub_name | stub_host | stub_addr
|
||||
;
|
||||
forwardstart: VAR_FORWARD_ZONE
|
||||
{
|
||||
struct config_stub* s;
|
||||
OUTYY(("\nP(forward_zone:)\n"));
|
||||
s = (struct config_stub*)calloc(1, sizeof(struct config_stub));
|
||||
if(s) {
|
||||
s->next = cfg_parser->cfg->forwards;
|
||||
cfg_parser->cfg->forwards = s;
|
||||
} else
|
||||
yyerror("out of memory");
|
||||
}
|
||||
;
|
||||
contents_forward: contents_forward content_forward
|
||||
| ;
|
||||
content_forward: forward_name | forward_host | forward_addr
|
||||
;
|
||||
server_num_threads: VAR_NUM_THREADS STRING
|
||||
{
|
||||
@@ -202,22 +249,6 @@ server_do_tcp: VAR_DO_TCP STRING
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_forward_to: VAR_FORWARD_TO STRING
|
||||
{
|
||||
OUTYY(("P(server_forward_to:%s)\n", $2));
|
||||
free(cfg_parser->cfg->fwd_address);
|
||||
cfg_parser->cfg->fwd_address = $2;
|
||||
}
|
||||
;
|
||||
server_forward_to_port: VAR_FORWARD_TO_PORT STRING
|
||||
{
|
||||
OUTYY(("P(server_forward_to_port:%s)\n", $2));
|
||||
if(atoi($2) == 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->fwd_port = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_chroot: VAR_CHROOT STRING
|
||||
{
|
||||
OUTYY(("P(server_chroot:%s)\n", $2));
|
||||
@@ -253,6 +284,38 @@ server_pidfile: VAR_PIDFILE STRING
|
||||
cfg_parser->cfg->pidfile = $2;
|
||||
}
|
||||
;
|
||||
server_hide_identity: VAR_HIDE_IDENTITY STRING
|
||||
{
|
||||
OUTYY(("P(server_hide_identity:%s)\n", $2));
|
||||
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
|
||||
yyerror("expected yes or no.");
|
||||
else cfg_parser->cfg->hide_identity = (strcmp($2, "yes")==0);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_hide_version: VAR_HIDE_VERSION STRING
|
||||
{
|
||||
OUTYY(("P(server_hide_version:%s)\n", $2));
|
||||
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
|
||||
yyerror("expected yes or no.");
|
||||
else cfg_parser->cfg->hide_version = (strcmp($2, "yes")==0);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_identity: VAR_IDENTITY STRING
|
||||
{
|
||||
OUTYY(("P(server_identity:%s)\n", $2));
|
||||
free(cfg_parser->cfg->identity);
|
||||
cfg_parser->cfg->identity = $2;
|
||||
}
|
||||
;
|
||||
server_version: VAR_VERSION STRING
|
||||
{
|
||||
OUTYY(("P(server_version:%s)\n", $2));
|
||||
free(cfg_parser->cfg->version);
|
||||
cfg_parser->cfg->version = $2;
|
||||
}
|
||||
;
|
||||
server_msg_cache_size: VAR_MSG_CACHE_SIZE STRING
|
||||
{
|
||||
OUTYY(("P(server_msg_cache_size:%s)\n", $2));
|
||||
@@ -306,6 +369,141 @@ server_rrset_cache_slabs: VAR_RRSET_CACHE_SLABS STRING
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_infra_host_ttl: VAR_INFRA_HOST_TTL STRING
|
||||
{
|
||||
OUTYY(("P(server_infra_host_ttl:%s)\n", $2));
|
||||
if(atoi($2) == 0 && strcmp($2, "0") != 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->host_ttl = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_infra_lame_ttl: VAR_INFRA_LAME_TTL STRING
|
||||
{
|
||||
OUTYY(("P(server_infra_lame_ttl:%s)\n", $2));
|
||||
if(atoi($2) == 0 && strcmp($2, "0") != 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->lame_ttl = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_infra_cache_numhosts: VAR_INFRA_CACHE_NUMHOSTS STRING
|
||||
{
|
||||
OUTYY(("P(server_infra_cache_numhosts:%s)\n", $2));
|
||||
if(atoi($2) == 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->infra_cache_numhosts = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_infra_cache_numlame: VAR_INFRA_CACHE_NUMLAME STRING
|
||||
{
|
||||
OUTYY(("P(server_infra_cache_numlame:%s)\n", $2));
|
||||
if(atoi($2) == 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->infra_cache_numlame = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_infra_cache_slabs: VAR_INFRA_CACHE_SLABS STRING
|
||||
{
|
||||
OUTYY(("P(server_infra_cache_slabs:%s)\n", $2));
|
||||
if(atoi($2) == 0)
|
||||
yyerror("number expected");
|
||||
else {
|
||||
cfg_parser->cfg->infra_cache_slabs = atoi($2);
|
||||
if(!is_pow2(cfg_parser->cfg->infra_cache_slabs))
|
||||
yyerror("must be a power of 2");
|
||||
}
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_target_fetch_policy: VAR_TARGET_FETCH_POLICY STRING
|
||||
{
|
||||
OUTYY(("P(server_target_fetch_policy:%s)\n", $2));
|
||||
free(cfg_parser->cfg->target_fetch_policy);
|
||||
cfg_parser->cfg->target_fetch_policy = $2;
|
||||
}
|
||||
;
|
||||
server_harden_short_bufsize: VAR_HARDEN_SHORT_BUFSIZE STRING
|
||||
{
|
||||
OUTYY(("P(server_harden_short_bufsize:%s)\n", $2));
|
||||
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
|
||||
yyerror("expected yes or no.");
|
||||
else cfg_parser->cfg->harden_short_bufsize =
|
||||
(strcmp($2, "yes")==0);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_harden_large_queries: VAR_HARDEN_LARGE_QUERIES STRING
|
||||
{
|
||||
OUTYY(("P(server_harden_large_queries:%s)\n", $2));
|
||||
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
|
||||
yyerror("expected yes or no.");
|
||||
else cfg_parser->cfg->harden_large_queries =
|
||||
(strcmp($2, "yes")==0);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_harden_glue: VAR_HARDEN_GLUE STRING
|
||||
{
|
||||
OUTYY(("P(server_harden_glue:%s)\n", $2));
|
||||
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
|
||||
yyerror("expected yes or no.");
|
||||
else cfg_parser->cfg->harden_glue =
|
||||
(strcmp($2, "yes")==0);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_do_not_query_address: VAR_DO_NOT_QUERY_ADDRESS STRING
|
||||
{
|
||||
OUTYY(("P(server_do_not_query_address:%s)\n", $2));
|
||||
if(!cfg_strlist_insert(&cfg_parser->cfg->donotqueryaddrs, $2))
|
||||
yyerror("out of memory");
|
||||
}
|
||||
;
|
||||
stub_name: VAR_NAME STRING
|
||||
{
|
||||
OUTYY(("P(name:%s)\n", $2));
|
||||
free(cfg_parser->cfg->stubs->name);
|
||||
cfg_parser->cfg->stubs->name = $2;
|
||||
}
|
||||
;
|
||||
stub_host: VAR_STUB_HOST STRING
|
||||
{
|
||||
OUTYY(("P(stub-host:%s)\n", $2));
|
||||
if(!cfg_strlist_insert(&cfg_parser->cfg->stubs->hosts, $2))
|
||||
yyerror("out of memory");
|
||||
}
|
||||
;
|
||||
stub_addr: VAR_STUB_ADDR STRING
|
||||
{
|
||||
OUTYY(("P(stub-addr:%s)\n", $2));
|
||||
if(!cfg_strlist_insert(&cfg_parser->cfg->stubs->addrs, $2))
|
||||
yyerror("out of memory");
|
||||
}
|
||||
;
|
||||
forward_name: VAR_NAME STRING
|
||||
{
|
||||
OUTYY(("P(name:%s)\n", $2));
|
||||
free(cfg_parser->cfg->forwards->name);
|
||||
cfg_parser->cfg->forwards->name = $2;
|
||||
}
|
||||
;
|
||||
forward_host: VAR_FORWARD_HOST STRING
|
||||
{
|
||||
OUTYY(("P(forward-host:%s)\n", $2));
|
||||
if(!cfg_strlist_insert(&cfg_parser->cfg->forwards->hosts, $2))
|
||||
yyerror("out of memory");
|
||||
}
|
||||
;
|
||||
forward_addr: VAR_FORWARD_ADDR STRING
|
||||
{
|
||||
OUTYY(("P(forward-addr:%s)\n", $2));
|
||||
if(!cfg_strlist_insert(&cfg_parser->cfg->forwards->addrs, $2))
|
||||
yyerror("out of memory");
|
||||
}
|
||||
;
|
||||
%%
|
||||
|
||||
/* parse helper routines could be here */
|
||||
|
||||
+101
-1
@@ -45,7 +45,7 @@
|
||||
#include "util/log.h"
|
||||
#include "util/storage/lookup3.h"
|
||||
|
||||
/** determine length of a dname in buffer, no compression pointers allowed. */
|
||||
/** determine length of a dname in buffer, no compression pointers allowed */
|
||||
size_t
|
||||
query_dname_len(ldns_buffer* query)
|
||||
{
|
||||
@@ -68,6 +68,29 @@ query_dname_len(ldns_buffer* query)
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
dname_valid(uint8_t* dname, size_t maxlen)
|
||||
{
|
||||
size_t len = 0;
|
||||
size_t labellen;
|
||||
labellen = *dname++;
|
||||
while(labellen) {
|
||||
if(labellen&0xc0)
|
||||
return 0; /* no compression ptrs allowed */
|
||||
len += labellen + 1;
|
||||
if(len > LDNS_MAX_DOMAINLEN)
|
||||
return 0; /* too long */
|
||||
if(len > maxlen)
|
||||
return 0; /* does not fit in memory allocation */
|
||||
dname += labellen;
|
||||
labellen = *dname++;
|
||||
}
|
||||
len += 1;
|
||||
if(len > maxlen)
|
||||
return 0; /* does not fit in memory allocation */
|
||||
return len;
|
||||
}
|
||||
|
||||
int
|
||||
query_dname_compare(uint8_t* d1, uint8_t* d2)
|
||||
{
|
||||
@@ -471,3 +494,80 @@ dname_buffer_write(ldns_buffer* pkt, uint8_t* dname)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void dname_str(uint8_t* dname, char* str)
|
||||
{
|
||||
size_t len = 0;
|
||||
uint8_t lablen = 0;
|
||||
char* s = str;
|
||||
if(!dname || !*dname) {
|
||||
*s++ = '.';
|
||||
*s = 0;
|
||||
return;
|
||||
}
|
||||
lablen = *dname++;
|
||||
while(lablen) {
|
||||
if(lablen > LDNS_MAX_LABELLEN) {
|
||||
*s++ = '#';
|
||||
*s = 0;
|
||||
return;
|
||||
}
|
||||
len += lablen+1;
|
||||
if(len >= LDNS_MAX_DOMAINLEN-1) {
|
||||
*s++ = '&';
|
||||
*s = 0;
|
||||
return;
|
||||
}
|
||||
while(lablen--) {
|
||||
if(isalnum((int)*dname)
|
||||
|| *dname == '-' || *dname == '_')
|
||||
*s++ = *(char*)dname++;
|
||||
else {
|
||||
*s++ = '?';
|
||||
dname++;
|
||||
}
|
||||
}
|
||||
*s++ = '.';
|
||||
lablen = *dname++;
|
||||
}
|
||||
*s = 0;
|
||||
}
|
||||
|
||||
int
|
||||
dname_strict_subdomain(uint8_t* d1, int labs1, uint8_t* d2, int labs2)
|
||||
{
|
||||
int m;
|
||||
/* check subdomain: d1: www.example.com. and d2: example.com. */
|
||||
if(labs2 >= labs1)
|
||||
return 0;
|
||||
if(dname_lab_cmp(d1, labs1, d2, labs2, &m) > 0) {
|
||||
/* subdomain if all labels match */
|
||||
return (m == labs2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
dname_strict_subdomain_c(uint8_t* d1, uint8_t* d2)
|
||||
{
|
||||
return dname_strict_subdomain(d1, dname_count_labels(d1), d2,
|
||||
dname_count_labels(d2));
|
||||
}
|
||||
|
||||
int
|
||||
dname_subdomain_c(uint8_t* d1, uint8_t* d2)
|
||||
{
|
||||
int m;
|
||||
/* check subdomain: d1: www.example.com. and d2: example.com. */
|
||||
/* or d1: example.com. and d2: example.com. */
|
||||
int labs1 = dname_count_labels(d1);
|
||||
int labs2 = dname_count_labels(d2);
|
||||
if(labs2 > labs1)
|
||||
return 0;
|
||||
if(dname_lab_cmp(d1, labs1, d2, labs2, &m) < 0) {
|
||||
/* must have been example.com , www.example.com - wrong */
|
||||
/* or otherwise different dnames */
|
||||
return 0;
|
||||
}
|
||||
return (m == labs2);
|
||||
}
|
||||
|
||||
+53
-7
@@ -54,6 +54,14 @@
|
||||
*/
|
||||
size_t query_dname_len(ldns_buffer* query);
|
||||
|
||||
/**
|
||||
* Determine if dname in memory is correct. no compression ptrs allowed.
|
||||
* @param dname: where dname starts in memory.
|
||||
* @param len: dname is not allowed to exceed this length (i.e. of allocation).
|
||||
* @return length of dname if dname is ok, 0 on a parse error.
|
||||
*/
|
||||
size_t dname_valid(uint8_t* dname, size_t len);
|
||||
|
||||
/** lowercase query dname */
|
||||
void query_dname_tolower(uint8_t* dname, size_t len);
|
||||
|
||||
@@ -127,13 +135,6 @@ void dname_pkt_copy(ldns_buffer* pkt, uint8_t* to, uint8_t* dname);
|
||||
*/
|
||||
int dname_buffer_write(ldns_buffer* pkt, uint8_t* dname);
|
||||
|
||||
/** debug helper. Print wireformat dname to output.
|
||||
* @param out: like stdout or a file.
|
||||
* @param pkt: if not NULL, the packet for resolving compression ptrs.
|
||||
* @param dname: pointer to (start of) dname.
|
||||
*/
|
||||
void dname_print(FILE* out, ldns_buffer* pkt, uint8_t* dname);
|
||||
|
||||
/**
|
||||
* Count the number of labels in an uncompressed dname in memory.
|
||||
* @param dname: pointer to uncompressed dname.
|
||||
@@ -161,4 +162,49 @@ int dname_count_size_labels(uint8_t* dname, size_t* size);
|
||||
*/
|
||||
int dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs);
|
||||
|
||||
/**
|
||||
* See if domain name d1 is a strict subdomain of d2.
|
||||
* That is a subdomain, but not equal.
|
||||
* @param d1: domain name, uncompressed wireformat
|
||||
* @param labs1: number of labels in d1, including root label.
|
||||
* @param d2: domain name, uncompressed wireformat
|
||||
* @param labs2: number of labels in d2, including root label.
|
||||
* @return true if d1 is a subdomain of d2, but not equal to d2.
|
||||
*/
|
||||
int dname_strict_subdomain(uint8_t* d1, int labs1, uint8_t* d2, int labs2);
|
||||
|
||||
/**
|
||||
* Like dname_strict_subdomain but counts labels
|
||||
* @param d1: domain name, uncompressed wireformat
|
||||
* @param d2: domain name, uncompressed wireformat
|
||||
* @return true if d1 is a subdomain of d2, but not equal to d2.
|
||||
*/
|
||||
int dname_strict_subdomain_c(uint8_t* d1, uint8_t* d2);
|
||||
|
||||
/**
|
||||
* Counts labels. Tests is d1 is a subdomain of d2.
|
||||
* @param d1: domain name, uncompressed wireformat
|
||||
* @param d2: domain name, uncompressed wireformat
|
||||
* @return true if d1 is a subdomain of d2.
|
||||
*/
|
||||
int dname_subdomain_c(uint8_t* d1, uint8_t* d2);
|
||||
|
||||
/**
|
||||
* Debug helper. Print wireformat dname to output.
|
||||
* @param out: like stdout or a file.
|
||||
* @param pkt: if not NULL, the packet for resolving compression ptrs.
|
||||
* @param dname: pointer to (start of) dname.
|
||||
*/
|
||||
void dname_print(FILE* out, ldns_buffer* pkt, uint8_t* dname);
|
||||
|
||||
/**
|
||||
* Debug helper. Print dname to given string buffer (string buffer must
|
||||
* be at least 255 chars + 1 for the 0, in printable form.
|
||||
* This may lose information (? for nonprintable characters, or & if
|
||||
* the name is too long, # for a bad label length).
|
||||
* @param dname: uncompressed wireformat.
|
||||
* @param str: buffer of 255+1 length.
|
||||
*/
|
||||
void dname_str(uint8_t* dname, char* str);
|
||||
|
||||
#endif /* UTIL_DATA_DNAME_H */
|
||||
|
||||
@@ -0,0 +1,793 @@
|
||||
/*
|
||||
* util/data/msgencode.c - Encode DNS messages, queries and replies.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains a routines to encode DNS messages.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/log.h"
|
||||
#include "util/region-allocator.h"
|
||||
#include "util/net_help.h"
|
||||
|
||||
/** return code that means the function ran out of memory. negative so it does
|
||||
* not conflict with DNS rcodes. */
|
||||
#define RETVAL_OUTMEM -2
|
||||
/** return code that means the data did not fit (completely) in the packet */
|
||||
#define RETVAL_TRUNC -4
|
||||
/** return code that means all is peachy keen. Equal to DNS rcode NOERROR */
|
||||
#define RETVAL_OK 0
|
||||
|
||||
/**
|
||||
* Data structure to help domain name compression in outgoing messages.
|
||||
* A tree of dnames and their offsets in the packet is kept.
|
||||
* It is kept sorted, not canonical, but by label at least, so that after
|
||||
* a lookup of a name you know its closest match, and the parent from that
|
||||
* closest match. These are possible compression targets.
|
||||
*
|
||||
* It is a binary tree, not a rbtree or balanced tree, as the effort
|
||||
* of keeping it balanced probably outweighs usefulness (given typical
|
||||
* DNS packet size).
|
||||
*/
|
||||
struct compress_tree_node {
|
||||
/** left node in tree, all smaller to this */
|
||||
struct compress_tree_node* left;
|
||||
/** right node in tree, all larger than this */
|
||||
struct compress_tree_node* right;
|
||||
|
||||
/** the parent node - not for tree, but zone parent. One less label */
|
||||
struct compress_tree_node* parent;
|
||||
/** the domain name for this node. Pointer to uncompressed memory. */
|
||||
uint8_t* dname;
|
||||
/** number of labels in domain name, kept to help compare func. */
|
||||
int labs;
|
||||
/** offset in packet that points to this dname */
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find domain name in tree, returns exact and closest match.
|
||||
* @param tree: root of tree.
|
||||
* @param dname: pointer to uncompressed dname.
|
||||
* @param labs: number of labels in domain name.
|
||||
* @param match: closest or exact match.
|
||||
* guaranteed to be smaller or equal to the sought dname.
|
||||
* can be null if the tree is empty.
|
||||
* @param matchlabels: number of labels that match with closest match.
|
||||
* can be zero is there is no match.
|
||||
* @return: 0 if no exact match.
|
||||
*/
|
||||
static int
|
||||
compress_tree_search(struct compress_tree_node* tree, uint8_t* dname,
|
||||
int labs, struct compress_tree_node** match, int* matchlabels)
|
||||
{
|
||||
int c, n, closen=0;
|
||||
struct compress_tree_node* p = tree;
|
||||
struct compress_tree_node* close = 0;
|
||||
while(p) {
|
||||
if((c = dname_lab_cmp(dname, labs, p->dname, p->labs, &n))
|
||||
== 0) {
|
||||
*matchlabels = n;
|
||||
*match = p;
|
||||
return 1;
|
||||
}
|
||||
if(c<0) p = p->left;
|
||||
else {
|
||||
closen = n;
|
||||
close = p; /* p->dname is smaller than dname */
|
||||
p = p->right;
|
||||
}
|
||||
}
|
||||
*matchlabels = closen;
|
||||
*match = close;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup a domain name in compression tree.
|
||||
* @param tree: root of tree (not the node with '.').
|
||||
* @param dname: pointer to uncompressed dname.
|
||||
* @param labs: number of labels in domain name.
|
||||
* @return: 0 if not found or compress treenode with best compression.
|
||||
*/
|
||||
static struct compress_tree_node*
|
||||
compress_tree_lookup(struct compress_tree_node* tree, uint8_t* dname,
|
||||
int labs)
|
||||
{
|
||||
struct compress_tree_node* p;
|
||||
int m;
|
||||
if(labs <= 1)
|
||||
return 0; /* do not compress root node */
|
||||
if(compress_tree_search(tree, dname, labs, &p, &m)) {
|
||||
/* exact match */
|
||||
return p;
|
||||
}
|
||||
/* return some ancestor of p that compresses well. */
|
||||
if(m>1) {
|
||||
/* www.example.com. (labs=4) matched foo.example.com.(labs=4)
|
||||
* then matchcount = 3. need to go up. */
|
||||
while(p && p->labs > m)
|
||||
p = p->parent;
|
||||
return p;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert node into domain name compression tree.
|
||||
* @param tree: root of tree (may be modified)
|
||||
* @param dname: pointer to uncompressed dname (stored in tree).
|
||||
* @param labs: number of labels in dname.
|
||||
* @param offset: offset into packet for dname.
|
||||
* @param region: how to allocate memory for new node.
|
||||
* @return new node or 0 on malloc failure.
|
||||
*/
|
||||
static struct compress_tree_node*
|
||||
compress_tree_insert(struct compress_tree_node** tree, uint8_t* dname,
|
||||
int labs, size_t offset, region_type* region)
|
||||
{
|
||||
int c, m;
|
||||
struct compress_tree_node* p, **prev;
|
||||
struct compress_tree_node* n = (struct compress_tree_node*)
|
||||
region_alloc(region, sizeof(struct compress_tree_node));
|
||||
if(!n) return 0;
|
||||
n->left = 0;
|
||||
n->right = 0;
|
||||
n->parent = 0;
|
||||
n->dname = dname;
|
||||
n->labs = labs;
|
||||
n->offset = offset;
|
||||
|
||||
/* find spot to insert it into */
|
||||
prev = tree;
|
||||
p = *tree;
|
||||
while(p) {
|
||||
c = dname_lab_cmp(dname, labs, p->dname, p->labs, &m);
|
||||
log_assert(c != 0); /* may not already be in tree */
|
||||
if(c==0) return p; /* insert only once */
|
||||
if(c<0) {
|
||||
prev = &p->left;
|
||||
p = p->left;
|
||||
} else {
|
||||
prev = &p->right;
|
||||
p = p->right;
|
||||
}
|
||||
}
|
||||
*prev = n;
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store domain name and ancestors into compression tree.
|
||||
* @param tree: root of tree (may be modified)
|
||||
* @param dname: pointer to uncompressed dname (stored in tree).
|
||||
* @param labs: number of labels in dname.
|
||||
* @param offset: offset into packet for dname.
|
||||
* @param region: how to allocate memory for new node.
|
||||
* @param closest: match from previous lookup, used to compress dname.
|
||||
* may be NULL if no previous match.
|
||||
* if the tree has an ancestor of dname already, this must be it.
|
||||
* @return: 0 on memory error.
|
||||
*/
|
||||
static int
|
||||
compress_tree_store(struct compress_tree_node** tree, uint8_t* dname,
|
||||
int labs, size_t offset, region_type* region,
|
||||
struct compress_tree_node* closest)
|
||||
{
|
||||
uint8_t lablen;
|
||||
struct compress_tree_node** lastparentptr = 0;
|
||||
struct compress_tree_node* newnode;
|
||||
int uplabs = labs-1; /* does not store root in tree */
|
||||
if(closest) uplabs = labs - closest->labs;
|
||||
log_assert(uplabs >= 0);
|
||||
while(uplabs--) {
|
||||
if(offset > PTR_MAX_OFFSET) {
|
||||
if(lastparentptr)
|
||||
*lastparentptr = closest;
|
||||
return 1; /* compression pointer no longer useful */
|
||||
}
|
||||
/* store dname, labs, offset */
|
||||
if(!(newnode = compress_tree_insert(tree, dname, labs, offset,
|
||||
region))) {
|
||||
if(lastparentptr)
|
||||
*lastparentptr = closest;
|
||||
return 0;
|
||||
}
|
||||
if(lastparentptr)
|
||||
*lastparentptr = newnode;
|
||||
lastparentptr = &newnode->parent;
|
||||
|
||||
/* next label */
|
||||
lablen = *dname++;
|
||||
dname += lablen;
|
||||
offset += lablen+1;
|
||||
labs--;
|
||||
}
|
||||
if(lastparentptr)
|
||||
*lastparentptr = closest;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** compress a domain name */
|
||||
static int
|
||||
write_compressed_dname(ldns_buffer* pkt, uint8_t* dname, int labs,
|
||||
struct compress_tree_node* p)
|
||||
{
|
||||
/* compress it */
|
||||
int labcopy = labs - p->labs;
|
||||
uint8_t lablen;
|
||||
uint16_t ptr;
|
||||
|
||||
if(labs == 1) {
|
||||
/* write root label */
|
||||
if(ldns_buffer_remaining(pkt) < 1)
|
||||
return 0;
|
||||
ldns_buffer_write_u8(pkt, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* copy the first couple of labels */
|
||||
while(labcopy--) {
|
||||
lablen = *dname++;
|
||||
if(ldns_buffer_remaining(pkt) < (size_t)lablen+1)
|
||||
return 0;
|
||||
ldns_buffer_write_u8(pkt, lablen);
|
||||
ldns_buffer_write(pkt, dname, lablen);
|
||||
dname += lablen;
|
||||
}
|
||||
/* insert compression ptr */
|
||||
if(ldns_buffer_remaining(pkt) < 2)
|
||||
return 0;
|
||||
ptr = PTR_CREATE(p->offset);
|
||||
ldns_buffer_write_u16(pkt, ptr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */
|
||||
static int
|
||||
compress_owner(struct ub_packed_rrset_key* key, ldns_buffer* pkt,
|
||||
region_type* region, struct compress_tree_node** tree,
|
||||
size_t owner_pos, uint16_t* owner_ptr, int owner_labs)
|
||||
{
|
||||
struct compress_tree_node* p;
|
||||
if(!*owner_ptr) {
|
||||
/* compress first time dname */
|
||||
if((p = compress_tree_lookup(*tree, key->rk.dname,
|
||||
owner_labs))) {
|
||||
if(p->labs == owner_labs)
|
||||
/* avoid ptr chains, since some software is
|
||||
* not capable of decoding ptr after a ptr. */
|
||||
*owner_ptr = htons(PTR_CREATE(p->offset));
|
||||
if(!write_compressed_dname(pkt, key->rk.dname,
|
||||
owner_labs, p))
|
||||
return RETVAL_TRUNC;
|
||||
/* check if typeclass+4 ttl + rdatalen is available */
|
||||
if(ldns_buffer_remaining(pkt) < 4+4+2)
|
||||
return RETVAL_TRUNC;
|
||||
} else {
|
||||
/* no compress */
|
||||
if(ldns_buffer_remaining(pkt) < key->rk.dname_len+4+4+2)
|
||||
return RETVAL_TRUNC;
|
||||
ldns_buffer_write(pkt, key->rk.dname,
|
||||
key->rk.dname_len);
|
||||
if(owner_pos <= PTR_MAX_OFFSET)
|
||||
*owner_ptr = htons(PTR_CREATE(owner_pos));
|
||||
}
|
||||
if(!compress_tree_store(tree, key->rk.dname,
|
||||
owner_labs, owner_pos, region, p))
|
||||
return RETVAL_OUTMEM;
|
||||
} else {
|
||||
/* always compress 2nd-further RRs in RRset */
|
||||
if(owner_labs == 1) {
|
||||
if(ldns_buffer_remaining(pkt) < 1+4+4+2)
|
||||
return RETVAL_TRUNC;
|
||||
ldns_buffer_write_u8(pkt, 0);
|
||||
} else {
|
||||
if(ldns_buffer_remaining(pkt) < 2+4+4+2)
|
||||
return RETVAL_TRUNC;
|
||||
ldns_buffer_write(pkt, owner_ptr, 2);
|
||||
}
|
||||
}
|
||||
return RETVAL_OK;
|
||||
}
|
||||
|
||||
/** compress any domain name to the packet, return RETVAL_* */
|
||||
static int
|
||||
compress_any_dname(uint8_t* dname, ldns_buffer* pkt, int labs,
|
||||
region_type* region, struct compress_tree_node** tree)
|
||||
{
|
||||
struct compress_tree_node* p;
|
||||
size_t pos = ldns_buffer_position(pkt);
|
||||
if((p = compress_tree_lookup(*tree, dname, labs))) {
|
||||
if(!write_compressed_dname(pkt, dname, labs, p))
|
||||
return RETVAL_TRUNC;
|
||||
} else {
|
||||
if(!dname_buffer_write(pkt, dname))
|
||||
return RETVAL_TRUNC;
|
||||
}
|
||||
if(!compress_tree_store(tree, dname, labs, pos, region, p))
|
||||
return RETVAL_OUTMEM;
|
||||
return RETVAL_OK;
|
||||
}
|
||||
|
||||
/** return true if type needs domain name compression in rdata */
|
||||
static const ldns_rr_descriptor*
|
||||
type_rdata_compressable(struct ub_packed_rrset_key* key)
|
||||
{
|
||||
uint16_t t = ntohs(key->rk.type);
|
||||
if(ldns_rr_descript(t) &&
|
||||
ldns_rr_descript(t)->_compress == LDNS_RR_COMPRESS)
|
||||
return ldns_rr_descript(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** compress domain names in rdata, return RETVAL_* */
|
||||
static int
|
||||
compress_rdata(ldns_buffer* pkt, uint8_t* rdata, size_t todolen,
|
||||
region_type* region, struct compress_tree_node** tree,
|
||||
const ldns_rr_descriptor* desc)
|
||||
{
|
||||
int labs, r, rdf = 0;
|
||||
size_t dname_len, len, pos = ldns_buffer_position(pkt);
|
||||
uint8_t count = desc->_dname_count;
|
||||
|
||||
ldns_buffer_skip(pkt, 2); /* rdata len fill in later */
|
||||
/* space for rdatalen checked for already */
|
||||
rdata += 2;
|
||||
todolen -= 2;
|
||||
while(todolen > 0 && count) {
|
||||
switch(desc->_wireformat[rdf]) {
|
||||
case LDNS_RDF_TYPE_DNAME:
|
||||
labs = dname_count_size_labels(rdata, &dname_len);
|
||||
if((r=compress_any_dname(rdata, pkt, labs, region,
|
||||
tree)) != RETVAL_OK)
|
||||
return r;
|
||||
rdata += dname_len;
|
||||
todolen -= dname_len;
|
||||
count--;
|
||||
len = 0;
|
||||
break;
|
||||
case LDNS_RDF_TYPE_STR:
|
||||
len = *rdata + 1;
|
||||
break;
|
||||
default:
|
||||
len = get_rdf_size(desc->_wireformat[rdf]);
|
||||
}
|
||||
if(len) {
|
||||
/* copy over */
|
||||
if(ldns_buffer_remaining(pkt) < len)
|
||||
return RETVAL_TRUNC;
|
||||
ldns_buffer_write(pkt, rdata, len);
|
||||
todolen -= len;
|
||||
rdata += len;
|
||||
}
|
||||
rdf++;
|
||||
}
|
||||
/* copy remainder */
|
||||
if(todolen > 0) {
|
||||
if(ldns_buffer_remaining(pkt) < todolen)
|
||||
return RETVAL_TRUNC;
|
||||
ldns_buffer_write(pkt, rdata, todolen);
|
||||
}
|
||||
|
||||
/* set rdata len */
|
||||
ldns_buffer_write_u16_at(pkt, pos, ldns_buffer_position(pkt)-pos-2);
|
||||
return RETVAL_OK;
|
||||
}
|
||||
|
||||
/** Returns true if RR type should be included */
|
||||
static int
|
||||
rrset_belongs_in_reply(ldns_pkt_section s, uint16_t rrtype, uint16_t qtype,
|
||||
int dnssec)
|
||||
{
|
||||
if(dnssec)
|
||||
return 1;
|
||||
/* skip non DNSSEC types, except if directly queried for */
|
||||
if(s == LDNS_SECTION_ANSWER) {
|
||||
if(qtype == LDNS_RR_TYPE_ANY || qtype == rrtype)
|
||||
return 1;
|
||||
}
|
||||
/* check DNSSEC-ness */
|
||||
switch(rrtype) {
|
||||
case LDNS_RR_TYPE_SIG:
|
||||
case LDNS_RR_TYPE_KEY:
|
||||
case LDNS_RR_TYPE_NXT:
|
||||
case LDNS_RR_TYPE_DS:
|
||||
case LDNS_RR_TYPE_RRSIG:
|
||||
case LDNS_RR_TYPE_NSEC:
|
||||
case LDNS_RR_TYPE_DNSKEY:
|
||||
/* FIXME: include NSEC3 here. */
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** store rrset in buffer in wireformat, return RETVAL_* */
|
||||
static int
|
||||
packed_rrset_encode(struct ub_packed_rrset_key* key, ldns_buffer* pkt,
|
||||
uint16_t* num_rrs, uint32_t timenow, region_type* region,
|
||||
int do_data, int do_sig, struct compress_tree_node** tree,
|
||||
ldns_pkt_section s, uint16_t qtype, int dnssec)
|
||||
{
|
||||
size_t i, owner_pos;
|
||||
int r, owner_labs;
|
||||
uint16_t owner_ptr = 0;
|
||||
struct packed_rrset_data* data = (struct packed_rrset_data*)
|
||||
key->entry.data;
|
||||
|
||||
/* does this RR type belong in the answer? */
|
||||
if(!rrset_belongs_in_reply(s, ntohs(key->rk.type), qtype, dnssec))
|
||||
return RETVAL_OK;
|
||||
|
||||
owner_labs = dname_count_labels(key->rk.dname);
|
||||
owner_pos = ldns_buffer_position(pkt);
|
||||
|
||||
if(do_data) {
|
||||
const ldns_rr_descriptor* c = type_rdata_compressable(key);
|
||||
for(i=0; i<data->count; i++) {
|
||||
if((r=compress_owner(key, pkt, region, tree,
|
||||
owner_pos, &owner_ptr, owner_labs))
|
||||
!= RETVAL_OK)
|
||||
return r;
|
||||
ldns_buffer_write(pkt, &key->rk.type, 2);
|
||||
ldns_buffer_write(pkt, &key->rk.rrset_class, 2);
|
||||
ldns_buffer_write_u32(pkt, data->rr_ttl[i]-timenow);
|
||||
if(c) {
|
||||
if((r=compress_rdata(pkt, data->rr_data[i],
|
||||
data->rr_len[i], region, tree, c))
|
||||
!= RETVAL_OK)
|
||||
return r;
|
||||
} else {
|
||||
if(ldns_buffer_remaining(pkt) < data->rr_len[i])
|
||||
return RETVAL_TRUNC;
|
||||
ldns_buffer_write(pkt, data->rr_data[i],
|
||||
data->rr_len[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* insert rrsigs */
|
||||
if(do_sig && dnssec) {
|
||||
size_t total = data->count+data->rrsig_count;
|
||||
for(i=data->count; i<total; i++) {
|
||||
if(owner_ptr && owner_labs != 1) {
|
||||
if(ldns_buffer_remaining(pkt) <
|
||||
2+4+4+data->rr_len[i])
|
||||
return RETVAL_TRUNC;
|
||||
ldns_buffer_write(pkt, &owner_ptr, 2);
|
||||
} else {
|
||||
if((r=compress_any_dname(key->rk.dname,
|
||||
pkt, owner_labs, region, tree))
|
||||
!= RETVAL_OK)
|
||||
return r;
|
||||
if(ldns_buffer_remaining(pkt) <
|
||||
4+4+data->rr_len[i])
|
||||
return RETVAL_TRUNC;
|
||||
}
|
||||
ldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG);
|
||||
ldns_buffer_write(pkt, &key->rk.rrset_class, 2);
|
||||
ldns_buffer_write_u32(pkt, data->rr_ttl[i]-timenow);
|
||||
/* rrsig rdata cannot be compressed, perform 100+ byte
|
||||
* memcopy. */
|
||||
ldns_buffer_write(pkt, data->rr_data[i],
|
||||
data->rr_len[i]);
|
||||
}
|
||||
}
|
||||
/* change rrnum only after we are sure it fits */
|
||||
if(do_data)
|
||||
*num_rrs += data->count;
|
||||
if(do_sig && dnssec)
|
||||
*num_rrs += data->rrsig_count;
|
||||
|
||||
return RETVAL_OK;
|
||||
}
|
||||
|
||||
/** store msg section in wireformat buffer, return RETVAL_* */
|
||||
static int
|
||||
insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
|
||||
ldns_buffer* pkt, size_t rrsets_before, uint32_t timenow,
|
||||
region_type* region, struct compress_tree_node** tree,
|
||||
ldns_pkt_section s, uint16_t qtype, int dnssec)
|
||||
{
|
||||
int r;
|
||||
size_t i, setstart;
|
||||
*num_rrs = 0;
|
||||
if(s != LDNS_SECTION_ADDITIONAL) {
|
||||
for(i=0; i<num_rrsets; i++) {
|
||||
setstart = ldns_buffer_position(pkt);
|
||||
if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
|
||||
pkt, num_rrs, timenow, region, 1, 1, tree,
|
||||
s, qtype, dnssec))
|
||||
!= RETVAL_OK) {
|
||||
/* Bad, but if due to size must set TC bit */
|
||||
/* trim off the rrset neatly. */
|
||||
ldns_buffer_set_position(pkt, setstart);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(i=0; i<num_rrsets; i++) {
|
||||
setstart = ldns_buffer_position(pkt);
|
||||
if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
|
||||
pkt, num_rrs, timenow, region, 1, 0, tree,
|
||||
s, qtype, dnssec))
|
||||
!= RETVAL_OK) {
|
||||
ldns_buffer_set_position(pkt, setstart);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if(dnssec)
|
||||
for(i=0; i<num_rrsets; i++) {
|
||||
setstart = ldns_buffer_position(pkt);
|
||||
if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
|
||||
pkt, num_rrs, timenow, region, 0, 1, tree,
|
||||
s, qtype, dnssec))
|
||||
!= RETVAL_OK) {
|
||||
ldns_buffer_set_position(pkt, setstart);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return RETVAL_OK;
|
||||
}
|
||||
|
||||
/** store query section in wireformat buffer, return RETVAL */
|
||||
static int
|
||||
insert_query(struct query_info* qinfo, struct compress_tree_node* tree,
|
||||
ldns_buffer* buffer, struct region* region)
|
||||
{
|
||||
if(ldns_buffer_remaining(buffer) <
|
||||
qinfo->qname_len+sizeof(uint16_t)*2)
|
||||
return RETVAL_TRUNC; /* buffer too small */
|
||||
if(!compress_tree_store(&tree, qinfo->qname,
|
||||
dname_count_labels(qinfo->qname),
|
||||
ldns_buffer_position(buffer), region, NULL))
|
||||
return RETVAL_OUTMEM;
|
||||
ldns_buffer_write(buffer, qinfo->qname, qinfo->qname_len);
|
||||
ldns_buffer_write_u16(buffer, qinfo->qtype);
|
||||
ldns_buffer_write_u16(buffer, qinfo->qclass);
|
||||
return RETVAL_OK;
|
||||
}
|
||||
|
||||
int
|
||||
reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
|
||||
uint16_t id, uint16_t flags, ldns_buffer* buffer, uint32_t timenow,
|
||||
region_type* region, uint16_t udpsize, int dnssec)
|
||||
{
|
||||
uint16_t ancount=0, nscount=0, arcount=0;
|
||||
struct compress_tree_node* tree = 0;
|
||||
int r;
|
||||
|
||||
ldns_buffer_clear(buffer);
|
||||
if(udpsize < ldns_buffer_limit(buffer))
|
||||
ldns_buffer_set_limit(buffer, udpsize);
|
||||
if(ldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE)
|
||||
return 0;
|
||||
|
||||
ldns_buffer_write(buffer, &id, sizeof(uint16_t));
|
||||
ldns_buffer_write_u16(buffer, flags);
|
||||
ldns_buffer_write_u16(buffer, rep->qdcount);
|
||||
/* set an, ns, ar counts to zero in case of small packets */
|
||||
ldns_buffer_write(buffer, "\000\000\000\000\000\000", 6);
|
||||
|
||||
/* insert query section */
|
||||
if(rep->qdcount) {
|
||||
if((r=insert_query(qinfo, tree, buffer, region)) !=
|
||||
RETVAL_OK) {
|
||||
if(r == RETVAL_TRUNC) {
|
||||
/* create truncated message */
|
||||
ldns_buffer_write_u16_at(buffer, 4, 0);
|
||||
LDNS_TC_SET(ldns_buffer_begin(buffer));
|
||||
ldns_buffer_flip(buffer);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* insert answer section */
|
||||
if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer,
|
||||
0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype,
|
||||
dnssec)) != RETVAL_OK) {
|
||||
if(r == RETVAL_TRUNC) {
|
||||
/* create truncated message */
|
||||
ldns_buffer_write_u16_at(buffer, 6, ancount);
|
||||
LDNS_TC_SET(ldns_buffer_begin(buffer));
|
||||
ldns_buffer_flip(buffer);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
ldns_buffer_write_u16_at(buffer, 6, ancount);
|
||||
|
||||
/* insert auth section */
|
||||
if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer,
|
||||
rep->an_numrrsets, timenow, region, &tree,
|
||||
LDNS_SECTION_AUTHORITY, qinfo->qtype, dnssec)) != RETVAL_OK) {
|
||||
if(r == RETVAL_TRUNC) {
|
||||
/* create truncated message */
|
||||
ldns_buffer_write_u16_at(buffer, 8, nscount);
|
||||
LDNS_TC_SET(ldns_buffer_begin(buffer));
|
||||
ldns_buffer_flip(buffer);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
ldns_buffer_write_u16_at(buffer, 8, nscount);
|
||||
|
||||
/* insert add section */
|
||||
if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer,
|
||||
rep->an_numrrsets + rep->ns_numrrsets, timenow, region,
|
||||
&tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype,
|
||||
dnssec)) != RETVAL_OK) {
|
||||
if(r == RETVAL_TRUNC) {
|
||||
/* no need to set TC bit, this is the additional */
|
||||
ldns_buffer_write_u16_at(buffer, 10, arcount);
|
||||
ldns_buffer_flip(buffer);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
ldns_buffer_write_u16_at(buffer, 10, arcount);
|
||||
ldns_buffer_flip(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t
|
||||
calc_edns_field_size(struct edns_data* edns)
|
||||
{
|
||||
if(!edns || !edns->edns_present)
|
||||
return 0;
|
||||
/* domain root '.' + type + class + ttl + rdatalen(=0) */
|
||||
return 1 + 2 + 2 + 4 + 2;
|
||||
}
|
||||
|
||||
void
|
||||
attach_edns_record(ldns_buffer* pkt, struct edns_data* edns)
|
||||
{
|
||||
size_t len;
|
||||
if(!edns || !edns->edns_present)
|
||||
return;
|
||||
/* inc additional count */
|
||||
ldns_buffer_write_u16_at(pkt, 10,
|
||||
ldns_buffer_read_u16_at(pkt, 10) + 1);
|
||||
len = ldns_buffer_limit(pkt);
|
||||
ldns_buffer_clear(pkt);
|
||||
ldns_buffer_set_position(pkt, len);
|
||||
/* write EDNS record */
|
||||
ldns_buffer_write_u8(pkt, 0); /* '.' label */
|
||||
ldns_buffer_write_u16(pkt, LDNS_RR_TYPE_OPT); /* type */
|
||||
ldns_buffer_write_u16(pkt, edns->udp_size); /* class */
|
||||
ldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */
|
||||
ldns_buffer_write_u8(pkt, edns->edns_version);
|
||||
ldns_buffer_write_u16(pkt, edns->bits);
|
||||
ldns_buffer_write_u16(pkt, 0); /* rdatalen */
|
||||
ldns_buffer_flip(pkt);
|
||||
}
|
||||
|
||||
int
|
||||
reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
|
||||
uint16_t id, uint16_t qflags, ldns_buffer* pkt, uint32_t timenow,
|
||||
int cached, struct region* region, uint16_t udpsize,
|
||||
struct edns_data* edns, int dnssec)
|
||||
{
|
||||
uint16_t flags;
|
||||
int attach_edns = 1;
|
||||
|
||||
if(!cached) {
|
||||
/* original flags, copy RD bit from query. */
|
||||
flags = rep->flags | (qflags & BIT_RD);
|
||||
} else {
|
||||
/* remove AA bit, copy RD and CD bits from query. */
|
||||
flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD));
|
||||
}
|
||||
log_assert(flags & BIT_QR); /* QR bit must be on in our replies */
|
||||
if(udpsize < LDNS_HEADER_SIZE)
|
||||
return 0;
|
||||
if(udpsize < LDNS_HEADER_SIZE + calc_edns_field_size(edns)) {
|
||||
/* packet too small to contain edns, omit it. */
|
||||
attach_edns = 0;
|
||||
} else {
|
||||
/* reserve space for edns record */
|
||||
udpsize -= calc_edns_field_size(edns);
|
||||
}
|
||||
|
||||
if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region,
|
||||
udpsize, dnssec)) {
|
||||
log_err("reply encode: out of memory");
|
||||
return 0;
|
||||
}
|
||||
if(attach_edns)
|
||||
attach_edns_record(pkt, edns);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
qinfo_query_encode(ldns_buffer* pkt, struct query_info* qinfo)
|
||||
{
|
||||
uint16_t flags = 0; /* QUERY, NOERROR */
|
||||
ldns_buffer_clear(pkt);
|
||||
log_assert(ldns_buffer_remaining(pkt) >= 12+255+4/*max query*/);
|
||||
ldns_buffer_skip(pkt, 2); /* id done later */
|
||||
ldns_buffer_write_u16(pkt, flags);
|
||||
ldns_buffer_write_u16(pkt, 1); /* query count */
|
||||
ldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */
|
||||
ldns_buffer_write(pkt, qinfo->qname, qinfo->qname_len);
|
||||
ldns_buffer_write_u16(pkt, qinfo->qtype);
|
||||
ldns_buffer_write_u16(pkt, qinfo->qclass);
|
||||
ldns_buffer_flip(pkt);
|
||||
}
|
||||
|
||||
void
|
||||
error_encode(ldns_buffer* buf, int r, struct query_info* qinfo,
|
||||
uint16_t qid, uint16_t qflags, struct edns_data* edns)
|
||||
{
|
||||
uint16_t flags;
|
||||
|
||||
ldns_buffer_clear(buf);
|
||||
ldns_buffer_write(buf, &qid, sizeof(uint16_t));
|
||||
flags = (uint16_t)(BIT_QR | BIT_RA | r); /* QR and retcode*/
|
||||
flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */
|
||||
ldns_buffer_write_u16(buf, flags);
|
||||
if(qinfo) flags = 1;
|
||||
else flags = 0;
|
||||
ldns_buffer_write_u16(buf, flags);
|
||||
flags = 0;
|
||||
ldns_buffer_write(buf, &flags, sizeof(uint16_t));
|
||||
ldns_buffer_write(buf, &flags, sizeof(uint16_t));
|
||||
ldns_buffer_write(buf, &flags, sizeof(uint16_t));
|
||||
if(qinfo) {
|
||||
ldns_buffer_write(buf, qinfo->qname, qinfo->qname_len);
|
||||
ldns_buffer_write_u16(buf, qinfo->qtype);
|
||||
ldns_buffer_write_u16(buf, qinfo->qclass);
|
||||
}
|
||||
ldns_buffer_flip(buf);
|
||||
if(edns) {
|
||||
struct edns_data es = *edns;
|
||||
es.edns_version = EDNS_ADVERTISED_VERSION;
|
||||
es.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
es.ext_rcode = 0;
|
||||
es.bits &= EDNS_DO;
|
||||
if(ldns_buffer_limit(buf) + calc_edns_field_size(&es) >
|
||||
edns->udp_size)
|
||||
return;
|
||||
attach_edns_record(buf, &es);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* util/data/msgencode.h - encode compressed DNS messages.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains temporary data structures and routines to create
|
||||
* compressed DNS messages.
|
||||
*/
|
||||
|
||||
#ifndef UTIL_DATA_MSGENCODE_H
|
||||
#define UTIL_DATA_MSGENCODE_H
|
||||
struct query_info;
|
||||
struct reply_info;
|
||||
struct region;
|
||||
struct edns_data;
|
||||
|
||||
/**
|
||||
* Generate answer from reply_info.
|
||||
* @param qinf: query information that provides query section in packet.
|
||||
* @param rep: reply to fill in.
|
||||
* @param id: id word from the query.
|
||||
* @param qflags: flags word from the query.
|
||||
* @param dest: buffer to put message into; will truncate if it does not fit.
|
||||
* @param timenow: time to subtract.
|
||||
* @param cached: set true if a cached reply (so no AA bit).
|
||||
* set false for the first reply.
|
||||
* @param region: where to allocate temp variables (for compression).
|
||||
* @param udpsize: size of the answer, 512, from EDNS, or 64k for TCP.
|
||||
* @param edns: EDNS data included in the answer, NULL for none.
|
||||
* or if edns_present = 0, it is not included.
|
||||
* @param dnssec: if 0 DNSSEC records are omitted from the answer.
|
||||
* @return: 0 on error (server failure).
|
||||
*/
|
||||
int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
|
||||
uint16_t id, uint16_t qflags, ldns_buffer* dest, uint32_t timenow,
|
||||
int cached, struct region* region, uint16_t udpsize,
|
||||
struct edns_data* edns, int dnssec);
|
||||
|
||||
/**
|
||||
* Regenerate the wireformat from the stored msg reply.
|
||||
* If the buffer is too small then the message is truncated at a whole
|
||||
* rrset and the TC bit set, or whole rrsets are left out of the additional
|
||||
* and the TC bit is not set.
|
||||
* @param qinfo: query info to store.
|
||||
* @param rep: reply to store.
|
||||
* @param id: id value to store, network order.
|
||||
* @param flags: flags value to store, host order.
|
||||
* @param buffer: buffer to store the packet into.
|
||||
* @param timenow: time now, to adjust ttl values.
|
||||
* @param region: to store temporary data in.
|
||||
* @param udpsize: size of the answer, 512, from EDNS, or 64k for TCP.
|
||||
* @param dnssec: if 0 DNSSEC records are omitted from the answer.
|
||||
* @return: nonzero is success, or
|
||||
* 0 on error: malloc failure (no log_err has been done).
|
||||
*/
|
||||
int reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
|
||||
uint16_t id, uint16_t flags, ldns_buffer* buffer, uint32_t timenow,
|
||||
struct region* region, uint16_t udpsize, int dnssec);
|
||||
|
||||
/**
|
||||
* Encode query packet. Assumes the buffer is large enough.
|
||||
* @param pkt: where to store the packet.
|
||||
* @param qinfo: query info.
|
||||
*/
|
||||
void qinfo_query_encode(ldns_buffer* pkt, struct query_info* qinfo);
|
||||
|
||||
/**
|
||||
* Estimate size of EDNS record in packet. EDNS record will be no larger.
|
||||
* @param edns: edns data or NULL.
|
||||
* @return octets to reserve for EDNS.
|
||||
*/
|
||||
uint16_t calc_edns_field_size(struct edns_data* edns);
|
||||
|
||||
/**
|
||||
* Attach EDNS record to buffer. Buffer has complete packet. There must
|
||||
* be enough room left for the EDNS record.
|
||||
* @param pkt: packet added to.
|
||||
* @param edns: if NULL or present=0, nothing is added to the packet.
|
||||
*/
|
||||
void attach_edns_record(ldns_buffer* pkt, struct edns_data* edns);
|
||||
|
||||
/**
|
||||
* Encode an error. With QR and RA set.
|
||||
*
|
||||
* @param pkt: where to store the packet.
|
||||
* @param r: RCODE value to encode.
|
||||
* @param qinfo: if not NULL, the query is included.
|
||||
* @param qid: query ID to set in packet. network order.
|
||||
* @param qflags: original query flags (to copy RD and CD bits). host order.
|
||||
* @param edns: if not NULL, this is the query edns info,
|
||||
* and an edns reply is attached. Only attached if EDNS record fits reply.
|
||||
*/
|
||||
void error_encode(ldns_buffer* pkt, int r, struct query_info* qinfo,
|
||||
uint16_t qid, uint16_t qflags, struct edns_data* edns);
|
||||
|
||||
#endif /* UTIL_DATA_MSGENCODE_H */
|
||||
+29
-31
@@ -44,7 +44,7 @@
|
||||
#include "util/storage/lookup3.h"
|
||||
#include "util/region-allocator.h"
|
||||
|
||||
/** smart comparison of (compressed, valid) dnames from packet. */
|
||||
/** smart comparison of (compressed, valid) dnames from packet */
|
||||
static int
|
||||
smart_compare(ldns_buffer* pkt, uint8_t* dnow,
|
||||
uint8_t* dprfirst, uint8_t* dprlast)
|
||||
@@ -96,7 +96,7 @@ new_rrset(struct msg_parse* msg, uint8_t* dname, size_t dnamelen,
|
||||
return p;
|
||||
}
|
||||
|
||||
/** See if next rrset is nsec at zone apex. */
|
||||
/** See if next rrset is nsec at zone apex */
|
||||
static int
|
||||
nsec_at_apex(ldns_buffer* pkt)
|
||||
{
|
||||
@@ -143,32 +143,30 @@ nsec_at_apex(ldns_buffer* pkt)
|
||||
|
||||
/** Calculate rrset flags */
|
||||
static uint32_t
|
||||
pkt_rrset_flags(struct msg_parse* msg, ldns_buffer* pkt, uint16_t type)
|
||||
pkt_rrset_flags(ldns_buffer* pkt, uint16_t type)
|
||||
{
|
||||
uint32_t f;
|
||||
if(msg->flags & BIT_CD)
|
||||
f = PACKED_RRSET_CD;
|
||||
else f = 0;
|
||||
uint32_t f = 0;
|
||||
if(type == LDNS_RR_TYPE_NSEC && nsec_at_apex(pkt)) {
|
||||
f |= PACKED_RRSET_NSEC_AT_APEX;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
/** Calculate hash value for rrset in packet. */
|
||||
static hashvalue_t
|
||||
hashvalue_t
|
||||
pkt_hash_rrset(ldns_buffer* pkt, uint8_t* dname, uint16_t type,
|
||||
uint16_t dclass, uint32_t rrset_flags)
|
||||
{
|
||||
/* note this MUST be identical to rrset_key_hash in packed_rrset.c */
|
||||
/* this routine handles compressed names */
|
||||
hashvalue_t h = 0xab;
|
||||
h = hashlittle(&type, sizeof(type), h);
|
||||
h = hashlittle(&dclass, sizeof(dclass), h);
|
||||
h = hashlittle(&type, sizeof(type), h); /* host order */
|
||||
h = hashlittle(&dclass, sizeof(dclass), h); /* netw order */
|
||||
h = hashlittle(&rrset_flags, sizeof(uint32_t), h);
|
||||
h = dname_pkt_hash(pkt, dname, h);
|
||||
return h;
|
||||
}
|
||||
|
||||
/** compare rrset_parse with data. */
|
||||
/** compare rrset_parse with data */
|
||||
static int
|
||||
rrset_parse_equals(struct rrset_parse* p, ldns_buffer* pkt, hashvalue_t h,
|
||||
uint32_t rrset_flags, uint8_t* dname, size_t dnamelen,
|
||||
@@ -182,12 +180,9 @@ rrset_parse_equals(struct rrset_parse* p, ldns_buffer* pkt, hashvalue_t h,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lookup in msg hashtable to find a rrset
|
||||
*/
|
||||
static struct rrset_parse*
|
||||
hashtable_lookup(struct msg_parse* msg, ldns_buffer* pkt, hashvalue_t h,
|
||||
uint32_t rrset_flags, uint8_t* dname, size_t dnamelen,
|
||||
struct rrset_parse*
|
||||
msgparse_hashtable_lookup(struct msg_parse* msg, ldns_buffer* pkt,
|
||||
hashvalue_t h, uint32_t rrset_flags, uint8_t* dname, size_t dnamelen,
|
||||
uint16_t type, uint16_t dclass)
|
||||
{
|
||||
struct rrset_parse* p = msg->hashtable[h & (PARSE_TABLE_SIZE-1)];
|
||||
@@ -229,9 +224,8 @@ pkt_rrsig_covered_equals(ldns_buffer* pkt, uint8_t* here, uint16_t type)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** remove rrset from hash list */
|
||||
static void
|
||||
bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset)
|
||||
void
|
||||
msgparse_bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset)
|
||||
{
|
||||
struct rrset_parse** p;
|
||||
p = &msg->hashtable[ rrset->hash & (PARSE_TABLE_SIZE-1) ];
|
||||
@@ -320,6 +314,7 @@ moveover_rrsigs(ldns_buffer* pkt, region_type* region,
|
||||
/* new */
|
||||
insert = (struct rr_parse*)region_alloc(region,
|
||||
sizeof(struct rr_parse));
|
||||
insert->outside_packet = 0;
|
||||
insert->ttl_data = sig->ttl_data;
|
||||
insert->size = sig->size;
|
||||
} else {
|
||||
@@ -378,7 +373,7 @@ change_rrsig_rrset(struct rrset_parse* sigset, struct msg_parse* msg,
|
||||
return dataset;
|
||||
}
|
||||
/* changeover the type of the rrset to data set */
|
||||
bucket_remove(msg, dataset);
|
||||
msgparse_bucket_remove(msg, dataset);
|
||||
/* insert into new hash bucket */
|
||||
dataset->rrset_bucket_next = msg->hashtable[hash&(PARSE_TABLE_SIZE-1)];
|
||||
msg->hashtable[hash&(PARSE_TABLE_SIZE-1)] = dataset;
|
||||
@@ -448,22 +443,23 @@ find_rrset(struct msg_parse* msg, ldns_buffer* pkt, uint8_t* dname,
|
||||
}
|
||||
}
|
||||
/* find by hashing and lookup in hashtable */
|
||||
*rrset_flags = pkt_rrset_flags(msg, pkt, type);
|
||||
*rrset_flags = pkt_rrset_flags(pkt, type);
|
||||
|
||||
/* if rrsig - try to lookup matching data set first */
|
||||
if(type == LDNS_RR_TYPE_RRSIG && pkt_rrsig_covered(pkt,
|
||||
ldns_buffer_current(pkt), &covtype)) {
|
||||
*hash = pkt_hash_rrset(pkt, dname, covtype, dclass,
|
||||
*rrset_flags);
|
||||
*rrset_prev = hashtable_lookup(msg, pkt, *hash, *rrset_flags,
|
||||
dname, dnamelen, covtype, dclass);
|
||||
*rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash,
|
||||
*rrset_flags, dname, dnamelen, covtype, dclass);
|
||||
if(!*rrset_prev && covtype == LDNS_RR_TYPE_NSEC) {
|
||||
/* if NSEC try with NSEC apex bit twiddled */
|
||||
*rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX;
|
||||
*hash = pkt_hash_rrset(pkt, dname, covtype, dclass,
|
||||
*rrset_flags);
|
||||
*rrset_prev = hashtable_lookup(msg, pkt, *hash,
|
||||
*rrset_flags, dname, dnamelen, covtype, dclass);
|
||||
*rrset_prev = msgparse_hashtable_lookup(msg, pkt,
|
||||
*hash, *rrset_flags, dname, dnamelen, covtype,
|
||||
dclass);
|
||||
}
|
||||
if(*rrset_prev) {
|
||||
*prev_dname_first = (*rrset_prev)->dname;
|
||||
@@ -479,8 +475,9 @@ find_rrset(struct msg_parse* msg, ldns_buffer* pkt, uint8_t* dname,
|
||||
/* find matching rrsig */
|
||||
*hash = pkt_hash_rrset(pkt, dname, LDNS_RR_TYPE_RRSIG,
|
||||
dclass, *rrset_flags);
|
||||
*rrset_prev = hashtable_lookup(msg, pkt, *hash, *rrset_flags,
|
||||
dname, dnamelen, LDNS_RR_TYPE_RRSIG, dclass);
|
||||
*rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash,
|
||||
*rrset_flags, dname, dnamelen, LDNS_RR_TYPE_RRSIG,
|
||||
dclass);
|
||||
if(*rrset_prev && rrset_has_sigover(pkt, *rrset_prev, type,
|
||||
&hasother)) {
|
||||
/* yes! */
|
||||
@@ -498,7 +495,7 @@ find_rrset(struct msg_parse* msg, ldns_buffer* pkt, uint8_t* dname,
|
||||
}
|
||||
|
||||
*hash = pkt_hash_rrset(pkt, dname, type, dclass, *rrset_flags);
|
||||
*rrset_prev = hashtable_lookup(msg, pkt, *hash, *rrset_flags,
|
||||
*rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, *rrset_flags,
|
||||
dname, dnamelen, type, dclass);
|
||||
if(*rrset_prev)
|
||||
*prev_dname_first = (*rrset_prev)->dname;
|
||||
@@ -722,9 +719,10 @@ add_rr_to_rrset(struct rrset_parse* rrset, ldns_buffer* pkt,
|
||||
/* create rr */
|
||||
if(!(rr = (struct rr_parse*)region_alloc(region, sizeof(*rr))))
|
||||
return LDNS_RCODE_SERVFAIL;
|
||||
rr->outside_packet = 0;
|
||||
rr->ttl_data = ldns_buffer_current(pkt);
|
||||
rr->next = 0;
|
||||
if(type == LDNS_RR_TYPE_RRSIG) {
|
||||
if(type == LDNS_RR_TYPE_RRSIG && rrset->type != LDNS_RR_TYPE_RRSIG) {
|
||||
if(rrset->rrsig_last)
|
||||
rrset->rrsig_last->next = rr;
|
||||
else rrset->rrsig_first = rr;
|
||||
|
||||
+38
-2
@@ -171,6 +171,9 @@ struct rr_parse {
|
||||
* its dname, type and class are the same and stored for the rrset.
|
||||
*/
|
||||
uint8_t* ttl_data;
|
||||
/** true if ttl_data is not part of the packet, but elsewhere in mem.
|
||||
* Set for generated CNAMEs for DNAMEs. */
|
||||
int outside_packet;
|
||||
/** the length of the rdata if allocated (with no dname compression)*/
|
||||
size_t size;
|
||||
/** next in list of RRs. */
|
||||
@@ -189,8 +192,6 @@ struct rr_parse {
|
||||
#define EDNS_RCODE_BADVERS 16 /** bad EDNS version */
|
||||
/** largest valid compression offset */
|
||||
#define PTR_MAX_OFFSET 0x3fff
|
||||
/** bits for EDNS bitfield */
|
||||
#define EDNS_DO 0x8000 /* Dnssec Ok */
|
||||
|
||||
/**
|
||||
* EDNS data storage
|
||||
@@ -257,4 +258,39 @@ int parse_extract_edns(struct msg_parse* msg, struct edns_data* edns);
|
||||
*/
|
||||
int parse_edns_from_pkt(ldns_buffer* pkt, struct edns_data* edns);
|
||||
|
||||
/**
|
||||
* Calculate hash value for rrset in packet.
|
||||
* @param pkt: the packet.
|
||||
* @param dname: pointer to uncompressed dname, or compressed dname in packet.
|
||||
* @param type: rrset type in host order.
|
||||
* @param dclass: rrset class in network order.
|
||||
* @param rrset_flags: rrset flags (same as packed_rrset flags).
|
||||
* @return hash value
|
||||
*/
|
||||
hashvalue_t pkt_hash_rrset(ldns_buffer* pkt, uint8_t* dname, uint16_t type,
|
||||
uint16_t dclass, uint32_t rrset_flags);
|
||||
|
||||
/**
|
||||
* Lookup in msg hashtable to find a rrset.
|
||||
* @param msg: with the hashtable.
|
||||
* @param pkt: packet for compressed names.
|
||||
* @param h: hash value
|
||||
* @param rrset_flags: flags of rrset sought for.
|
||||
* @param dname: name of rrset sought for.
|
||||
* @param dnamelen: len of dname.
|
||||
* @param type: rrset type, host order.
|
||||
* @param dclass: rrset class, network order.
|
||||
* @return NULL or the rrset_parse if found.
|
||||
*/
|
||||
struct rrset_parse* msgparse_hashtable_lookup(struct msg_parse* msg,
|
||||
ldns_buffer* pkt, hashvalue_t h, uint32_t rrset_flags,
|
||||
uint8_t* dname, size_t dnamelen, uint16_t type, uint16_t dclass);
|
||||
|
||||
/**
|
||||
* Remove rrset from hash table.
|
||||
* @param msg: with hashtable.
|
||||
* @param rrset: with hash value and id info.
|
||||
*/
|
||||
void msgparse_bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset);
|
||||
|
||||
#endif /* UTIL_DATA_MSGPARSE_H */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user