mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-08-17 21:25:50 +02:00
Compare commits
148
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82e4110e09 | ||
|
|
fb57492c0a | ||
|
|
bde1ce87d5 | ||
|
|
b7a627b8cc | ||
|
|
ea7630845f | ||
|
|
04edbb68b7 | ||
|
|
2884751446 | ||
|
|
c8982075c5 | ||
|
|
0c8d24b653 | ||
|
|
d2b3e464ac | ||
|
|
f3525b2081 | ||
|
|
849026931b | ||
|
|
da073cfc3e | ||
|
|
caaad8b810 | ||
|
|
0fe0251c8d | ||
|
|
9be27227a1 | ||
|
|
a9a47f8b41 | ||
|
|
46f7dc4d08 | ||
|
|
0e5b4105d1 | ||
|
|
9b31997a4c | ||
|
|
98552115d0 | ||
|
|
d58327876f | ||
|
|
8b048a8229 | ||
|
|
967793aad3 | ||
|
|
6ffe218762 | ||
|
|
da8ae735ce | ||
|
|
8cdb815df6 | ||
|
|
e48ecb0e15 | ||
|
|
635c65f4ed | ||
|
|
2b4ef794b9 | ||
|
|
0e1b3fb3e0 | ||
|
|
be483a3bfb | ||
|
|
6ef599255d | ||
|
|
c8f2bf0333 | ||
|
|
e5196b25ae | ||
|
|
193892e52d | ||
|
|
a6c543362d | ||
|
|
32396dc677 | ||
|
|
ed57c4de4c | ||
|
|
bfe329e34d | ||
|
|
9e30587b1e | ||
|
|
cf9837bf7f | ||
|
|
0bdfbccbdc | ||
|
|
3bb9aaafca | ||
|
|
7bfaf07981 | ||
|
|
2e28288b1d | ||
|
|
452b7cc069 | ||
|
|
4d194ac2fa | ||
|
|
f041511951 | ||
|
|
0e4bda4c55 | ||
|
|
e56a3a60ef | ||
|
|
faa7ad14c6 | ||
|
|
c87fe76e47 | ||
|
|
a18af6d1e1 | ||
|
|
3adebc8721 | ||
|
|
1a4fadaf00 | ||
|
|
b26f289ccc | ||
|
|
0e000a8587 | ||
|
|
84ae5c8e79 | ||
|
|
05f9d35f00 | ||
|
|
c7ad292438 | ||
|
|
b63db13e72 | ||
|
|
6ee18c6824 | ||
|
|
e8f4ac9b1a | ||
|
|
5e420a8275 | ||
|
|
bb3a1178b6 | ||
|
|
306db56f1f | ||
|
|
95b0a129e2 | ||
|
|
c4e1a83be5 | ||
|
|
fec9e1d262 | ||
|
|
c854c57c6f | ||
|
|
ee6e142192 | ||
|
|
ac74d176cc | ||
|
|
8343901c0f | ||
|
|
b7745a2e53 | ||
|
|
82cab3f855 | ||
|
|
6ce08cf242 | ||
|
|
877038b6d9 | ||
|
|
a2227517ed | ||
|
|
04a896098e | ||
|
|
7e84653485 | ||
|
|
71723c6276 | ||
|
|
219d0c70dd | ||
|
|
85c88707ca | ||
|
|
e7d64c7eac | ||
|
|
d7d8e117ef | ||
|
|
d9e9a31562 | ||
|
|
5d594b301f | ||
|
|
648a776493 | ||
|
|
8404701a09 | ||
|
|
4b065a443c | ||
|
|
0b04728785 | ||
|
|
76a31aacd3 | ||
|
|
179273a13a | ||
|
|
7f8b84d0e5 | ||
|
|
2b08436c4c | ||
|
|
5cc518f114 | ||
|
|
1315edc5e9 | ||
|
|
97f5d3d28e | ||
|
|
ad78119e19 | ||
|
|
2756924a44 | ||
|
|
3fd809d537 | ||
|
|
22b8057cca | ||
|
|
fe6f28ddc5 | ||
|
|
1e4dc049eb | ||
|
|
1a4d24816b | ||
|
|
1f47306af9 | ||
|
|
f1a3c9b646 | ||
|
|
a67e62dd40 | ||
|
|
1b6d254224 | ||
|
|
a0e442eb32 | ||
|
|
7eaceaab2f | ||
|
|
2ba3ff9f7d | ||
|
|
e7cb0f27cc | ||
|
|
317938efba | ||
|
|
d6ce1f426d | ||
|
|
9f97c4ac44 | ||
|
|
6a5f5e652a | ||
|
|
61b42edaef | ||
|
|
060d4740c2 | ||
|
|
5a198e0b4c | ||
|
|
abf85e9270 | ||
|
|
ab4b9aa250 | ||
|
|
a2f143cfcc | ||
|
|
e297886386 | ||
|
|
cdf334528f | ||
|
|
1f9ad9e66e | ||
|
|
75073cefea | ||
|
|
9374c1465a | ||
|
|
a2311972e7 | ||
|
|
11a764a82f | ||
|
|
f187e1da7e | ||
|
|
2dbc83d5ae | ||
|
|
fcac316d63 | ||
|
|
43a2640a9c | ||
|
|
8892d4b63c | ||
|
|
1c33240c16 | ||
|
|
b97a63aa95 | ||
|
|
2ef042ef0e | ||
|
|
185c6f76b1 | ||
|
|
87fe7013a0 | ||
|
|
77019eeb5e | ||
|
|
1152a4c5e8 | ||
|
|
83f8775d5a | ||
|
|
d9907b118f | ||
|
|
6ccdaf41e5 | ||
|
|
eb4ba518f1 | ||
|
|
6a355b37fe |
+72
-29
@@ -1,9 +1,13 @@
|
||||
# Copyright 2007 NLnet Labs
|
||||
# See the file LICENSE for the license
|
||||
#
|
||||
# Standard installation pathnames
|
||||
|
||||
QUIET=yes
|
||||
debug_enabled=@debug_enabled@
|
||||
ifeq ($(debug_enabled),yes)
|
||||
QUIET=yes
|
||||
else
|
||||
QUIET=no
|
||||
endif
|
||||
|
||||
ifeq "$(QUIET)" "yes"
|
||||
Q=@
|
||||
INFO=@echo
|
||||
@@ -33,7 +37,7 @@ configfile=@ub_conf_file@
|
||||
YACC=@YACC@
|
||||
LEX=@LEX@
|
||||
CC=@CC@
|
||||
CPPFLAGS=-I$(srcdir) @CPPFLAGS@ @DEFS@
|
||||
CPPFLAGS=-I$(srcdir) -I. @CPPFLAGS@ @DEFS@
|
||||
CFLAGS=@CFLAGS@
|
||||
LDFLAGS=@LDFLAGS@
|
||||
LIBS=@LIBS@
|
||||
@@ -62,54 +66,71 @@ COMMON_OBJ=$(addprefix $(BUILD),$(COMMON_SRC:.c=.o))
|
||||
COMPAT_OBJ=$(addprefix $(BUILD)compat/,$(LIBOBJS))
|
||||
UNITTEST_SRC=$(patsubst $(srcdir)/%,%, \
|
||||
$(wildcard $(srcdir)/testcode/unit*.c)) \
|
||||
testcode/readhex.c testcode/ldns-testpkts.c checkconf/worker_cb.c \
|
||||
testcode/readhex.c testcode/ldns-testpkts.c smallapp/worker_cb.c \
|
||||
$(COMMON_SRC)
|
||||
UNITTEST_OBJ=$(addprefix $(BUILD),$(UNITTEST_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
DAEMON_SRC=$(patsubst $(srcdir)/%,%, $(wildcard $(srcdir)/daemon/*.c)) \
|
||||
$(COMMON_SRC)
|
||||
DAEMON_OBJ=$(addprefix $(BUILD),$(DAEMON_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
CHECKCONF_SRC=checkconf/unbound-checkconf.c checkconf/worker_cb.c $(COMMON_SRC)
|
||||
CHECKCONF_SRC=smallapp/unbound-checkconf.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
CHECKCONF_OBJ=$(addprefix $(BUILD),$(CHECKCONF_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
HOST_SRC=smallapp/unbound-host.c
|
||||
HOST_OBJ=$(addprefix $(BUILD),$(HOST_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
TESTBOUND_SRC=testcode/testbound.c testcode/ldns-testpkts.c \
|
||||
daemon/worker.c daemon/acl_list.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 checkconf/worker_cb.c $(COMMON_SRC)
|
||||
LOCKVERIFY_SRC=testcode/lock_verify.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
LOCKVERIFY_OBJ=$(addprefix $(BUILD),$(LOCKVERIFY_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
PKTVIEW_SRC=testcode/pktview.c testcode/readhex.c checkconf/worker_cb.c \
|
||||
PKTVIEW_SRC=testcode/pktview.c testcode/readhex.c smallapp/worker_cb.c \
|
||||
$(COMMON_SRC)
|
||||
PKTVIEW_OBJ=$(addprefix $(BUILD),$(PKTVIEW_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
SIGNIT_SRC=testcode/signit.c checkconf/worker_cb.c $(COMMON_SRC)
|
||||
SIGNIT_SRC=testcode/signit.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
SIGNIT_OBJ=$(addprefix $(BUILD),$(SIGNIT_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
MEMSTATS_SRC=testcode/memstats.c checkconf/worker_cb.c $(COMMON_SRC)
|
||||
MEMSTATS_SRC=testcode/memstats.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
MEMSTATS_OBJ=$(addprefix $(BUILD),$(MEMSTATS_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \
|
||||
ASYNCLOOK_SRC=testcode/asynclook.c
|
||||
ASYNCLOOK_OBJ=$(addprefix $(BUILD),$(ASYNCLOOK_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
STREAMTCP_SRC=testcode/streamtcp.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
STREAMTCP_OBJ=$(addprefix $(BUILD),$(STREAMTCP_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
LIBUNBOUND_SRC=$(patsubst $(srcdir)/%,%, \
|
||||
$(wildcard $(srcdir)/libunbound/*.c) $(COMMON_SRC))
|
||||
LIBUNBOUND_OBJ=$(addprefix $(BUILD),$(LIBUNBOUND_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
ALL_SRC=$(sort $(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \
|
||||
$(TESTBOUND_SRC) $(LOCKVERIFY_SRC) $(PKTVIEW_SRC) $(SIGNIT_SRC) \
|
||||
$(MEMSTATS_SRC) $(CHECKCONF_SRC)
|
||||
$(MEMSTATS_SRC) $(CHECKCONF_SRC) $(LIBUNBOUND_SRC) $(HOST_SRC) \
|
||||
$(ASYNCLOOK_SRC) $(STREAMTCP_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) $(staticexe) $(RUNTIME_PATH) $(CFLAGS) $(LDFLAGS)
|
||||
LINK_LIB=$(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) $(LDFLAGS) -release $(VERSION)
|
||||
LINK_LIB=$(LIBTOOL) --tag=CC --mode=link $(CC) $(RUNTIME_PATH) $(CFLAGS) $(LDFLAGS) $(staticexe) -release $(VERSION) -no-undefined
|
||||
|
||||
.PHONY: clean realclean doc lint all install uninstall tests test download_ldns strip
|
||||
.PHONY: clean realclean doc lint all install uninstall tests test download_ldns strip lib
|
||||
|
||||
$(BUILD)%.o: $(srcdir)/%.c
|
||||
$(INFO) Build $<
|
||||
@if test ! -z "$(ldnsdir)" -a ! -e $(ldnsdir)/include/ldns/ldns.h; \
|
||||
then (cd $(ldnsdir); $(MAKE) copy-headers); fi
|
||||
@if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
@-if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
$Q$(COMPILE) -c $< -o $@
|
||||
|
||||
all: $(COMMON_OBJ) unbound unbound-checkconf
|
||||
all: $(COMMON_OBJ) unbound unbound-checkconf lib unbound-host
|
||||
|
||||
tests: all unittest testbound lock-verify pktview signit memstats
|
||||
tests: all unittest testbound lock-verify pktview signit memstats \
|
||||
asynclook streamtcp
|
||||
|
||||
test: tests
|
||||
bash testcode/do-tests.sh
|
||||
|
||||
lib: libunbound.la
|
||||
|
||||
libunbound.la: $(LIBUNBOUND_OBJ)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK_LIB) --export-symbols $(srcdir)/libunbound/ubsyms.def -o $@ $(sort $(LIBUNBOUND_OBJ:.o=.lo)) -rpath $(libdir) $(LIBS)
|
||||
|
||||
ifeq ($(patsubst ldns-src%,ldns-src,$(ldnsdir)),ldns-src)
|
||||
ldnslib=$(ldnsdir)/lib/libldns.a
|
||||
$(ldnslib):
|
||||
@@ -127,6 +148,10 @@ unbound-checkconf: $(CHECKCONF_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(CHECKCONF_OBJ)) $(LIBS)
|
||||
|
||||
unbound-host: $(HOST_OBJ) libunbound.la $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(HOST_OBJ)) -L. -L.libs -lunbound $(LIBS)
|
||||
|
||||
unittest: $(UNITTEST_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(UNITTEST_OBJ)) $(LIBS)
|
||||
@@ -151,6 +176,14 @@ memstats: $(MEMSTATS_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(MEMSTATS_OBJ)) $(LIBS)
|
||||
|
||||
asynclook: $(ASYNCLOOK_OBJ) $(ldnslib) libunbound.la
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(ASYNCLOOK_OBJ)) $(LIBS) -L. -L.libs -lunbound
|
||||
|
||||
streamtcp: $(STREAMTCP_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(STREAMTCP_OBJ)) $(LIBS)
|
||||
|
||||
#testcode/ldns-testpkts.c: $(ldnsdir)/examples/ldns-testpkts.c \
|
||||
# $(ldnsdir)/examples/ldns-testpkts.h
|
||||
# cp $(ldnsdir)/examples/ldns-testpkts.c testcode/ldns-testpkts.c
|
||||
@@ -162,14 +195,14 @@ util/configlexer.c: $(srcdir)/util/configlexer.lex util/configparser.h
|
||||
ifeq "$(strip $(LEX))" ":"
|
||||
$Qecho "rebuild lexer, but no lex program, skipped"
|
||||
else
|
||||
@if test ! -d util; then $(INSTALL) -d util; fi
|
||||
@-if test ! -d util; then $(INSTALL) -d util; fi
|
||||
$Qecho "#include \"util/configyyrename.h\"" > $@
|
||||
$Q$(LEX) -t $< >> $@
|
||||
endif
|
||||
|
||||
util/configparser.c util/configparser.h: $(srcdir)/util/configparser.y
|
||||
$(INFO) Yacc $<
|
||||
@if test ! -d util; then $(INSTALL) -d util; fi
|
||||
@-if test ! -d util; then $(INSTALL) -d util; fi
|
||||
$Q$(YACC) -d -o util/configparser.c $<
|
||||
|
||||
clean:
|
||||
@@ -185,7 +218,7 @@ realclean: clean
|
||||
|
||||
$(BUILD)%.lint: $(srcdir)/%.c
|
||||
$(INFO) Lint $<
|
||||
@if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
@-if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
$Q$(LINT) $(LINTFLAGS) -I. -I$(srcdir) -I$(ldnsdir)/include $<
|
||||
$Qtouch $@
|
||||
|
||||
@@ -195,29 +228,37 @@ tags: $(srcdir)/*.[ch] $(srcdir)/*/*.[ch]
|
||||
ctags -f $(srcdir)/tags $(srcdir)/*.[ch] $(srcdir)/*/*.[ch]
|
||||
|
||||
doc:
|
||||
ifdef doxygen
|
||||
$(doxygen) $(srcdir)/doc/unbound.doxygen
|
||||
endif
|
||||
if test -n "$(doxygen)"; then \
|
||||
$(doxygen) $(srcdir)/doc/unbound.doxygen; fi
|
||||
|
||||
strip:
|
||||
strip unbound
|
||||
strip unbound-checkconf
|
||||
strip unbound-host
|
||||
|
||||
install:
|
||||
$(INSTALL) -d $(bindir)
|
||||
$(INSTALL) -d $(mandir)
|
||||
$(INSTALL) -d $(mandir)/man8
|
||||
$(INSTALL) -d $(mandir)/man5
|
||||
$(INSTALL) -m 755 -d $(bindir)
|
||||
$(INSTALL) -m 755 -d $(mandir)
|
||||
$(INSTALL) -m 755 -d $(mandir)/man8
|
||||
$(INSTALL) -m 755 -d $(mandir)/man5
|
||||
$(INSTALL) -m 755 -d $(libdir)
|
||||
$(INSTALL) -m 755 -d $(includedir)
|
||||
$(LIBTOOL) --mode=install cp unbound $(bindir)/unbound
|
||||
$(LIBTOOL) --mode=install cp unbound-checkconf $(bindir)/unbound-checkconf
|
||||
$(LIBTOOL) --mode=install cp unbound-host $(bindir)/unbound-host
|
||||
$(INSTALL) -c -m 644 $(srcdir)/doc/unbound.8 $(mandir)/man8
|
||||
$(INSTALL) -c -m 644 $(srcdir)/doc/unbound-checkconf.8 $(mandir)/man8
|
||||
$(INSTALL) -c -m 644 $(srcdir)/doc/unbound.conf.5 $(mandir)/man5
|
||||
if test ! -e $(configfile); then $(INSTALL) -d `dirname $(configfile)`; $(INSTALL) -c -m 644 $(srcdir)/doc/example.conf $(configfile); fi
|
||||
$(LIBTOOL) --mode=install cp $(srcdir)/libunbound/unbound.h $(includedir)/unbound.h
|
||||
$(LIBTOOL) --mode=install cp libunbound.la $(libdir)
|
||||
$(LIBTOOL) --mode=finish $(libdir)
|
||||
|
||||
uninstall:
|
||||
rm -f -- $(bindir)/unbound $(bindir)/unbound-checkconf
|
||||
rm -f -- $(bindir)/unbound $(bindir)/unbound-checkconf $(bindir)/unbound-host
|
||||
rm -f -- $(mandir)/man8/unbound.8 $(mandir)/man8/unbound-checkconf.8 $(mandir)/man5/unbound.conf.5
|
||||
rm -f -- $(includedir)/unbound.h
|
||||
$(LIBTOOL) --mode=uninstall rm -f $(libdir)/libunbound.la
|
||||
@echo
|
||||
@echo "You still need to remove `dirname $(configfile)` , $(configfile) by hand"
|
||||
|
||||
@@ -232,13 +273,15 @@ $(BUILD)%.d: $(srcdir)/%.c
|
||||
$(INFO) Depend $<
|
||||
@if test ! -z "$(ldnsdir)" -a ! -e $(ldnsdir)/include/ldns/ldns.h; \
|
||||
then (cd $(ldnsdir); $(MAKE) copy-headers); fi
|
||||
@if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
@-if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
$Q$(SHELL) -ec '$(CC) -MM $(CPPFLAGS) $(CFLAGS) $< \
|
||||
| sed '\''s!\(.*\)\.o[ :]*!$(dir $@)\1.o $@ : !g'\'' > $@; \
|
||||
[ -s $@ ] || rm -f $@'
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(MAKECMDGOALS),realclean)
|
||||
ifeq ($(debug_enabled),yes)
|
||||
-include $(addprefix $(BUILD),$(ALL_SRC:.c=.d))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
+16
-11
@@ -57,14 +57,8 @@
|
||||
/* Define to 1 if you have the `crypto' library (-lcrypto). */
|
||||
#undef HAVE_LIBCRYPTO
|
||||
|
||||
/* Define to 1 if you have the `nsl' library (-lnsl). */
|
||||
#undef HAVE_LIBNSL
|
||||
|
||||
/* Define to 1 if you have the `rt' library (-lrt). */
|
||||
#undef HAVE_LIBRT
|
||||
|
||||
/* Define to 1 if you have the `socket' library (-lsocket). */
|
||||
#undef HAVE_LIBSOCKET
|
||||
/* Define to 1 if you have the `ldns' library (-lldns). */
|
||||
#undef HAVE_LIBLDNS
|
||||
|
||||
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
|
||||
to 0 otherwise. */
|
||||
@@ -82,6 +76,9 @@
|
||||
/* Define to 1 if you have the <openssl/err.h> header file. */
|
||||
#undef HAVE_OPENSSL_ERR_H
|
||||
|
||||
/* Define to 1 if you have the <openssl/rand.h> header file. */
|
||||
#undef HAVE_OPENSSL_RAND_H
|
||||
|
||||
/* Define to 1 if you have the <openssl/ssl.h> header file. */
|
||||
#undef HAVE_OPENSSL_SSL_H
|
||||
|
||||
@@ -188,12 +185,12 @@
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Define to 'int' or type of struct msghdr.msg_iovlen. */
|
||||
#undef TYPE_MSGIOVLEN
|
||||
|
||||
/* use statistics for allocs and frees, for debug use */
|
||||
#undef UNBOUND_ALLOC_STATS
|
||||
|
||||
/* define this to enable debug checks. */
|
||||
#undef UNBOUND_DEBUG
|
||||
|
||||
/* Define if you want to use internal select based events */
|
||||
#undef USE_MINI_EVENT
|
||||
|
||||
@@ -287,6 +284,10 @@
|
||||
#undef vfork
|
||||
|
||||
|
||||
#ifndef UNBOUND_DEBUG
|
||||
# define NDEBUG
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@@ -331,6 +332,10 @@
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENSSL_RAND_H
|
||||
#include <openssl/rand.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ATTR_FORMAT
|
||||
# define ATTR_FORMAT(archetype, string_index, first_to_check) \
|
||||
__attribute__ ((format (archetype, string_index, first_to_check)))
|
||||
|
||||
+92
-45
@@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
AC_PREREQ(2.57)
|
||||
|
||||
AC_INIT(unbound, 0.8, unbound-bugs@nlnetlabs.nl, unbound)
|
||||
AC_INIT(unbound, 0.9, unbound-bugs@nlnetlabs.nl, unbound)
|
||||
|
||||
CFLAGS=
|
||||
AC_AIX
|
||||
@@ -119,6 +119,39 @@ CHECK_COMPILER_FLAG(O2, [CFLAGS="$CFLAGS -O2"])
|
||||
|
||||
AC_CHECK_HEADERS([getopt.h time.h],,, [AC_INCLUDES_DEFAULT])
|
||||
|
||||
CHECK_COMPILER_FLAG_NEEDED(-std=c99 -D__EXTENSIONS__ -D_BSD_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE,
|
||||
[
|
||||
#include "confdefs.h"
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/time.h>
|
||||
#ifdef HAVE_TIME_H
|
||||
#include <time.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
int test() {
|
||||
int a;
|
||||
char **opts = NULL;
|
||||
struct timeval tv;
|
||||
char *t;
|
||||
time_t time = 0;
|
||||
char *buf = NULL;
|
||||
const char* str = NULL;
|
||||
t = ctime_r(&time, buf);
|
||||
tv.tv_usec = 10;
|
||||
srandom(32);
|
||||
a = getopt(2, opts, "a");
|
||||
a = isascii(32);
|
||||
str = gai_strerror(0);
|
||||
return a;
|
||||
}
|
||||
], [CFLAGS="$CFLAGS -std=c99 -D__EXTENSIONS__ -D_BSD_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE"])
|
||||
|
||||
CHECK_COMPILER_FLAG_NEEDED(-std=c99 -D__EXTENSIONS__ -D_BSD_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE,
|
||||
[
|
||||
#include "confdefs.h"
|
||||
@@ -223,10 +256,21 @@ int test() {
|
||||
CHECK_COMPILER_FLAG(xO4, [CFLAGS="$CFLAGS -xO4"])
|
||||
CHECK_COMPILER_FLAG(xtarget=generic, [CFLAGS="$CFLAGS -xtarget=generic"])
|
||||
# flag warnings.
|
||||
CHECK_COMPILER_FLAG(W, [CFLAGS="$CFLAGS -W"])
|
||||
CHECK_COMPILER_FLAG(Wall, [CFLAGS="$CFLAGS -Wall"])
|
||||
CHECK_COMPILER_FLAG(Wextra, [CFLAGS="$CFLAGS -Wextra"])
|
||||
CHECK_COMPILER_FLAG(Wdeclaration-after-statement, [CFLAGS="$CFLAGS -Wdeclaration-after-statement"])
|
||||
AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [Enable debug warnings, asserts, makefile-dependencies]))
|
||||
debug_enabled="$enable_debug"
|
||||
AC_SUBST(debug_enabled)
|
||||
case "$enable_debug" in
|
||||
yes)
|
||||
CHECK_COMPILER_FLAG(W, [CFLAGS="$CFLAGS -W"])
|
||||
CHECK_COMPILER_FLAG(Wall, [CFLAGS="$CFLAGS -Wall"])
|
||||
CHECK_COMPILER_FLAG(Wextra, [CFLAGS="$CFLAGS -Wextra"])
|
||||
CHECK_COMPILER_FLAG(Wdeclaration-after-statement, [CFLAGS="$CFLAGS -Wdeclaration-after-statement"])
|
||||
AC_DEFINE([UNBOUND_DEBUG], [], [define this to enable debug checks.])
|
||||
;;
|
||||
no|*)
|
||||
# nothing to do.
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_C_INLINE
|
||||
|
||||
@@ -277,7 +321,7 @@ AC_CHECK_FORMAT_ATTRIBUTE
|
||||
AC_CHECK_UNUSED_ATTRIBUTE
|
||||
|
||||
if test "$srcdir" != "."; then
|
||||
CPPFLAGS="$CPPFLAGS -I$srcdir";
|
||||
CPPFLAGS="$CPPFLAGS -I$srcdir"
|
||||
fi
|
||||
|
||||
# skip these tests, we do not need them.
|
||||
@@ -351,29 +395,6 @@ AC_INCLUDES_DEFAULT
|
||||
#endif
|
||||
])
|
||||
|
||||
# Check the type of struct msghdr msg_iovlen.
|
||||
AC_MSG_CHECKING([sign of struct msghdr.msg_iovlen])
|
||||
AC_RUN_IFELSE([
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
int main()
|
||||
{
|
||||
struct msghdr h;
|
||||
h.msg_iovlen = -1;
|
||||
if(10/h.msg_iovlen == 0) return 0; /* unsigned */
|
||||
return 1; /* signed */
|
||||
}
|
||||
], [
|
||||
# if compiled and exit status 0
|
||||
cv_msgiovlen_type="unsigned" ], [
|
||||
# not compiled or bad exit status
|
||||
cv_msgiovlen_type="int" ],
|
||||
# when cross compiling
|
||||
[ 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)
|
||||
@@ -415,9 +436,9 @@ AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=pathname],
|
||||
for dir in $withval; do
|
||||
ssldir="$dir"
|
||||
if test -f "$dir/include/openssl/ssl.h"; then
|
||||
found_ssl="yes";
|
||||
found_ssl="yes"
|
||||
AC_DEFINE_UNQUOTED([HAVE_SSL], [], [Define if you have the SSL libraries installed.])
|
||||
CPPFLAGS="$CPPFLAGS -I$ssldir/include";
|
||||
CPPFLAGS="$CPPFLAGS -I$ssldir/include"
|
||||
break;
|
||||
fi
|
||||
done
|
||||
@@ -426,7 +447,7 @@ AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=pathname],
|
||||
else
|
||||
AC_MSG_RESULT(found in $ssldir)
|
||||
HAVE_SSL=yes
|
||||
LDFLAGS="$LDFLAGS -L$ssldir/lib";
|
||||
LDFLAGS="$LDFLAGS -L$ssldir/lib"
|
||||
if test "x$enable_rpath" = xyes; then
|
||||
RUNTIME_PATH="$RUNTIME_PATH -R$ssldir/lib"
|
||||
fi
|
||||
@@ -439,6 +460,7 @@ AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=pathname],
|
||||
fi
|
||||
AC_CHECK_HEADERS([openssl/ssl.h],,, [AC_INCLUDES_DEFAULT])
|
||||
AC_CHECK_HEADERS([openssl/err.h],,, [AC_INCLUDES_DEFAULT])
|
||||
AC_CHECK_HEADERS([openssl/rand.h],,, [AC_INCLUDES_DEFAULT])
|
||||
|
||||
# check for thread library.
|
||||
AC_ARG_WITH(pthreads, AC_HELP_STRING([--with-pthreads],
|
||||
@@ -476,8 +498,12 @@ if test x_$withval != x_no; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# check to see if libraries are needed for these functions.
|
||||
AC_SEARCH_LIBS([inet_pton], [nsl])
|
||||
AC_SEARCH_LIBS([socket], [socket])
|
||||
# check for library used by libevent after 1.3c
|
||||
AC_CHECK_LIB(rt, clock_gettime)
|
||||
AC_SEARCH_LIBS([clock_gettime], [rt])
|
||||
|
||||
# check for libevent
|
||||
AC_ARG_WITH(libevent, AC_HELP_STRING([--with-libevent=pathname],
|
||||
[set path to libevent (will check /usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr)]),
|
||||
@@ -485,22 +511,34 @@ AC_ARG_WITH(libevent, AC_HELP_STRING([--with-libevent=pathname],
|
||||
if test x_$withval != x_no; then
|
||||
AC_MSG_CHECKING(for libevent)
|
||||
if test x_$withval = x_ -o x_$withval = x_yes; then
|
||||
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr";
|
||||
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
|
||||
fi
|
||||
for dir in $withval; do
|
||||
thedir="$dir"
|
||||
if test -f "$dir/include/event.h"; then
|
||||
found_libevent="yes";
|
||||
CPPFLAGS="$CPPFLAGS -I$thedir/include";
|
||||
found_libevent="yes"
|
||||
CPPFLAGS="$CPPFLAGS -I$thedir/include"
|
||||
break;
|
||||
fi
|
||||
done
|
||||
if test x_$found_libevent != x_yes; then
|
||||
if test -f "$dir/event.h" -a -f "$dir/.libs/libevent.a"; then
|
||||
if test -f "$dir/event.h" -a -f "$dir/libevent.la"; then
|
||||
# libevent source directory
|
||||
AC_MSG_RESULT(found in $thedir)
|
||||
CPPFLAGS="$CPPFLAGS -I$thedir";
|
||||
LDFLAGS="$thedir/.libs/*.o $LDFLAGS";
|
||||
CPPFLAGS="$CPPFLAGS -I$thedir"
|
||||
BAK_LDFLAGS="$LDFLAGS"
|
||||
# remove evdns from linking
|
||||
mkdir build >/dev/null 2>&1
|
||||
mkdir build/libevent >/dev/null 2>&1
|
||||
mkdir build/libevent/.libs >/dev/null 2>&1
|
||||
ev_files_o=`ls $thedir/*.o | grep -v evdns\.o`
|
||||
ev_files_lo=`ls $thedir/*.lo | grep -v evdns\.lo`
|
||||
ev_files_libso=`ls $thedir/.libs/*.o | grep -v evdns\.o`
|
||||
cp $ev_files_o build/libevent
|
||||
cp $ev_files_lo build/libevent
|
||||
cp $ev_files_libso build/libevent/.libs
|
||||
LATE_LDFLAGS="build/libevent/*.lo"
|
||||
LDFLAGS="build/libevent/*.o $LDFLAGS"
|
||||
else
|
||||
AC_MSG_ERROR([Cannot find the libevent library in $withval
|
||||
You can restart ./configure --with-libevent=no to use a builtin alternative.
|
||||
@@ -509,7 +547,7 @@ many outgoing ports. ])
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(found in $thedir)
|
||||
LDFLAGS="$LDFLAGS -L$thedir/lib";
|
||||
LDFLAGS="$LDFLAGS -L$thedir/lib"
|
||||
if test "x$enable_rpath" = xyes; then
|
||||
RUNTIME_PATH="$RUNTIME_PATH -R$thedir/lib"
|
||||
fi
|
||||
@@ -519,6 +557,9 @@ many outgoing ports. ])
|
||||
AC_CHECK_HEADERS([event.h],,, [AC_INCLUDES_DEFAULT])
|
||||
AC_CHECK_FUNCS([event_base_free]) # only in libevent 1.2 and later
|
||||
AC_CHECK_FUNCS([event_base_once]) # only in libevent 1.4? and later
|
||||
if test -n "$BAK_LDFLAGS"; then
|
||||
LDFLAGS="$BAK_LDFLAGS"
|
||||
fi
|
||||
else
|
||||
AC_DEFINE(USE_MINI_EVENT, 1, [Define if you want to use internal select based events])
|
||||
fi
|
||||
@@ -551,10 +592,6 @@ else
|
||||
AC_FUNC_MALLOC
|
||||
fi
|
||||
|
||||
# check to see if libraries are needed for these functions.
|
||||
AC_CHECK_LIB(socket, socket)
|
||||
AC_CHECK_LIB(nsl, inet_pton)
|
||||
|
||||
AC_FUNC_CHOWN
|
||||
AC_FUNC_FORK
|
||||
AC_TYPE_SIGNAL
|
||||
@@ -574,6 +611,7 @@ AC_ARG_WITH(ldns,
|
||||
|
||||
AC_CHECK_LIB(ldns, ldns_rr_new, [
|
||||
dnl ldns was found, check compat functions
|
||||
AC_CHECK_LIB(ldns, [ldns_rr_free])
|
||||
AC_REPLACE_FUNCS(inet_aton)
|
||||
AC_REPLACE_FUNCS(inet_pton)
|
||||
AC_REPLACE_FUNCS(inet_ntop)
|
||||
@@ -605,7 +643,7 @@ echo "(cd $ldnsdir; ./configure)"
|
||||
(cd $ldnsdir; ./configure)
|
||||
AC_MSG_NOTICE([Finished $ldnsdir configure])
|
||||
CPPFLAGS="$CPPFLAGS -I$ldnsdir/include"
|
||||
LDFLAGS="$LDFLAGS $ldnsdir/lib/*.o"
|
||||
LDFLAGS="$LDFLAGS $ldnsdir/*.lo"
|
||||
AC_SUBST(ldnsdir)
|
||||
dnl use ldns compat functions
|
||||
AC_DEFINE(HAVE_INET_ATON)
|
||||
@@ -616,10 +654,15 @@ AC_DEFINE(HAVE_STRLCPY)
|
||||
AC_DEFINE(HAVE_MEMMOVE)
|
||||
AC_DEFINE(HAVE_GETADDRINFO)
|
||||
])
|
||||
LDFLAGS="$LATE_LDFLAGS $LDFLAGS"
|
||||
|
||||
AC_DEFINE_UNQUOTED([MAXSYSLOGMSGLEN], [5120], [Define to the maximum message length to pass to syslog.])
|
||||
|
||||
AH_BOTTOM([
|
||||
#ifndef UNBOUND_DEBUG
|
||||
# define NDEBUG
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@@ -664,6 +707,10 @@ AH_BOTTOM([
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENSSL_RAND_H
|
||||
#include <openssl/rand.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ATTR_FORMAT
|
||||
# define ATTR_FORMAT(archetype, string_index, first_to_check) \
|
||||
__attribute__ ((format (archetype, string_index, first_to_check)))
|
||||
|
||||
+3
-1
@@ -104,7 +104,7 @@ acl_list_insert(struct acl_list* acl, struct sockaddr_storage* addr,
|
||||
node->control = control;
|
||||
if(!rbtree_insert(acl->tree, &node->node)) {
|
||||
if(complain_duplicates)
|
||||
verbose(VERB_DETAIL, "duplicate acl address ignored.");
|
||||
verbose(VERB_QUERY, "duplicate acl address ignored.");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -200,6 +200,8 @@ acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg)
|
||||
return 0;
|
||||
if(!acl_list_str_cfg(acl, "::1", "allow", 0))
|
||||
return 0;
|
||||
if(!acl_list_str_cfg(acl, "::ffff:127.0.0.1", "allow", 0))
|
||||
return 0;
|
||||
acl_list_init_parents(acl);
|
||||
return 1;
|
||||
}
|
||||
|
||||
+2
-2
@@ -79,14 +79,14 @@ struct acl_list {
|
||||
struct acl_addr {
|
||||
/** redblacktree node, key is this structure: addr and addrlen, net */
|
||||
rbnode_t node;
|
||||
/** parent node in acl tree that encompasses this entry */
|
||||
struct acl_addr* parent;
|
||||
/** address */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr */
|
||||
socklen_t addrlen;
|
||||
/** netblock size */
|
||||
int net;
|
||||
/** parent node in acl tree that encompasses this entry */
|
||||
struct acl_addr* parent;
|
||||
/** access control on this netblock */
|
||||
enum acl_access control;
|
||||
};
|
||||
|
||||
+8
-126
@@ -51,10 +51,8 @@
|
||||
#include "services/cache/rrset.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "services/localzone.h"
|
||||
#include "services/modstack.h"
|
||||
#include "util/module.h"
|
||||
#include "iterator/iterator.h"
|
||||
#include "validator/validator.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include <signal.h>
|
||||
|
||||
/** How many quit requests happened. */
|
||||
@@ -129,7 +127,7 @@ daemon_init()
|
||||
checklock_start();
|
||||
ERR_load_crypto_strings();
|
||||
daemon->need_to_exit = 0;
|
||||
daemon->num_modules = 0;
|
||||
modstack_init(&daemon->mods);
|
||||
if(!(daemon->env = (struct module_env*)calloc(1,
|
||||
sizeof(*daemon->env)))) {
|
||||
free(daemon);
|
||||
@@ -158,136 +156,19 @@ daemon_open_shared_ports(struct daemon* daemon)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** count number of modules (words) in the string */
|
||||
static int
|
||||
count_modules(const char* s)
|
||||
{
|
||||
int num = 0;
|
||||
if(!s)
|
||||
return 0;
|
||||
while(*s) {
|
||||
/* skip whitespace */
|
||||
while(*s && isspace((int)*s))
|
||||
s++;
|
||||
if(*s && !isspace((int)*s)) {
|
||||
/* skip identifier */
|
||||
num++;
|
||||
while(*s && !isspace((int)*s))
|
||||
s++;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get funcblock for module name
|
||||
* @param str: string with module name. Advanced to next value on success.
|
||||
* @return funcblock or NULL on error.
|
||||
*/
|
||||
static struct module_func_block*
|
||||
daemon_module_factory(const char** str)
|
||||
{
|
||||
/* these are the modules available */
|
||||
int num = 2;
|
||||
const char* names[] = {"iterator", "validator", NULL};
|
||||
struct module_func_block* (*fb[])(void) =
|
||||
{&iter_get_funcblock, &val_get_funcblock, NULL};
|
||||
|
||||
int i;
|
||||
const char* s = *str;
|
||||
while(*s && isspace((int)*s))
|
||||
s++;
|
||||
for(i=0; i<num; i++) {
|
||||
if(strncmp(names[i], s, strlen(names[i])) == 0) {
|
||||
s += strlen(names[i]);
|
||||
*str = s;
|
||||
return (*fb[i])();
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read config file module settings and set up the modfunc block
|
||||
* @param daemon: the daemon.
|
||||
* @return false on error
|
||||
*/
|
||||
static int
|
||||
daemon_config_modules(struct daemon* daemon)
|
||||
{
|
||||
const char* str = daemon->cfg->module_conf;
|
||||
int i;
|
||||
verbose(VERB_DETAIL, "module config: \"%s\"", str);
|
||||
daemon->num_modules = count_modules(str);
|
||||
if(daemon->num_modules == 0) {
|
||||
log_err("error: no modules specified");
|
||||
return 0;
|
||||
}
|
||||
if(daemon->num_modules > MAX_MODULE) {
|
||||
log_err("error: too many modules (%d max %d)",
|
||||
daemon->num_modules, MAX_MODULE);
|
||||
return 0;
|
||||
}
|
||||
daemon->modfunc = (struct module_func_block**)calloc((size_t)
|
||||
daemon->num_modules, sizeof(struct module_func_block*));
|
||||
if(!daemon->modfunc) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
for(i=0; i<daemon->num_modules; i++) {
|
||||
daemon->modfunc[i] = daemon_module_factory(&str);
|
||||
if(!daemon->modfunc[i]) {
|
||||
log_err("Unknown value for first module in: '%s'",
|
||||
str);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
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++) {
|
||||
log_assert(fptr_whitelist_mod_deinit(
|
||||
daemon->modfunc[i]->deinit));
|
||||
(*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.
|
||||
* Setup modules. setup module stack.
|
||||
* @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 */
|
||||
if(!daemon_config_modules(daemon)) {
|
||||
fatal_exit("failed to setup modules");
|
||||
}
|
||||
daemon->env->cfg = daemon->cfg;
|
||||
daemon->env->alloc = &daemon->superalloc;
|
||||
daemon->env->worker = NULL;
|
||||
daemon->env->need_to_validate = 0; /* set by module init below */
|
||||
for(i=0; i<daemon->num_modules; i++) {
|
||||
verbose(VERB_OPS, "init module %d: %s",
|
||||
i, daemon->modfunc[i]->name);
|
||||
log_assert(fptr_whitelist_mod_init(daemon->modfunc[i]->init));
|
||||
if(!(*daemon->modfunc[i]->init)(daemon->env, i)) {
|
||||
fatal_exit("module init for module %s failed",
|
||||
daemon->modfunc[i]->name);
|
||||
}
|
||||
if(!modstack_setup(&daemon->mods, daemon->cfg->module_conf,
|
||||
daemon->env)) {
|
||||
fatal_exit("failed to setup modules");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,7 +356,7 @@ daemon_delete(struct daemon* daemon)
|
||||
{
|
||||
if(!daemon)
|
||||
return;
|
||||
daemon_desetup_modules(daemon);
|
||||
modstack_desetup(&daemon->mods, daemon->env);
|
||||
listening_ports_free(daemon->ports);
|
||||
if(daemon->env) {
|
||||
slabhash_delete(daemon->env->msg_cache);
|
||||
@@ -494,5 +375,6 @@ daemon_delete(struct daemon* daemon)
|
||||
CRYPTO_cleanup_all_ex_data(); /* safe, no more threads right now */
|
||||
ERR_remove_state(0);
|
||||
ERR_free_strings();
|
||||
RAND_cleanup();
|
||||
checklock_stop();
|
||||
}
|
||||
|
||||
+3
-4
@@ -44,6 +44,7 @@
|
||||
|
||||
#include "util/locks.h"
|
||||
#include "util/alloc.h"
|
||||
#include "services/modstack.h"
|
||||
struct config_file;
|
||||
struct worker;
|
||||
struct listen_port;
|
||||
@@ -76,10 +77,8 @@ struct daemon {
|
||||
struct alloc_cache superalloc;
|
||||
/** 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;
|
||||
/** stack of module callbacks */
|
||||
struct module_stack mods;
|
||||
/** access control, which client IPs are allowed to connect */
|
||||
struct acl_list* acl;
|
||||
/** local authority zones */
|
||||
|
||||
+5
-3
@@ -46,7 +46,7 @@
|
||||
|
||||
void server_stats_init(struct server_stats* stats)
|
||||
{
|
||||
memset(stats, 0, sizeof(stats));
|
||||
memset(stats, 0, sizeof(*stats));
|
||||
}
|
||||
|
||||
void server_stats_querymiss(struct server_stats* stats, struct worker* worker)
|
||||
@@ -59,10 +59,12 @@ void server_stats_querymiss(struct server_stats* stats, struct worker* worker)
|
||||
|
||||
void server_stats_log(struct server_stats* stats, int threadnum)
|
||||
{
|
||||
log_info("server stats for thread %d: %u queries, %u from cache",
|
||||
log_info("server stats for thread %d: %u queries, "
|
||||
"%u answers from cache, %u recursions",
|
||||
threadnum, (unsigned)stats->num_queries,
|
||||
(unsigned)(stats->num_queries -
|
||||
stats->num_queries_missed_cache));
|
||||
stats->num_queries_missed_cache),
|
||||
(unsigned)stats->num_queries_missed_cache);
|
||||
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?
|
||||
|
||||
+5
-5
@@ -264,8 +264,8 @@ do_chroot(struct daemon* daemon, struct config_file* cfg, int debug_mode)
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
/* initialize, but not to 0 (root) */
|
||||
memset(&uid, -12, sizeof(uid));
|
||||
memset(&gid, -12, sizeof(gid));
|
||||
memset(&uid, 112, sizeof(uid));
|
||||
memset(&gid, 112, sizeof(gid));
|
||||
log_assert(cfg);
|
||||
|
||||
/* daemonize last to be able to print error to user */
|
||||
@@ -274,7 +274,7 @@ do_chroot(struct daemon* daemon, struct config_file* cfg, int debug_mode)
|
||||
fatal_exit("Could not chdir to %s: %s",
|
||||
cfg->directory, strerror(errno));
|
||||
}
|
||||
verbose(VERB_DETAIL, "chdir to %s", cfg->directory);
|
||||
verbose(VERB_QUERY, "chdir to %s", cfg->directory);
|
||||
}
|
||||
if(cfg->username && cfg->username[0]) {
|
||||
struct passwd *pwd;
|
||||
@@ -288,7 +288,7 @@ do_chroot(struct daemon* daemon, struct config_file* cfg, int debug_mode)
|
||||
if(chroot(cfg->chrootdir))
|
||||
fatal_exit("unable to chroot to %s: %s",
|
||||
cfg->chrootdir, strerror(errno));
|
||||
verbose(VERB_DETAIL, "chroot to %s", cfg->chrootdir);
|
||||
verbose(VERB_QUERY, "chroot to %s", cfg->chrootdir);
|
||||
}
|
||||
if(cfg->username && cfg->username[0]) {
|
||||
if(setgid(gid) != 0)
|
||||
@@ -297,7 +297,7 @@ do_chroot(struct daemon* daemon, struct config_file* cfg, int debug_mode)
|
||||
if(setuid(uid) != 0)
|
||||
fatal_exit("unable to set user id of %s: %s",
|
||||
cfg->username, strerror(errno));
|
||||
verbose(VERB_DETAIL, "drop user privileges, run as %s",
|
||||
verbose(VERB_QUERY, "drop user privileges, run as %s",
|
||||
cfg->username);
|
||||
}
|
||||
/* check old pid file before forking */
|
||||
|
||||
+124
-30
@@ -136,13 +136,13 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
|
||||
superac = alloc_get_mem(&worker->daemon->superalloc);
|
||||
iter = 0;
|
||||
val = 0;
|
||||
for(i=0; i<worker->env.mesh->num_modules; i++) {
|
||||
for(i=0; i<worker->env.mesh->mods.num; i++) {
|
||||
log_assert(fptr_whitelist_mod_get_mem(worker->env.mesh->
|
||||
modfunc[i]->get_mem));
|
||||
if(strcmp(worker->env.mesh->modfunc[i]->name, "validator")==0)
|
||||
val += (*worker->env.mesh->modfunc[i]->get_mem)
|
||||
mods.mod[i]->get_mem));
|
||||
if(strcmp(worker->env.mesh->mods.mod[i]->name, "validator")==0)
|
||||
val += (*worker->env.mesh->mods.mod[i]->get_mem)
|
||||
(&worker->env, i);
|
||||
else iter += (*worker->env.mesh->modfunc[i]->get_mem)
|
||||
else iter += (*worker->env.mesh->mods.mod[i]->get_mem)
|
||||
(&worker->env, i);
|
||||
}
|
||||
me = sizeof(*worker) + sizeof(*worker->base) + sizeof(*worker->comsig)
|
||||
@@ -163,6 +163,23 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
|
||||
(unsigned)infra, (unsigned)iter, (unsigned)val, (unsigned)ac,
|
||||
(unsigned)superac, (unsigned)me);
|
||||
debug_total_mem(total);
|
||||
#else /* no UNBOUND_ALLOC_STATS */
|
||||
size_t val = 0;
|
||||
int i;
|
||||
if(verbosity < VERB_QUERY)
|
||||
return;
|
||||
for(i=0; i<worker->env.mesh->mods.num; i++) {
|
||||
log_assert(fptr_whitelist_mod_get_mem(worker->env.mesh->
|
||||
mods.mod[i]->get_mem));
|
||||
if(strcmp(worker->env.mesh->mods.mod[i]->name, "validator")==0)
|
||||
val += (*worker->env.mesh->mods.mod[i]->get_mem)
|
||||
(&worker->env, i);
|
||||
}
|
||||
verbose(VERB_QUERY, "cache memory msg=%u rrset=%u infra=%u val=%u",
|
||||
(unsigned)slabhash_get_mem(worker->env.msg_cache),
|
||||
(unsigned)slabhash_get_mem(&worker->env.rrset_cache->table),
|
||||
(unsigned)infra_get_mem(worker->env.infra_cache),
|
||||
(unsigned)val);
|
||||
#endif /* UNBOUND_ALLOC_STATS */
|
||||
}
|
||||
|
||||
@@ -250,40 +267,45 @@ static int
|
||||
worker_check_request(ldns_buffer* pkt, struct worker* worker)
|
||||
{
|
||||
if(ldns_buffer_limit(pkt) < LDNS_HEADER_SIZE) {
|
||||
verbose(VERB_DETAIL, "request too short, discarded");
|
||||
verbose(VERB_QUERY, "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");
|
||||
verbose(VERB_QUERY, "request too large, discarded");
|
||||
return -1;
|
||||
}
|
||||
if(LDNS_QR_WIRE(ldns_buffer_begin(pkt))) {
|
||||
verbose(VERB_DETAIL, "request has QR bit on, discarded");
|
||||
verbose(VERB_QUERY, "request has QR bit on, discarded");
|
||||
return -1;
|
||||
}
|
||||
if(LDNS_TC_WIRE(ldns_buffer_begin(pkt))) {
|
||||
LDNS_TC_CLR(ldns_buffer_begin(pkt));
|
||||
verbose(VERB_QUERY, "request bad, has TC bit on");
|
||||
return LDNS_RCODE_FORMERR;
|
||||
}
|
||||
if(LDNS_OPCODE_WIRE(ldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY) {
|
||||
verbose(VERB_DETAIL, "request unknown opcode %d",
|
||||
verbose(VERB_QUERY, "request unknown opcode %d",
|
||||
LDNS_OPCODE_WIRE(ldns_buffer_begin(pkt)));
|
||||
return LDNS_RCODE_NOTIMPL;
|
||||
}
|
||||
if(LDNS_QDCOUNT(ldns_buffer_begin(pkt)) != 1) {
|
||||
verbose(VERB_DETAIL, "request wrong nr qd=%d",
|
||||
verbose(VERB_QUERY, "request wrong nr qd=%d",
|
||||
LDNS_QDCOUNT(ldns_buffer_begin(pkt)));
|
||||
return LDNS_RCODE_FORMERR;
|
||||
}
|
||||
if(LDNS_ANCOUNT(ldns_buffer_begin(pkt)) != 0) {
|
||||
verbose(VERB_DETAIL, "request wrong nr an=%d",
|
||||
verbose(VERB_QUERY, "request wrong nr an=%d",
|
||||
LDNS_ANCOUNT(ldns_buffer_begin(pkt)));
|
||||
return LDNS_RCODE_FORMERR;
|
||||
}
|
||||
if(LDNS_NSCOUNT(ldns_buffer_begin(pkt)) != 0) {
|
||||
verbose(VERB_DETAIL, "request wrong nr ns=%d",
|
||||
verbose(VERB_QUERY, "request wrong nr ns=%d",
|
||||
LDNS_NSCOUNT(ldns_buffer_begin(pkt)));
|
||||
return LDNS_RCODE_FORMERR;
|
||||
}
|
||||
if(LDNS_ARCOUNT(ldns_buffer_begin(pkt)) > 1) {
|
||||
verbose(VERB_DETAIL, "request wrong nr ar=%d",
|
||||
verbose(VERB_QUERY, "request wrong nr ar=%d",
|
||||
LDNS_ARCOUNT(ldns_buffer_begin(pkt)));
|
||||
return LDNS_RCODE_FORMERR;
|
||||
}
|
||||
@@ -684,6 +706,8 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
LDNS_QR_SET(ldns_buffer_begin(c->buffer));
|
||||
LDNS_RCODE_SET(ldns_buffer_begin(c->buffer),
|
||||
LDNS_RCODE_REFUSED);
|
||||
log_addr(VERB_ALGO, "refused query from",
|
||||
&repinfo->addr, repinfo->addrlen);
|
||||
log_buf(VERB_ALGO, "refuse", c->buffer);
|
||||
return 1;
|
||||
}
|
||||
@@ -734,7 +758,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
}
|
||||
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",
|
||||
verbose(VERB_QUERY, "worker request: EDNS bufsize %d ignored",
|
||||
(int)edns.udp_size);
|
||||
edns.udp_size = NORMAL_UDP_SIZE;
|
||||
}
|
||||
@@ -811,21 +835,21 @@ worker_sighandler(int sig, void* arg)
|
||||
struct worker* worker = (struct worker*)arg;
|
||||
switch(sig) {
|
||||
case SIGHUP:
|
||||
verbose(VERB_DETAIL, "caught signal SIGHUP");
|
||||
verbose(VERB_QUERY, "caught signal SIGHUP");
|
||||
comm_base_exit(worker->base);
|
||||
break;
|
||||
case SIGINT:
|
||||
verbose(VERB_DETAIL, "caught signal SIGINT");
|
||||
verbose(VERB_QUERY, "caught signal SIGINT");
|
||||
worker->need_to_exit = 1;
|
||||
comm_base_exit(worker->base);
|
||||
break;
|
||||
case SIGQUIT:
|
||||
verbose(VERB_DETAIL, "caught signal SIGQUIT");
|
||||
verbose(VERB_QUERY, "caught signal SIGQUIT");
|
||||
worker->need_to_exit = 1;
|
||||
comm_base_exit(worker->base);
|
||||
break;
|
||||
case SIGTERM:
|
||||
verbose(VERB_DETAIL, "caught signal SIGTERM");
|
||||
verbose(VERB_QUERY, "caught signal SIGTERM");
|
||||
worker->need_to_exit = 1;
|
||||
comm_base_exit(worker->base);
|
||||
break;
|
||||
@@ -835,6 +859,30 @@ worker_sighandler(int sig, void* arg)
|
||||
}
|
||||
}
|
||||
|
||||
/** restart statistics timer for worker, if enabled */
|
||||
static void
|
||||
worker_restart_timer(struct worker* worker)
|
||||
{
|
||||
if(worker->env.cfg->stat_interval > 0) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = worker->env.cfg->stat_interval;
|
||||
tv.tv_usec = 0;
|
||||
comm_timer_set(worker->stat_timer, &tv);
|
||||
}
|
||||
}
|
||||
|
||||
void worker_stat_timer_cb(void* arg)
|
||||
{
|
||||
struct worker* worker = (struct worker*)arg;
|
||||
server_stats_log(&worker->stats, worker->thread_num);
|
||||
mesh_stats(worker->env.mesh, "mesh has");
|
||||
worker_mem_report(worker, NULL);
|
||||
server_stats_init(&worker->stats);
|
||||
mesh_stats_clear(worker->env.mesh);
|
||||
/* start next timer */
|
||||
worker_restart_timer(worker);
|
||||
}
|
||||
|
||||
struct worker*
|
||||
worker_create(struct daemon* daemon, int id)
|
||||
{
|
||||
@@ -899,17 +947,10 @@ 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)) {
|
||||
if(!(worker->rndstate = ub_initstate(seed, NULL))) {
|
||||
seed = 0;
|
||||
log_err("could not init random numbers.");
|
||||
worker_delete(worker);
|
||||
@@ -946,6 +987,12 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
worker->stat_timer = comm_timer_create(worker->base,
|
||||
worker_stat_timer_cb, worker);
|
||||
if(!worker->stat_timer) {
|
||||
log_err("could not create statistics timer");
|
||||
}
|
||||
|
||||
/* we use the msg_buffer_size as a good estimate for what the
|
||||
* user wants for memory usage sizes */
|
||||
worker->scratchpad = regional_create_custom(cfg->msg_buffer_size);
|
||||
@@ -967,8 +1014,7 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
||||
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.mesh = mesh_create(&worker->daemon->mods, &worker->env);
|
||||
worker->env.detach_subs = &mesh_detach_subs;
|
||||
worker->env.attach_sub = &mesh_attach_sub;
|
||||
worker->env.kill_sub = &mesh_state_delete;
|
||||
@@ -979,6 +1025,12 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
||||
return 0;
|
||||
}
|
||||
worker_mem_report(worker, NULL);
|
||||
/* if statistics enabled start timer */
|
||||
if(worker->env.cfg->stat_interval > 0) {
|
||||
verbose(VERB_ALGO, "set statistics interval %d secs",
|
||||
worker->env.cfg->stat_interval);
|
||||
worker_restart_timer(worker);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -994,8 +1046,8 @@ worker_delete(struct worker* worker)
|
||||
if(!worker)
|
||||
return;
|
||||
if(worker->env.mesh && verbosity >= VERB_OPS) {
|
||||
mesh_stats(worker->env.mesh, "mesh has");
|
||||
server_stats_log(&worker->stats, worker->thread_num);
|
||||
mesh_stats(worker->env.mesh, "mesh has");
|
||||
worker_mem_report(worker, NULL);
|
||||
}
|
||||
mesh_delete(worker->env.mesh);
|
||||
@@ -1004,9 +1056,9 @@ worker_delete(struct worker* worker)
|
||||
outside_network_delete(worker->back);
|
||||
comm_signal_delete(worker->comsig);
|
||||
comm_point_delete(worker->cmd_com);
|
||||
comm_timer_delete(worker->stat_timer);
|
||||
comm_base_delete(worker->base);
|
||||
ub_randfree(worker->rndstate);
|
||||
free(worker->rndstate);
|
||||
/* close fds after deleting commpoints, to be sure.
|
||||
Also epoll does not like closing fd before event_del */
|
||||
if(worker->cmd_send_fd != -1)
|
||||
@@ -1075,3 +1127,45 @@ worker_alloc_cleanup(void* arg)
|
||||
slabhash_clear(&worker->env.rrset_cache->table);
|
||||
slabhash_clear(worker->env.msg_cache);
|
||||
}
|
||||
|
||||
/* --- fake callbacks for fptr_wlist to work --- */
|
||||
int libworker_send_packet(ldns_buffer* ATTR_UNUSED(pkt),
|
||||
struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
socklen_t ATTR_UNUSED(addrlen), int ATTR_UNUSED(timeout),
|
||||
struct module_qstate* ATTR_UNUSED(q), int ATTR_UNUSED(use_tcp))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct outbound_entry* libworker_send_query(uint8_t* ATTR_UNUSED(qname),
|
||||
size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
|
||||
uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
|
||||
int ATTR_UNUSED(dnssec), struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
socklen_t ATTR_UNUSED(addrlen), struct module_qstate* ATTR_UNUSED(q))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libworker_handle_reply(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libworker_handle_service_reply(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int context_query_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+5
-3
@@ -59,9 +59,6 @@ struct listen_port;
|
||||
struct ub_randstate;
|
||||
struct regional;
|
||||
|
||||
/** size of table used for random numbers. large to be more secure. */
|
||||
#define RND_STATE_SIZE 256
|
||||
|
||||
/** worker commands */
|
||||
enum worker_commands {
|
||||
/** make the worker quit */
|
||||
@@ -93,6 +90,8 @@ struct worker {
|
||||
struct comm_signal* comsig;
|
||||
/** commpoint to listen to commands. */
|
||||
struct comm_point* cmd_com;
|
||||
/** timer for statistics */
|
||||
struct comm_timer* stat_timer;
|
||||
|
||||
/** number of requests that can be handled by this worker */
|
||||
size_t request_size;
|
||||
@@ -217,4 +216,7 @@ int worker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
||||
/** cleanup the cache to remove all rrset IDs from it, arg is worker */
|
||||
void worker_alloc_cleanup(void* arg);
|
||||
|
||||
/** statistics timer callback handler */
|
||||
void worker_stat_timer_cb(void* arg);
|
||||
|
||||
#endif /* DAEMON_WORKER_H */
|
||||
|
||||
@@ -7,3 +7,6 @@ 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.
|
||||
|
||||
Jakob Schlyter - for advice on secure settings, random numbers and blacklists.
|
||||
|
||||
Ondřej Surý - running coverity analysis tool on 0.9 dev version.
|
||||
Alexander Gall - multihomed, anycast testing of unbound resolver server.
|
||||
|
||||
+246
@@ -1,3 +1,248 @@
|
||||
11 February 2008: Wouter
|
||||
- nicer statistic output (from trunk).
|
||||
- ub_val to ub_ for library API (from trunk).
|
||||
- (from trunk) set_option, set_debugout, etc/hosts handling.
|
||||
|
||||
8 February 2008: Wouter
|
||||
- test program for multiple queries over a TCP channel.
|
||||
- tpkg test for stream tcp queries.
|
||||
- unbound replies to multiple TCP queries on a TCP channel.
|
||||
- fixup misclassification of root referral with NS in answer
|
||||
when validating a nonrec query.
|
||||
- tag 0.9
|
||||
|
||||
7 February 2008: Wouter
|
||||
- moved up all current level 2 to be level 3. And 3 to 4.
|
||||
to make room for new debug level 2 for detailed information
|
||||
for operators.
|
||||
- verbosity level 2. Describes recursion and validation.
|
||||
- cleaner configure script and fixes for libevent solaris.
|
||||
- signedness for log output memory sizes in high verbosity.
|
||||
|
||||
6 February 2008: Wouter
|
||||
- clearer explanation of threading configure options.
|
||||
- fixup asynclook test for nothreading (it creates only one process
|
||||
to do the extended test).
|
||||
- changed name of ub_val_result_free to ub_val_resolve_free.
|
||||
- removes warning message during library linking, renamed
|
||||
libunbound/unbound.c -> libunbound.c and worker to libworker.
|
||||
- fallback without EDNS if result is NOTIMPL as well as on FORMERR.
|
||||
|
||||
5 February 2008: Wouter
|
||||
- statistics-interval: seconds option added.
|
||||
- test for statistics option
|
||||
- ignore errors making directories, these can occur in parallel builds
|
||||
- fixup Makefile strip command and libunbound docs typo.
|
||||
|
||||
31 January 2008: Wouter
|
||||
- bg thread/process reads and writes the pipe nonblocking all the time
|
||||
so that even if the pipe is buffered or so, the bg thread does not
|
||||
block, and services both pipes and queries.
|
||||
|
||||
30 January 2008: Wouter
|
||||
- check trailing / on chrootdir in checkconf.
|
||||
- check if root hints and anchor files are in chrootdir.
|
||||
- no route to host tcp error is verbosity level 2.
|
||||
- removed unused send_reply_iov. and its configure check.
|
||||
- added prints of 'remote address is 1.2.3.4 port 53' to errors
|
||||
from netevent; the basic socket errors.
|
||||
|
||||
28 January 2008: Wouter
|
||||
- fixup uninit use of buffer by libunbound (query id, flags) for
|
||||
local_zone answers.
|
||||
- fixup uninit warning from random.c; also seems to fix sporadic
|
||||
sigFPE coming out of openssl.
|
||||
- made openssl entropy warning more silent for library use. Needs
|
||||
verbosity 1 now.
|
||||
- fixup forgotten locks for rbtree_searches on ctx->query tree.
|
||||
- random generator cleanup - RND_STATE_SIZE removed, and instead
|
||||
a super-rnd can be passed at init to chain init random states.
|
||||
- test also does lock checks if available.
|
||||
- protect config access in libworker_setup().
|
||||
- libevent doesn't like comm_base_exit outside of runloop.
|
||||
- close fds after removing commpoints only (for epoll, kqueue).
|
||||
|
||||
25 January 2008: Wouter
|
||||
- added tpkg for asynclook and library use.
|
||||
- allows localhost to be queried when as a library.
|
||||
- fixup race condition between cancel and answer (in case of
|
||||
really fast answers that beat the cancel).
|
||||
- please doxygen, put doxygen comment in one place.
|
||||
- asynclook -b blocking mode and test.
|
||||
- refactor asynclook, nicer code.
|
||||
- fixup race problems from opensll in rand init from library, with
|
||||
a mutex around the rand init.
|
||||
- fix pass async_id=NULL to _async resolve().
|
||||
- rewrote _wait() routine, so that it is threadsafe.
|
||||
- cancelation is threadsafe.
|
||||
- asynclook extended test in tpkg.
|
||||
- fixed two races where forked bg process waits for (somehow shared?)
|
||||
locks, so does not service the query pipe on the bg side.
|
||||
Now those locks are only held for fg_threads and for bg_as_a_thread.
|
||||
|
||||
24 January 2008: Wouter
|
||||
- tested the cancel() function.
|
||||
- asynclook -c (cancel) feature.
|
||||
- fix fail to allocate context actions.
|
||||
- make pipe nonblocking at start.
|
||||
- update plane for retry mode with caution to limit bandwidth.
|
||||
- fix Makefile for concurrent make of unbound-host.
|
||||
- renamed ub_val_ctx_wait/poll/process/fd to ub_val*.
|
||||
- new calls to set forwarding added to header and docs.
|
||||
|
||||
23 January 2008: Wouter
|
||||
- removed debug prints from if-auto, verb-algo enables some.
|
||||
- libunbound QUIT setup, remove memory leaks, when using threads
|
||||
will share memory for passing results instead of writing it over
|
||||
the pipe, only writes ID number over the pipe (towards the handler
|
||||
thread that does process() ).
|
||||
|
||||
22 January 2008: Wouter
|
||||
- library code for async in libunbound/unbound.c.
|
||||
- fix link testbound.
|
||||
- fixup exit bug in mini_event.
|
||||
- background worker query enter and result functions.
|
||||
- bg query test application asynclook, it looks up multiple
|
||||
hostaddresses (A records) at the same time.
|
||||
|
||||
21 January 2008: Wouter
|
||||
- libworker work, netevent raw commpoints, write_msg, serialize.
|
||||
|
||||
18 January 2008: Wouter
|
||||
- touch up of manpage for libunbound.
|
||||
- support for IP_RECVDSTADDR (for *BSD ip4).
|
||||
- fix for BSD, do not use ip4to6 mapping, make two sockets, once
|
||||
ip6 and once ip4, uses socket options.
|
||||
- goodbye ip4to6 mapping.
|
||||
- update ldns-testpkts with latest version from ldns-trunk.
|
||||
- updated makedist for relative ldns pathnames.
|
||||
- library API with more information inside the result structure.
|
||||
- work on background resolves.
|
||||
|
||||
17 January 2008: Wouter
|
||||
- fixup configure in case -lldns is installed.
|
||||
- fixup a couple of doxygen warnings, about enum variables.
|
||||
- interface-automatic now copies the interface address from the
|
||||
PKT_INFO structure as well.
|
||||
- manual page with library API, all on one page 'man libunbound'.
|
||||
- rewrite of PKTINFO structure, it also captures IP4 PKTINFO.
|
||||
|
||||
16 January 2008: Wouter
|
||||
- incoming queries to the server with TC bit on are replied FORMERR.
|
||||
- interface-automatic replied the wrong source address on localhost
|
||||
queries. Seems to be due to ifnum=0 in recvmsg PKTINFO. Trying
|
||||
to use ifnum=-1 to mean 'no interface, use kernel route'.
|
||||
|
||||
15 January 2008: Wouter
|
||||
- interface-automatic feature. experimental. Nice for anycast.
|
||||
- tpkg test for ip6 ancillary data.
|
||||
- removed debug prints.
|
||||
- porting experience, define for Solaris, test refined for BSD
|
||||
compatibility. The feature probably will not work on OpenBSD.
|
||||
- makedist fixup for ldns-src in build-dir.
|
||||
|
||||
14 January 2008: Wouter
|
||||
- in no debug sets NDEBUG to remove asserts.
|
||||
- configure --enable-debug is needed for dependency generation
|
||||
for assertions and for compiler warnings.
|
||||
- ldns.tgz updated with ldns-trunk (where buffer.h is updated).
|
||||
- fix lint, unit test in optimize mode.
|
||||
- default access control allows ::ffff:127.0.0.1 v6mapped localhost.
|
||||
|
||||
11 January 2008: Wouter
|
||||
- man page, warning removed.
|
||||
- added text describing the use of stub zones for private zones.
|
||||
- checkconf tests for bad hostnames (IP address), and for doubled
|
||||
interface lines.
|
||||
- memory sizes can be given with 'k', 'Kb', or M or G appended.
|
||||
|
||||
10 January 2008: Wouter
|
||||
- typo in example.conf.
|
||||
- made using ldns-src that is included the package more portable
|
||||
by linking with .lo instead of .o files in the ldns package.
|
||||
- nicer do-ip6: yes/no documentation.
|
||||
- nicer linking of libevent .o files.
|
||||
- man pages render correctly on solaris.
|
||||
|
||||
9 January 2008: Wouter
|
||||
- fixup openssl RAND problem, when the system is not configured to
|
||||
give entropy, and the rng needs to be seeded.
|
||||
|
||||
8 January 2008: Wouter
|
||||
- print median and quartiles with extensive logging.
|
||||
|
||||
4 January 2008: Wouter
|
||||
- document misconfiguration in private network.
|
||||
|
||||
2 January 2008: Wouter
|
||||
- fixup typo in requirements.
|
||||
- document that 'refused' is a better choice than 'drop' for
|
||||
the access control list, as refused will stop retries.
|
||||
|
||||
7 December 2007: Wouter
|
||||
- unbound-host has a -d option to show what happens. This can help
|
||||
with debugging (why do I get this answer).
|
||||
- fixup CNAME handling, on nodata, sets and display canonname.
|
||||
- dot removed from CNAME display.
|
||||
- respect -v for NXDOMAINs.
|
||||
- updated ldns-src.tar.gz with ldns-trunk today (1.2.2 fixes).
|
||||
- size_t to int for portability of the header file.
|
||||
- fixup bogus handling.
|
||||
- dependencies and lint for unbound-host.
|
||||
|
||||
6 December 2007: Wouter
|
||||
- library resolution works in foreground mode, unbound-host app
|
||||
receives data.
|
||||
- unbound-host prints rdata using ldns.
|
||||
- unbound-host accepts trust anchors, and prints validation
|
||||
information when you give -v.
|
||||
|
||||
5 December 2007: Wouter
|
||||
- locking in context_new() inside the function.
|
||||
- setup of libworker.
|
||||
|
||||
4 December 2007: Wouter
|
||||
- minor Makefile fixup.
|
||||
- moved module-stack code out of daemon/daemon into services/modstack,
|
||||
preparing for code-reuse.
|
||||
- move context into own header file.
|
||||
- context query structure.
|
||||
- removed unused variable pwd from checkconf.
|
||||
- removed unused assignment from outside netw.
|
||||
- check timeval length of string.
|
||||
- fixup error in val_utils getsigner.
|
||||
- fixup same (*var) error in netblocktostr.
|
||||
- fixup memleak on parse error in localzone.
|
||||
- fixup memleak on packet parse error.
|
||||
- put ; after union in parser.y.
|
||||
- small hardening in iter_operate against iq==NULL.
|
||||
- hardening, if error reply with rcode=0 (noerror) send servfail.
|
||||
- fixup same (*var) error in find_rrset in msgparse, was harmless.
|
||||
- check return value of evtimer_add().
|
||||
- fixup lockorder in lruhash_reclaim(), building up a list of locked
|
||||
entries one at a time. Instead they are removed and unlocked.
|
||||
- fptr_wlist for markdelfunc.
|
||||
- removed is_locked param from lruhash delkeyfunc.
|
||||
- moved bin_unlock during bin_split purely to please.
|
||||
|
||||
3 December 2007: Wouter
|
||||
- changed checkconf/ to smallapp/ to make room for more support tools.
|
||||
(such as unbound-host).
|
||||
- install dirs created with -m 755 because they need to be accessible.
|
||||
- library extensive featurelist added to TODO.
|
||||
- please doxygen, lint.
|
||||
- library test application, with basic functionality.
|
||||
- fix for building in a subdirectory.
|
||||
- link lib fix for Leopard.
|
||||
|
||||
30 November 2007: Wouter
|
||||
- makefile that creates libunbound.la, basic file or libunbound.a
|
||||
when creating static executables (no libtool).
|
||||
- more API setup.
|
||||
|
||||
29 November 2007: Wouter
|
||||
- 0.9 public API start.
|
||||
|
||||
28 November 2007: Wouter
|
||||
- Changeup plan for 0.8 - no complication needed, a simple solution
|
||||
has been chosen for authoritative features.
|
||||
@@ -18,6 +263,7 @@
|
||||
have a SOA record in authority, so this is OK for the validator),
|
||||
and NS record is needed to be a referral.
|
||||
- duplicate checking when adding NSECs for a CNAME, and test.
|
||||
- created svn tag 0.8, after completing testbed tests.
|
||||
|
||||
27 November 2007: Wouter
|
||||
- per suggestion in rfc2308, replaced default max-ttl value with 1 day.
|
||||
|
||||
+17
-2
@@ -30,8 +30,9 @@ This software is under BSD license, see LICENSE for details.
|
||||
This improves randomization and spoof resistance. For the default
|
||||
of 16 ports the builtin alternative works well.
|
||||
* --without-pthreads
|
||||
This disables pthreads, and uses Solaris thr library or no threading.
|
||||
Without this option the pthreads library is detected automatically.
|
||||
This disables pthreads. Without this option the pthreads library
|
||||
is detected automatically. Use this option to disable threading
|
||||
altogether, or, on Solaris, also use --with(out)-solaris-threads.
|
||||
* --enable-static-exe
|
||||
This enables a debug option to statically link, against ldns and
|
||||
libevent libraries.
|
||||
@@ -53,6 +54,20 @@ o If libevent is older (before 1.3c), unbound will exit instead of reload
|
||||
on sighup. On a restart 'did not exit gracefully last time' warning is
|
||||
printed. Perform ./configure --with-libevent=no or update libevent, rerun
|
||||
configure and recompile unbound to make sighup work correctly.
|
||||
It is strongly suggested to use the latest version of libevent (1.4 or more)
|
||||
on Solaris, because of fixes in the solaris event port code in libevent.
|
||||
o If you receive no answers deploying unbound on a private network.
|
||||
Check that the zone you configure as a stub zone is correct, listing working
|
||||
ip-addresses. Check that the NS data in the zone, those hostnames, are
|
||||
in fact those same, working, ip-addresses. Unbound will fetch the NS data
|
||||
from the zone and use that information in preference to the config
|
||||
information.
|
||||
o If you are not receiving the correct source IP address on replies (e.g.
|
||||
you are running a multihomed, anycast server), the interface-automatic
|
||||
option can be enabled to set socket options to achieve the correct
|
||||
source IP address on UDP replies. Listing all IP addresses explicitly in
|
||||
the config file is an alternative. The interface-automatic option uses
|
||||
non portable socket options, Linux and FreeBSD should work fine.
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
TODO items.
|
||||
o use real entropy to make random (ID, port) numbers more random.
|
||||
o in production mode, do not free memory on exit. In debug mode, test leaks.
|
||||
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 speed up pkt domain name decompression loop detection using counter perhaps.
|
||||
@@ -11,16 +7,10 @@ o detect OS/400 pthreads implementation that allows upgrading to writelock
|
||||
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 (option) to not send replies to clients after a timeout of (say 5 secs) has
|
||||
@@ -28,21 +18,17 @@ o (option) to not send replies to clients after a timeout of (say 5 secs) has
|
||||
o private TTL feature
|
||||
o pretend-dnssec-unaware, and pretend-edns-unaware modes for debug/workshops.
|
||||
o delegpt use rbtree for ns-list, to avoid slowdown for very large NS sets.
|
||||
o be able to have different listen and query-to addresses to bind to,
|
||||
so you can listen to localhost and query-to to the internet.
|
||||
o reprime and refresh oft used data before timeout.
|
||||
o retain prime results in a overlaid roothints file.
|
||||
o store primed key data in a overlaid keyhints file (sort of like drafttimers).
|
||||
o windows version, auto update feature, a query to check for the version.
|
||||
o autoreport of problems
|
||||
o logrotation, syslog
|
||||
o command the server with TSIG inband. get-config, clearcache,
|
||||
get stats, get memstats, get ..., reload, clear one zone from cache
|
||||
o watch for spoof nearmisses.
|
||||
o watch for spoof nearmisses. Keep counter of nearmisses and print that
|
||||
in the stats lines, operator can determine what level is a redalert.
|
||||
o improve compression of DNS packets by first puttig uncompressible rrs, then
|
||||
compress to their rdata.
|
||||
o if one server is not responsive do not spend 75 secs on that server, but
|
||||
try other servers with lower rtt.
|
||||
o NSID rfc 5001 support.
|
||||
o timers rfc 5011 support.
|
||||
o Treat YXDOMAIN from a DNAME properly, in iterator (not throwaway), validator.
|
||||
@@ -54,7 +40,18 @@ o proactively prime root, stubs and trust anchors, feature.
|
||||
early failure, faster on first query, but more traffic.
|
||||
o use privilege separation, to change privilege options during reload securely
|
||||
not needed.
|
||||
o check if for PowerDNS(2.9.21) CNAME in Answer section & rcode=NXDOMAIN needs
|
||||
to be fixed up to be rcode=NOERROR?
|
||||
o On Windows use CryptGenRandom() to get random seed for arc4random.
|
||||
o Think about intermediate firewalls dropping EDNS UDP & handling that.
|
||||
detect nonEDNS middlebox by timeout on edns queries, and fallback to
|
||||
nonEDNS when appropriate.
|
||||
o library add convenience functions for A, AAAA, PTR, getaddrinfo, libresolve.
|
||||
o library add function to get signature data (or whole reply message).
|
||||
o library add function to validate input from app that is signed.
|
||||
o add dynamic-update requests (making a dynupd request) to libunbound api.
|
||||
o in an ipv6 connected only environment unbound cannot use outgoing IP6
|
||||
to send to ip4to6 mapped hosts, need ip4to6map of NS and disable
|
||||
V6ONLY socket option.
|
||||
o support multiple dns messages in a TCP query stream for the unbound server.
|
||||
o SIG(0) and TSIG.
|
||||
o support OPT record placement on recv anywhere in the additional section.
|
||||
o add local-file: config with authority features.
|
||||
|
||||
+17
-8
@@ -15,6 +15,10 @@ server:
|
||||
# verbosity number, 0 is least verbose. 1 is default.
|
||||
verbosity: 1
|
||||
|
||||
# print statistics to the log (for every thread) every N seconds.
|
||||
# Set to "" or 0 to disable. Default is disabled.
|
||||
# statistics-interval: 0
|
||||
|
||||
# number of threads to create. 1 disables threading.
|
||||
# num-threads: 1
|
||||
|
||||
@@ -26,6 +30,10 @@ server:
|
||||
# interface: 192.0.2.153
|
||||
# interface: 192.0.2.154
|
||||
# interface: 2001:DB8::5
|
||||
|
||||
# enable this feature to copy the source address of queries to reply.
|
||||
# Socket options not be supported on all platforms. experimental.
|
||||
# interface-automatic: no
|
||||
|
||||
# port to answer queries from
|
||||
# port: 53
|
||||
@@ -59,8 +67,8 @@ server:
|
||||
# msg-buffer-size: 65552
|
||||
|
||||
# the amount of memory to use for the message cache.
|
||||
# in bytes. default is 4 Mb
|
||||
# msg-cache-size: 4194304
|
||||
# plain value in bytes or you can append k, m or G. default is "4Mb".
|
||||
# msg-cache-size: 4m
|
||||
|
||||
# the number of slabs to use for the message cache.
|
||||
# the number of slabs must be a power of 2.
|
||||
@@ -71,8 +79,8 @@ server:
|
||||
# num-queries-per-thread: 1024
|
||||
|
||||
# the amount of memory to use for the RRset cache.
|
||||
# in bytes. default is 4 Mb
|
||||
# rrset-cache-size: 4194304
|
||||
# plain value in bytes or you can append k, m or G. default is "4Mb".
|
||||
# rrset-cache-size: 4m
|
||||
|
||||
# the number of slabs to use for the RRset cache.
|
||||
# the number of slabs must be a power of 2.
|
||||
@@ -99,7 +107,7 @@ server:
|
||||
# infra-cache-numhosts: 10000
|
||||
|
||||
# the maximum size of the lame zones cached per host. in bytes.
|
||||
# infra-cache-lame-size: 10240
|
||||
# infra-cache-lame-size: 10k
|
||||
|
||||
# Enable IPv4, "yes" or "no".
|
||||
# do-ip4: yes
|
||||
@@ -121,6 +129,7 @@ server:
|
||||
# access-control: 127.0.0.0/8 allow
|
||||
# access-control: ::0/0 refuse
|
||||
# access-control: ::1 allow
|
||||
# access-control: ::ffff:127.0.0.1 allow
|
||||
|
||||
# if given, a chroot(2) is done to the given directory.
|
||||
# i.e. you can chroot to the working directory, for example,
|
||||
@@ -130,7 +139,7 @@ server:
|
||||
|
||||
# if given, user privileges are dropped (after binding port),
|
||||
# and the given username is assumed. Default is user "unbound".
|
||||
# If you give "" no priviliges are dropped.
|
||||
# If you give "" no privileges are dropped.
|
||||
# username: "unbound"
|
||||
|
||||
# the working directory.
|
||||
@@ -247,8 +256,8 @@ server:
|
||||
# val-nsec3-keysize-iterations: "1024 150 2048 500 4096 2500"
|
||||
|
||||
# the amount of memory to use for the key cache.
|
||||
# in bytes. default is 4 Mb
|
||||
# key-cache-size: 4194304
|
||||
# plain value in bytes or you can append k, m or G. default is "4Mb".
|
||||
# key-cache-size: 4m
|
||||
|
||||
# the number of slabs to use for the key cache.
|
||||
# the number of slabs must be a power of 2.
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
.TH "libunbound" "3" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" libunbound.3 -- unbound library functions manual
|
||||
.\"
|
||||
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
.\"
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.SH "NAME"
|
||||
.LP
|
||||
.B libunbound,
|
||||
.B unbound.h,
|
||||
.B ub_ctx,
|
||||
.B ub_result,
|
||||
.B ub_callback_t,
|
||||
.B ub_ctx_create,
|
||||
.B ub_ctx_delete,
|
||||
.B ub_ctx_set_option,
|
||||
.B ub_ctx_config,
|
||||
.B ub_ctx_set_fwd,
|
||||
.B ub_ctx_resolvconf,
|
||||
.B ub_ctx_hosts,
|
||||
.B ub_ctx_add_ta,
|
||||
.B ub_ctx_add_ta_file,
|
||||
.B ub_ctx_trustedkeys,
|
||||
.B ub_ctx_debugout,
|
||||
.B ub_ctx_debuglevel,
|
||||
.B ub_ctx_async,
|
||||
.B ub_poll,
|
||||
.B ub_wait,
|
||||
.B ub_fd,
|
||||
.B ub_process,
|
||||
.B ub_resolve,
|
||||
.B ub_resolve_async,
|
||||
.B ub_cancel,
|
||||
.B ub_resolve_free,
|
||||
.B ub_strerror
|
||||
\- Unbound DNS validating resolver @version@ functions.
|
||||
.SH "SYNOPSIS"
|
||||
.LP
|
||||
.B #include <unbound.h>
|
||||
.LP
|
||||
\fIstruct ub_ctx *\fR
|
||||
\fBub_ctx_create\fR(\fIvoid\fR);
|
||||
.LP
|
||||
\fIvoid\fR
|
||||
\fBub_ctx_delete\fR(\fIstruct ub_ctx*\fR ctx);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_set_option\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR opt, \fIchar*\fR val);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_config\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_set_fwd\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR addr);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_resolvconf\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_hosts\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_add_ta\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR ta);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_add_ta_file\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_trustedkeys\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_debugout\fR(\fIstruct ub_ctx*\fR ctx, \fIFILE*\fR out);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_debuglevel\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR d);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_async\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR dothread);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_poll\fR(\fIstruct ub_ctx*\fR ctx);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_wait\fR(\fIstruct ub_ctx*\fR ctx);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_fd\fR(\fIstruct ub_ctx*\fR ctx);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_process\fR(\fIstruct ub_ctx*\fR ctx);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_resolve\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR name,
|
||||
.br
|
||||
\fIint\fR rrtype, \fIint\fR rrclass, \fIint*\fR secure,
|
||||
.br
|
||||
\fIint*\fR data, \fIstruct ub_result**\fR result);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_resolve_async\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR name,
|
||||
.br
|
||||
\fIint\fR rrtype, \fIint\fR rrclass, \fIvoid*\fR mydata,
|
||||
.br
|
||||
\fIub_callback_t\fR callback, \fIint*\fR async_id);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_cancel\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR async_id);
|
||||
.LP
|
||||
\fIvoid\fR
|
||||
\fBub_resolve_free\fR(\fIstruct ub_result*\fR result);
|
||||
.LP
|
||||
\fIconst char *\fR
|
||||
\fBub_strerror\fR(\fIint\fR err);
|
||||
.SH "DESCRIPTION"
|
||||
.LP
|
||||
.B Unbound
|
||||
is an implementation of a DNS resolver, that does caching and
|
||||
DNSSEC validation. This is the library API, for using the \-lunbound library.
|
||||
The server daemon is described in \fIunbound\fR(8).
|
||||
The library can be used to convert hostnames to ip addresses, and back,
|
||||
and obtain other information from the DNS. The library performs public\-key
|
||||
validation of results with DNSSEC.
|
||||
.P
|
||||
The library uses a variable of type \fIstruct ub_ctx\fR to keep context
|
||||
between calls. The user must maintain it, creating it with
|
||||
.B ub_ctx_create
|
||||
and deleting it with
|
||||
.B ub_ctx_delete\fR.
|
||||
It can be created and deleted at any time. Creating it anew removes any
|
||||
previous configuration (such as trusted keys) and clears any cached results.
|
||||
.P
|
||||
The functions are thread\-safe, and a context an be used in a threaded (as
|
||||
well as in a non\-threaded) environment. Also resolution (and validation)
|
||||
can be performed blocking and non\-blocking (also called asynchronous).
|
||||
The async method returns from the call immediately, so that processing
|
||||
can go on, while the results become available later.
|
||||
.P
|
||||
The functions are discussed in turn below.
|
||||
.SH "FUNCTIONS"
|
||||
.TP
|
||||
.B ub_ctx_create
|
||||
Create a new context, initialised with defaults.
|
||||
The information from /etc/resolv.conf and /etc/hosts is not utilised
|
||||
by default. Use
|
||||
.B ub_ctx_resolvconf
|
||||
and
|
||||
.B ub_ctx_hosts
|
||||
to read them.
|
||||
.TP
|
||||
.B ub_ctx_delete
|
||||
Delete validation context and free associated resources.
|
||||
Outstanding async queries are killed and callbacks are not called for them.
|
||||
.TP
|
||||
.B ub_ctx_set_option
|
||||
A power\-user interface that lets you specify one of the options from the
|
||||
config file format, see \fIunbound.conf\fR(5). Not all options are
|
||||
relevant. For some specific options, such as adding trust anchors, special
|
||||
routines exist. Pass the option name with the trailing ':'.
|
||||
.TP
|
||||
.B ub_ctx_config
|
||||
A power\-user interface that lets you specify an unbound config file, see
|
||||
\fIunbound.conf\fR(5), which is read for configuration. Not all options are
|
||||
relevant. For some specific options, such as adding trust anchors, special
|
||||
routines exist.
|
||||
.TP
|
||||
.B ub_ctx_set_fwd
|
||||
Set machine to forward DNS queries to, the caching resolver to use.
|
||||
IP4 or IP6 address. Forwards all DNS requests to that machine, which
|
||||
is expected to run a recursive resolver. If the proxy is not
|
||||
DNSSEC capable, validation may fail. Can be called several times, in
|
||||
that case the addresses are used as backup servers.
|
||||
At this time it is only possible to set configuration before the
|
||||
first resolve is done.
|
||||
.TP
|
||||
.B ub_ctx_resolvconf
|
||||
Read list of nameservers to use from the filename given.
|
||||
Usually "/etc/resolv.conf". Uses those nameservers as caching proxies.
|
||||
If they do not support DNSSEC, validation may fail.
|
||||
Only nameservers are picked up, the searchdomain, ndots and other
|
||||
settings from \fIresolv.conf\fR(5) are ignored.
|
||||
If fname NULL is passed, "/etc/resolv.conf" is used.
|
||||
At this time it is only possible to set configuration before the
|
||||
first resolve is done.
|
||||
.TP
|
||||
.B ub_ctx_hosts
|
||||
Read list of hosts from the filename given.
|
||||
Usually "/etc/hosts". When queried for, these addresses are not marked
|
||||
DNSSEC secure. If fname NULL is passed, "/etc/hosts" is used.
|
||||
At this time it is only possible to set configuration before the
|
||||
first resolve is done.
|
||||
.TP
|
||||
.B
|
||||
ub_ctx_add_ta
|
||||
Add a trust anchor to the given context.
|
||||
At this time it is only possible to add trusted keys before the
|
||||
first resolve is done.
|
||||
The format is a string, similar to the zone-file format,
|
||||
[domainname] [type] [rdata contents]. Both DS and DNSKEY records are accepted.
|
||||
.TP
|
||||
.B ub_ctx_add_ta_file
|
||||
Add trust anchors to the given context.
|
||||
Pass name of a file with DS and DNSKEY records in zone file format.
|
||||
At this time it is only possible to add trusted keys before the
|
||||
first resolve is done.
|
||||
.TP
|
||||
.B ub_ctx_trustedkeys
|
||||
Add trust anchors to the given context.
|
||||
Pass the name of a bind-style config file with trusted-keys{}.
|
||||
At this time it is only possible to add trusted keys before the
|
||||
first resolve is done.
|
||||
.TP
|
||||
.B ub_ctx_debugout
|
||||
Set debug and error log output to the given stream. Pass NULL to disable
|
||||
output. Default is stderr. File-names or using syslog can be enabled
|
||||
using config options, this routine is for using your own stream.
|
||||
.TP
|
||||
.B ub_ctx_debuglevel
|
||||
Set debug verbosity for the context. Output is directed to stderr.
|
||||
Higher debug level gives more output.
|
||||
.TP
|
||||
.B ub_ctx_async
|
||||
Set a context behaviour for asynchronous action.
|
||||
if set to true, enables threading and a call to
|
||||
.B ub_resolve_async
|
||||
creates a thread to handle work in the background.
|
||||
If false, a process is forked to handle work in the background.
|
||||
Changes to this setting after
|
||||
.B ub_resolve_async
|
||||
calls have been made have no effect (delete and re\-create the context
|
||||
to change).
|
||||
.TP
|
||||
.B ub_poll
|
||||
Poll a context to see if it has any new results.
|
||||
Do not poll in a loop, instead extract the fd below to poll for readiness,
|
||||
and then check, or wait using the wait routine.
|
||||
Returns 0 if nothing to read, or nonzero if a result is available.
|
||||
If nonzero, call
|
||||
.B ub_process
|
||||
to do callbacks.
|
||||
.TP
|
||||
.B ub_wait
|
||||
Wait for a context to finish with results. Calls
|
||||
.B ub_process
|
||||
after the wait for you. After the wait, there are no more outstanding
|
||||
asynchronous queries.
|
||||
.TP
|
||||
.B ub_fd
|
||||
Get file descriptor. Wait for it to become readable, at this point
|
||||
answers are returned from the asynchronous validating resolver.
|
||||
Then call the \fBub_process\fR to continue processing.
|
||||
.TP
|
||||
.B ub_process
|
||||
Call this routine to continue processing results from the validating
|
||||
resolver (when the fd becomes readable).
|
||||
Will perform necessary callbacks.
|
||||
.TP
|
||||
.B ub_resolve
|
||||
Perform resolution and validation of the target name.
|
||||
The name is a domain name in a zero terminated text string.
|
||||
The rrtype and rrclass are DNS type and class codes.
|
||||
The value secure returns true if the answer validated securely.
|
||||
The value data returns true if there was data.
|
||||
The result structure is newly allocated with the resulting data.
|
||||
.TP
|
||||
.B ub_resolve_async
|
||||
Perform asynchronous resolution and validation of the target name.
|
||||
Arguments mean the same as for \fBub_resolve\fR except no
|
||||
data is returned immediately, instead a callback is called later.
|
||||
The callback receives a copy of the mydata pointer, that you can use to pass
|
||||
information to the callback. The callback type is a function pointer to
|
||||
a function declared as
|
||||
.IP
|
||||
void my_callback_function(void* my_arg, int err,
|
||||
.br
|
||||
struct ub_result* result);
|
||||
.IP
|
||||
The async_id is returned so you can (at your option) decide to track it
|
||||
and cancel the request if needed.
|
||||
.TP
|
||||
.B ub_cancel
|
||||
Cancel an async query in progress.
|
||||
.TP
|
||||
.B ub_resolve_free
|
||||
Free struct ub_result contents after use.
|
||||
.TP
|
||||
.B ub_strerror
|
||||
Convert error value from one of the unbound library functions
|
||||
to a human readable string.
|
||||
.SH "RESULT DATA STRUCTURE"
|
||||
.LP
|
||||
The result of the DNS resolution and validation is returned as
|
||||
\fIstruct ub_result\fR. The result structure contains the following entries.
|
||||
.P
|
||||
.nf
|
||||
struct ub_result {
|
||||
char* qname; /* text string, original question */
|
||||
int qtype; /* type code asked for */
|
||||
int qclass; /* class code asked for */
|
||||
char** data; /* array of rdata items, NULL terminated*/
|
||||
int* len; /* array with lengths of rdata items */
|
||||
char* canonname; /* canonical name of result */
|
||||
int rcode; /* additional error code in case of no data */
|
||||
int havedata; /* true if there is data */
|
||||
int nxdomain; /* true if nodata because name does not exist */
|
||||
int secure; /* true if result is secure */
|
||||
int bogus; /* true if a security failure happened */
|
||||
};
|
||||
.fi
|
||||
.P
|
||||
If both secure and bogus are false, security was not enabled for the
|
||||
domain of the query.
|
||||
.SH "RETURN VALUES"
|
||||
Many routines return an error code. The value 0 (zero) denotes no error
|
||||
happened. Other values can be passed to
|
||||
.B ub_strerror
|
||||
to obtain a readable error string.
|
||||
.B ub_strerror
|
||||
returns a zero terminated string.
|
||||
.B ub_ctx_create
|
||||
returns NULL on an error (a malloc failure).
|
||||
.B ub_poll
|
||||
returns true if some information may be available, false otherwise.
|
||||
.B ub_fd
|
||||
returns a file descriptor or -1 on error.
|
||||
.SH "SEE ALSO"
|
||||
\fIunbound.conf\fR(5),
|
||||
\fIunbound\fR(8).
|
||||
.SH "AUTHORS"
|
||||
.B Unbound
|
||||
developers are mentioned in the CREDITS file in the distribution.
|
||||
@@ -250,6 +250,8 @@ X make overload mode work; phase 0 all ok, phase 1 some threads close ports,
|
||||
* private TTL, dTLS features.
|
||||
* retry-mode, where a bogus result triggers a retry-mode query, where a list
|
||||
of responses over a time interval is collected, and each is validated.
|
||||
or try in TCP mode. Do not 'try all servers several times', since we must
|
||||
not create packet storms with operator errors.
|
||||
* draft-timers, DLV features.
|
||||
|
||||
treeshrew/
|
||||
|
||||
@@ -34,7 +34,7 @@ then interface with the modules to perform the necessary processing.
|
||||
|
||||
In Section 2 the origins of the Unbound project are documented. Section
|
||||
3 lists the goals, while Section 4 lists the explicit non-goals of the
|
||||
project.
|
||||
project. Section 5 discusses choices made during development.
|
||||
|
||||
|
||||
2. History
|
||||
@@ -138,7 +138,7 @@ o The method by which dnssec-lameness is detected is not secure. DNSSEC lame
|
||||
before the validator will properly verify the messages.
|
||||
|
||||
Also for zones for which no chain of trust exists, but a DS is given by the
|
||||
parent, dnssec-lameness detection enables. This delivers dnnsec to our
|
||||
parent, dnssec-lameness detection enables. This delivers dnssec to our
|
||||
clients when possible (for client validators).
|
||||
|
||||
The following issue needs to be resolved:
|
||||
@@ -200,3 +200,9 @@ o If a client makes a query without RD bit, in the case of a returned
|
||||
ascertains that RRSIGs are OK (and not omitted), but does not
|
||||
check NSEC/NSEC3.
|
||||
|
||||
o Case preservation
|
||||
Unbound preserves the casing received from authority servers as best
|
||||
as possible. It compresses without case, so case can get lost there.
|
||||
The casing from the authority server is used in preference to the casing
|
||||
of the query name. This is different from BIND. RFC4343 allows either
|
||||
behaviour.
|
||||
|
||||
+25
-27
@@ -1,3 +1,4 @@
|
||||
.TH "unbound-checkconf" "8" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" unbound-checkconf.8 -- unbound configuration checker manual
|
||||
.\"
|
||||
@@ -6,40 +7,37 @@
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.Dd @date@
|
||||
.Dt unbound-checkconf 8
|
||||
.Sh NAME
|
||||
.SH "NAME"
|
||||
.LP
|
||||
unbound-checkconf
|
||||
.Nd Check unbound configuration file for errors.
|
||||
.Sh SYNOPSIS
|
||||
.Nm unbound-checkconf
|
||||
.Op Fl h
|
||||
.Op cfgfile
|
||||
.Sh DESCRIPTION
|
||||
.Ic Unbound-checkconf
|
||||
\- Check unbound configuration file for errors.
|
||||
.SH "SYNOPSIS"
|
||||
.B unbound-checkconf
|
||||
.RB [ \-h ]
|
||||
.IR cfgfile
|
||||
.SH "DESCRIPTION"
|
||||
.B Unbound-checkconf
|
||||
checks the configuration file for the
|
||||
.Xr unbound 8
|
||||
\fIunbound\fR(8)
|
||||
DNS resolver for syntax and other errors.
|
||||
The config file syntax is
|
||||
described in
|
||||
.Xr unbound.conf 5 .
|
||||
.Pp
|
||||
The config file syntax is described in
|
||||
\fIunbound.conf\fR(5).
|
||||
.P
|
||||
The available options are:
|
||||
.Bl -tag -width indent
|
||||
.It Fl h
|
||||
.TP
|
||||
.B \-h
|
||||
Show the version and commandline option help.
|
||||
.It cfgfile
|
||||
.TP
|
||||
.I cfgfile
|
||||
The config file to read with settings for unbound. It is checked.
|
||||
If omitted, the config file at the default location is checked.
|
||||
.Sh EXIT CODE
|
||||
.SH "EXIT CODE"
|
||||
The unbound-checkconf program exits with status code 1 on error,
|
||||
0 for a correct config file.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width indent
|
||||
.It Pa /etc/unbound/unbound.conf
|
||||
.SH "FILES"
|
||||
.TP
|
||||
.I /etc/unbound/unbound.conf
|
||||
unbound configuration file.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr unbound.conf 5 ,
|
||||
.Xr unbound 8 .
|
||||
.SH "SEE ALSO"
|
||||
\fIunbound.conf\fR(5),
|
||||
\fIunbound\fR(8).
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
.TH "unbound\-host" "1" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" unbound-host.1 -- unbound DNS lookup utility
|
||||
.\"
|
||||
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
.\"
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.SH "NAME"
|
||||
.LP
|
||||
.B unbound\-host
|
||||
\- unbound DNS lookup utility
|
||||
.SH "SYNOPSIS"
|
||||
.LP
|
||||
.B unbound\-host
|
||||
.RB [ \-vdh ]
|
||||
.RB [ \-c
|
||||
.IR class ]
|
||||
.RB [ \-t
|
||||
.IR type ]
|
||||
.I hostname
|
||||
.RB [ \-y
|
||||
.IR key ]
|
||||
.RB [ \-f
|
||||
.IR keyfile ]
|
||||
.RB [ \-F
|
||||
.IR namedkeyfile ]
|
||||
.SH "DESCRIPTION"
|
||||
.LP
|
||||
.B Unbound\-host
|
||||
uses the unbound validating resolver to query for the hostname and display
|
||||
results. With the \fB\-v\fR option it displays validation
|
||||
status: secure, insecure, bogus (security failure).
|
||||
.P
|
||||
The available options are:
|
||||
.TP
|
||||
.I hostname
|
||||
This name is resolved (looked up in the DNS).
|
||||
If a IPv4 or IPv6 address is given, a reverse lookup is performed.
|
||||
.TP
|
||||
.B \-h
|
||||
Show the version and commandline option help.
|
||||
.TP
|
||||
.B \-v
|
||||
Enable verbose output and it shows validation results, on every line.
|
||||
Secure means that the NXDOMAIN (no such domain name), nodata (no such data)
|
||||
or positive data response validated correctly with one of the keys.
|
||||
Insecure means that that domain name has no security set up for it.
|
||||
Bogus (security failure) means that the response failed one or more checks,
|
||||
it is likely wrong, outdated, tampered with, or broken.
|
||||
.TP
|
||||
.B \-d
|
||||
Enable debug output to stderr. One \-d shows what the resolver and validator
|
||||
are doing and may tell you what is going on. More times, \-d \-d, gives a
|
||||
lot of output, with every packet sent and received.
|
||||
.TP
|
||||
.B \-c \fIclass
|
||||
Specify the class to lookup for, the default is IN the internet class.
|
||||
.TP
|
||||
.B \-t \fItype
|
||||
Specify the type of data to lookup. The default looks for IPv4, IPv6 and
|
||||
mail handler data, or domain name pointers for reverse queries.
|
||||
.TP
|
||||
.B \-y \fIkey
|
||||
Specify a public key to use as trust anchor. This is the base for a chain
|
||||
of trust that is built up from the trust anchor to the response, in order
|
||||
to validate the response message. Can be given as a DS or DNSKEY record.
|
||||
For example \-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD".
|
||||
.TP
|
||||
.B \-f \fIkeyfile
|
||||
Reads keys from a file. Every line has a DS or DNSKEY record, in the format
|
||||
as for \-y. The zone file format, the same as dig and drill produce.
|
||||
.TP
|
||||
.B \-F \fInamedkeyfile
|
||||
Reads keys from a BIND\-style named.conf file. Only the trusted\-key {}; entries
|
||||
are read.
|
||||
.SH "EXAMPLES"
|
||||
.LP
|
||||
Some examples of use.
|
||||
.P
|
||||
$ unbound\-host www.example.com
|
||||
.P
|
||||
$ unbound\-host \-v \-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD" www.example.com
|
||||
.P
|
||||
$ unbound\-host \-v \-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD" 192.0.2.153
|
||||
.SH "EXIT CODE"
|
||||
The unbound\-host program exits with status code 1 on error,
|
||||
0 on no error. The data may not be available on exit code 0, exit code 1
|
||||
means the lookup encountered a fatal error.
|
||||
.SH "SEE ALSO"
|
||||
\fIunbound.conf\fR(5),
|
||||
\fIunbound\fR(8).
|
||||
+32
-28
@@ -1,3 +1,4 @@
|
||||
.TH "unbound" "8" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" unbound.8 -- unbound manual
|
||||
.\"
|
||||
@@ -6,42 +7,45 @@
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.Dd @date@
|
||||
.Dt unbound 8
|
||||
.Sh NAME
|
||||
unbound
|
||||
.Nd Unbound DNS validating resolver @version@.
|
||||
.Sh SYNOPSIS
|
||||
.Nm unbound
|
||||
.Op Fl h
|
||||
.Op Fl d
|
||||
.Op Fl v
|
||||
.Op Fl c Ar cfgfile
|
||||
.Sh DESCRIPTION
|
||||
.Ic Unbound
|
||||
.SH "NAME"
|
||||
.LP
|
||||
.B unbound
|
||||
\- Unbound DNS validating resolver @version@.
|
||||
.SH "SYNOPSIS"
|
||||
.LP
|
||||
.B unbound
|
||||
.RB [ \-h ]
|
||||
.RB [ \-d ]
|
||||
.RB [ \-v ]
|
||||
.RB [ \-c
|
||||
.IR cfgfile ]
|
||||
.SH "DESCRIPTION"
|
||||
.LP
|
||||
.B Unbound
|
||||
is an implementation of a DNS resolver, that does caching and
|
||||
DNSSEC validation.
|
||||
.Pp
|
||||
.P
|
||||
The available options are:
|
||||
.Bl -tag -width indent
|
||||
.It Fl h
|
||||
.TP
|
||||
.B \-h
|
||||
Show the version and commandline option help.
|
||||
.It Fl c Ar cfgfile
|
||||
.TP
|
||||
.B \-c\fI cfgfile
|
||||
Set the config file with settings for unbound to read instead of the
|
||||
file at default location /etc/unbound/unbound.conf. The syntax is
|
||||
described in
|
||||
.Xr unbound.conf 5 .
|
||||
.It Fl d
|
||||
described in \fIunbound.conf\fR(5).
|
||||
.TP
|
||||
.B \-d
|
||||
Debug flag, do not fork into the background, but stay attached to the
|
||||
console. This flag will also delay writing to the logfile until the
|
||||
thread-spawn time. So that most config and setup errors appear on stderr.
|
||||
.It Fl v
|
||||
thread\-spawn time. So that most config and setup errors appear on stderr.
|
||||
.TP
|
||||
.B \-v
|
||||
Increase verbosity. If given multiple times, more information is logged.
|
||||
This is in addition to the verbosity (if any) from the config file.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr unbound.conf 5 ,
|
||||
.Xr unbound-checkconf 8 .
|
||||
.Sh AUTHORS
|
||||
.Ic Unbound
|
||||
.SH "SEE ALSO"
|
||||
\fIunbound.conf\fR(5),
|
||||
\fIunbound\-checkconf\fR(8).
|
||||
.SH "AUTHORS"
|
||||
.B Unbound
|
||||
developers are mentioned in the CREDITS file in the distribution.
|
||||
|
||||
+352
-210
@@ -1,3 +1,4 @@
|
||||
.TH "unbound.conf" "5" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" unbound.conf.5 -- unbound.conf manual
|
||||
.\"
|
||||
@@ -6,39 +7,44 @@
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.Dd @date@
|
||||
.Os FreeBSD
|
||||
.Dt unbound.conf 5
|
||||
.Sh NAME
|
||||
.Nm unbound.conf
|
||||
.Nd Unbound configuration file.
|
||||
.Sh SYNOPSIS
|
||||
.Nm unbound.conf
|
||||
.Sh DESCRIPTION
|
||||
.Ic unbound.conf
|
||||
.SH "NAME"
|
||||
.LP
|
||||
.B unbound.conf
|
||||
\- Unbound configuration file.
|
||||
.SH "SYNOPSIS"
|
||||
.LP
|
||||
.B unbound.conf
|
||||
.SH "DESCRIPTION"
|
||||
.LP
|
||||
.B unbound.conf
|
||||
is used to configure
|
||||
.Xr unbound 8 .
|
||||
\fIunbound\fR(8).
|
||||
The file format has attributes and values. Some attributes have attributes inside them.
|
||||
The notation is: attribute: value.
|
||||
.Pp
|
||||
.P
|
||||
Comments start with # and last to the end of line. Empty lines are
|
||||
ignored as is whitespace at the beginning of a line.
|
||||
.Pp
|
||||
.P
|
||||
The utility
|
||||
.Xr unbound-checkconf 8
|
||||
\fIunbound\-checkconf\fR(8)
|
||||
can be used to check unbound.conf prior to usage.
|
||||
.Sh EXAMPLE
|
||||
.SH "EXAMPLE"
|
||||
An example config file is shown below. Copy this to /etc/unbound/unbound.conf
|
||||
and start the server with:
|
||||
.P
|
||||
.nf
|
||||
$ unbound -c /etc/unbound/unbound.conf
|
||||
$ unbound \-c /etc/unbound/unbound.conf
|
||||
.fi
|
||||
.P
|
||||
Most settings are the defaults. Stop the server with:
|
||||
.P
|
||||
.nf
|
||||
$ kill `cat /etc/unbound/unbound.pid`
|
||||
.fi
|
||||
.P
|
||||
Below is a minimal config file. The source distribution contains an extensive
|
||||
example.conf file with all the options.
|
||||
.P
|
||||
.nf
|
||||
# unbound.conf(5) config file for unbound(8).
|
||||
server:
|
||||
@@ -48,205 +54,268 @@ server:
|
||||
# logfile: "/etc/unbound/unbound.log" #uncomment to use logfile.
|
||||
pidfile: "/etc/unbound/unbound.pid"
|
||||
# verbosity: 1 # uncomment and increase to get more logging.
|
||||
|
||||
# listen on all interfaces, answer queries from the local subnet.
|
||||
interface: 0.0.0.0
|
||||
interface: ::0
|
||||
access-control: 10.0.0.0/8 allow
|
||||
access-control: 2001:DB8::/64 allow
|
||||
access\-control: 10.0.0.0/8 allow
|
||||
access\-control: 2001:DB8::/64 allow
|
||||
.fi
|
||||
.Sh FILE FORMAT
|
||||
.SH "FILE FORMAT"
|
||||
.LP
|
||||
There must be whitespace between keywords. Attribute keywords end with a colon ':'. An attribute
|
||||
is followed by its containing attributes, or a value.
|
||||
.Pp
|
||||
.P
|
||||
Files can be included using the
|
||||
.Ic include:
|
||||
.B include:
|
||||
directive. It can appear anywhere, and takes a single filename as an argument.
|
||||
Processing continues as if the text from the included file was copied into
|
||||
the config file at that point.
|
||||
.Ss Server Options
|
||||
.SS "Server Options"
|
||||
These options are part of the
|
||||
.Ic server:
|
||||
.B server:
|
||||
clause.
|
||||
.Bl -tag -width indent
|
||||
.It \fBverbosity:\fR <number>
|
||||
.TP
|
||||
.B verbosity: \fI<number>
|
||||
The verbosity number, level 0 means no verbosity, only errors. Level 1
|
||||
gives operational information. Level 2 gives query level information,
|
||||
output per query. Level 3 gives algorithm level information.
|
||||
gives operational information. Level 2 gives detailed operational
|
||||
information. Level 3 gives query level information, output per query.
|
||||
Level 4 gives algorithm level information.
|
||||
Default is level 1. The verbosity can also be increased from the commandline,
|
||||
see
|
||||
.Xr unbound 8 .
|
||||
.It \fBnum-threads:\fR <number>
|
||||
\fIunbound\fR(8).
|
||||
.TP
|
||||
.B statistics-interval: \fI<seconds>
|
||||
The number of seconds between printing statistics to the log for every thread.
|
||||
Disable with value 0 or "". Default is disabled.
|
||||
.TP
|
||||
.B num\-threads: \fI<number>
|
||||
The number of threads to create to serve clients. Use 1 for no threading.
|
||||
.It \fBport:\fR <port number>
|
||||
.TP
|
||||
.B port: \fI<port number>
|
||||
The port number, default 53, on which the server responds to queries.
|
||||
.It \fBinterface:\fR <ip address>
|
||||
.TP
|
||||
.B interface: \fI<ip address>
|
||||
Interface to use to connect to the network. This interface is listened to
|
||||
for queries from clients, and answers to clients are given from it.
|
||||
Can be given multiple times to work on several interfaces. If none are
|
||||
given the default is to listen to localhost.
|
||||
The interfaces are not changed on a reload (kill -HUP) but only on restart.
|
||||
.It \fBoutgoing-interface:\fR <ip address>
|
||||
The interfaces are not changed on a reload (kill \-HUP) but only on restart.
|
||||
.TP
|
||||
.B interface-automatic: \fI<yes or no>
|
||||
Detect source interface on UDP queries and copy them to replies. This
|
||||
feature is experimental, and needs support in your OS for IPv6
|
||||
(and its socket options) and IPv4 (and have source-interface socket options).
|
||||
Default value is no.
|
||||
.TP
|
||||
.B outgoing\-interface: \fI<ip address>
|
||||
Interface to use to connect to the network. This interface is used to send
|
||||
queries to authoritative servers and receive their replies. Can be given
|
||||
multiple times to work on several interfaces. If none are given the
|
||||
default (all) is used. You can specify the same interfaces in
|
||||
.Ic interface:
|
||||
.B interface:
|
||||
and
|
||||
.Ic outgoing-interface:
|
||||
.B outgoing\-interface:
|
||||
lines, the interfaces are then used for both purposes. Outgoing queries are
|
||||
sent via a random outgoing interface to counter spoofing.
|
||||
.It \fBoutgoing-port:\fR <port number>
|
||||
.TP
|
||||
.B outgoing\-port: \fI<port number>
|
||||
The starting port number where the outgoing query port range is allocated.
|
||||
Default is 1053.
|
||||
.It \fBoutgoing-range:\fR <number>
|
||||
.TP
|
||||
.B outgoing\-range: \fI<number>
|
||||
Number of ports to open. This number is opened per thread for every outgoing
|
||||
query interface. Must be at least 1. Default is 16.
|
||||
Larger numbers give more protection against spoofing attempts, but need
|
||||
extra resources from the operating system.
|
||||
.It \fBoutgoing-num-tcp:\fR <number>
|
||||
.TP
|
||||
.B outgoing\-num\-tcp: \fI<number>
|
||||
Number of outgoing TCP buffers to allocate per thread. Default is 10. If set
|
||||
to 0, or if do_tcp is "no", no TCP queries to authoritative servers are done.
|
||||
.It \fBincoming-num-tcp:\fR <number>
|
||||
.TP
|
||||
.B incoming\-num\-tcp: \fI<number>
|
||||
Number of incoming TCP buffers to allocate per thread. Default is 10. If set
|
||||
to 0, or if do_tcp is "no", no TCP queries from clients are accepted.
|
||||
.It \fBmsg-buffer-size:\fR <number>
|
||||
.TP
|
||||
.B msg\-buffer\-size: \fI<number>
|
||||
Number of bytes size of the message buffers. Default is 65552 bytes, enough
|
||||
for 64 Kb packets, the maximum DNS message size. No message larger than this
|
||||
can be sent or received. Can be reduced to use less memory, but some requests
|
||||
for DNS data, such as for huge resource records, will result in a SERVFAIL
|
||||
reply to the client.
|
||||
.It \fBmsg-cache-size:\fR <number>
|
||||
.TP
|
||||
.B msg\-cache\-size: \fI<number>
|
||||
Number of bytes size of the message cache. Default is 4 megabytes.
|
||||
.It \fBmsg-cache-slabs:\fR <number>
|
||||
A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes
|
||||
or gigabytes (1024*1024 bytes in a megabyte).
|
||||
.TP
|
||||
.B msg\-cache\-slabs: \fI<number>
|
||||
Number of slabs in the message cache. Slabs reduce lock contention by threads.
|
||||
Must be set to a power of 2. Setting (close) to the number of cpus is a
|
||||
reasonable guess.
|
||||
.It \fBnum-queries-per-thread:\fR <number>
|
||||
.TP
|
||||
.B num\-queries\-per\-thread: \fI<number>
|
||||
The number of queries that every thread will service simultaneously.
|
||||
If more queries arrive that need servicing, they are dropped. This forces
|
||||
the client to resend after a timeout; allowing the server time to work on
|
||||
the existing queries. Default 1024.
|
||||
.It \fBrrset-cache-size:\fR <number>
|
||||
.TP
|
||||
.B rrset\-cache\-size: \fI<number>
|
||||
Number of bytes size of the RRset cache. Default is 4 megabytes.
|
||||
.It \fBrrset-cache-slabs:\fR <number>
|
||||
A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes
|
||||
or gigabytes (1024*1024 bytes in a megabyte).
|
||||
.TP
|
||||
.B rrset\-cache\-slabs: \fI<number>
|
||||
Number of slabs in the RRset cache. Slabs reduce lock contention by threads.
|
||||
Must be set to a power of 2.
|
||||
.It \fBcache-max-ttl:\fR <seconds>
|
||||
.TP
|
||||
.B cache\-max\-ttl: \fI<seconds>
|
||||
Time to live maximum for RRsets and messages in the cache. Default is
|
||||
86400 seconds (1 day). If the maximum kicks in, responses to clients
|
||||
still get decrementing TTLs based on the original (larger) values.
|
||||
When the internal TTL expires, the cache item has expired.
|
||||
Can be set lower to force the resolver to query for data often, and not
|
||||
trust (very large) TTL values.
|
||||
.It \fBinfra-host-ttl:\fR <seconds>
|
||||
.TP
|
||||
.B infra\-host\-ttl: \fI<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>
|
||||
.TP
|
||||
.B infra\-lame\-ttl: \fI<seconds>
|
||||
The time to live when a delegation is discovered to be lame. Default is 900.
|
||||
.It \fBinfra-cache-slabs:\fR <number>
|
||||
.TP
|
||||
.B infra\-cache\-slabs: \fI<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>
|
||||
.TP
|
||||
.B infra\-cache\-numhosts: \fI<number>
|
||||
Number of hosts for which information is cached. Default is 10000.
|
||||
.It \fBinfra-cache-lame-size:\fR <number>
|
||||
.TP
|
||||
.B infra\-cache\-lame\-size: \fI<number>
|
||||
Number of bytes that the lameness cache per host is allowed to use. Default
|
||||
is 10 kb, which gives maximum storage for a couple score zones, depending on
|
||||
the lame zone name lengths.
|
||||
.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>
|
||||
Enable or disable whether ip6 queries are answered. Default is yes.
|
||||
.It \fBdo-udp:\fR <yes or no>
|
||||
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 \fBaccess-control:\fR <IP netblock> <action>
|
||||
.TP
|
||||
.B do\-ip4: \fI<yes or no>
|
||||
Enable or disable whether ip4 queries are answered or issued. Default is yes.
|
||||
.TP
|
||||
.B do\-ip6: \fI<yes or no>
|
||||
Enable or disable whether ip6 queries are answered or issued. Default is yes.
|
||||
If disabled, queries are not answered on IPv6, and queries are not sent on
|
||||
IPv6 to the internet nameservers.
|
||||
.TP
|
||||
.B do\-udp: \fI<yes or no>
|
||||
Enable or disable whether UDP queries are answered or issued. Default is yes.
|
||||
.TP
|
||||
.B do\-tcp: \fI<yes or no>
|
||||
Enable or disable whether TCP queries are answered or issued. Default is yes.
|
||||
.TP
|
||||
.B access\-control: \fI<IP netblock> <action>
|
||||
The netblock is given as an IP4 or IP6 address with /size appended for a
|
||||
classless network block. The action can be deny, refuse or allow.
|
||||
Deny stops queries from hosts from that netblock.
|
||||
Refuse stops queries too, but sends a DNS rcode REFUSED error message back.
|
||||
Allow gives access to clients from that netblock.
|
||||
By default only localhost is allowed, the rest is refused.
|
||||
.It \fBchroot:\fR <directory>
|
||||
The default is refused, because that is protocol\-friendly. The DNS protocol
|
||||
is not designed to handle dropped packets due to policy, and dropping may
|
||||
result in (possibly excessive) retried queries.
|
||||
.TP
|
||||
.B chroot: \fI<directory>
|
||||
If given a chroot is done to the given directory. The default is
|
||||
"/etc/unbound". If you give "" no chroot is performed.
|
||||
.It \fBusername:\fR <name>
|
||||
.TP
|
||||
.B username: \fI<name>
|
||||
If given, after binding the port the user privileges are dropped. Default is
|
||||
"unbound". If you give username: "" no user change is performed.
|
||||
.Pp
|
||||
.IP
|
||||
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 reload will fail; a restart is needed.
|
||||
.It \fBdirectory:\fR <directory>
|
||||
.TP
|
||||
.B directory: \fI<directory>
|
||||
Sets the working directory for the program.
|
||||
.It \fBlogfile:\fR <filename>
|
||||
.TP
|
||||
.B logfile: \fI<filename>
|
||||
If "" is given, logging goes to stderr, or nowhere once daemonized.
|
||||
The logfile is appended to, in the following format:
|
||||
.nf
|
||||
[seconds since 1970] unbound[pid:tid]: type: message.
|
||||
If this option is given, the use-syslog is option is set to "no".
|
||||
.fi
|
||||
If this option is given, the use\-syslog is option is set to "no".
|
||||
The logfile is reopened (for append) when the config file is reread, on
|
||||
SIGHUP.
|
||||
.It \fBuse-syslog:\fR <yes or no>
|
||||
.TP
|
||||
.B use\-syslog: \fI<yes or no>
|
||||
Sets unbound to send log messages to the syslogd, using
|
||||
.Xr syslog 3 .
|
||||
\fIsyslog\fR(3).
|
||||
The log facility LOG_DAEMON is used, with identity "unbound".
|
||||
The logfile setting is overridden when use-syslog is turned on.
|
||||
The logfile setting is overridden when use\-syslog is turned on.
|
||||
The default is to log to syslog.
|
||||
.It \fBpidfile:\fR <filename>
|
||||
.TP
|
||||
.B pidfile: \fI<filename>
|
||||
The process id is written to the file. Default is "/etc/unbound/unbound.pid".
|
||||
So,
|
||||
.nf
|
||||
kill -HUP `cat /etc/unbound/unbound.pid`
|
||||
kill \-HUP `cat /etc/unbound/unbound.pid`
|
||||
.fi
|
||||
triggers a reload,
|
||||
.nf
|
||||
kill -QUIT `cat /etc/unbound/unbound.pid`
|
||||
kill \-QUIT `cat /etc/unbound/unbound.pid`
|
||||
.fi
|
||||
gracefully terminates.
|
||||
.It \fBroot-hints:\fR <filename>
|
||||
.TP
|
||||
.B root\-hints: \fI<filename>
|
||||
Read the root hints from this file. Default is nothing, using builtin hints
|
||||
for the IN class. The file has the format of zone files, with root
|
||||
nameserver names and addresses only. The default may become outdated,
|
||||
when servers change, therefore it is good practice to use a root-hints file.
|
||||
.It \fBhide-identity:\fR <yes or no>
|
||||
when servers change, therefore it is good practice to use a root\-hints file.
|
||||
.TP
|
||||
.B hide\-identity: \fI<yes or no>
|
||||
If enabled id.server and hostname.bind queries are refused.
|
||||
.It \fBidentity:\fR <string>
|
||||
.TP
|
||||
.B identity: \fI<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>
|
||||
.TP
|
||||
.B hide\-version: \fI<yes or no>
|
||||
If enabled version.server and version.bind queries are refused.
|
||||
.It \fBversion:\fR <string>
|
||||
.TP
|
||||
.B version: \fI<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">
|
||||
.TP
|
||||
.B target\-fetch\-policy: \fI<"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.
|
||||
.Pp
|
||||
.IP
|
||||
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
|
||||
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.
|
||||
.Pp
|
||||
.IP
|
||||
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
|
||||
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>
|
||||
.TP
|
||||
.B harden\-short\-bufsize: \fI<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>
|
||||
.TP
|
||||
.B harden\-large\-queries: \fI<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>
|
||||
.TP
|
||||
.B harden\-glue: \fI<yes or no>
|
||||
Will trust glue only if it is within the servers authority. Default is on.
|
||||
.It \fBharden-dnssec-stripped:\fR <yes or no>
|
||||
Require DNSSEC data for trust-anchored zones, if such data is absent,
|
||||
.TP
|
||||
.B harden\-dnssec\-stripped: \fI<yes or no>
|
||||
Require DNSSEC data for trust\-anchored zones, if such data is absent,
|
||||
the zone becomes bogus. If turned off, and no DNSSEC data is received
|
||||
(or the DNSKEY data fails to validate), then the zone is made insecure,
|
||||
this behaves like there is no trust anchor. You could turn this off if
|
||||
@@ -254,60 +323,71 @@ you are sometimes behind an intrusive firewall (of some sort) that
|
||||
removes DNSSEC data from packets, or a zone changes from signed to
|
||||
unsigned to badly signed often. If turned off you run the risk of a
|
||||
downgrade attack that disables security for a zone. Default is on.
|
||||
.It \fBdo-not-query-address:\fR <IP address>
|
||||
.TP
|
||||
.B do\-not\-query\-address: \fI<IP address>
|
||||
Do not query the given IP address. Can be IP4 or IP6. Append /num to
|
||||
indicate a classless delegation netblock, for example like
|
||||
10.2.3.4/24 or 2001::11/64.
|
||||
.It \fBdo-not-query-localhost:\fR <yes or no>
|
||||
If yes, localhost is added to the do-not-query-address entries, both
|
||||
.TP
|
||||
.B do\-not\-query\-localhost: \fI<yes or no>
|
||||
If yes, localhost is added to the do\-not\-query\-address entries, both
|
||||
IP6 ::1 and IP4 127.0.0.1/8. If no, then localhost can be used to send
|
||||
queries to. Default is yes.
|
||||
.It \fBmodule-config:\fR <"module names">
|
||||
.TP
|
||||
.B module\-config: \fI<"module names">
|
||||
Module configuration, a list of module names separated by spaces, surround
|
||||
the string with quotes (""). The modules can be validator, iterator.
|
||||
Setting this to "iterator" will result in a non-validating server.
|
||||
Setting this to "iterator" will result in a non\-validating server.
|
||||
Setting this to "validator iterator" will turn on DNSSEC validation.
|
||||
You must also set trust-anchors for validation to be useful.
|
||||
.It \fBtrust-anchor-file:\fR <filename>
|
||||
You must also set trust\-anchors for validation to be useful.
|
||||
.TP
|
||||
.B trust\-anchor\-file: \fI<filename>
|
||||
File with trusted keys for validation. Both DS and DNSKEY entries can appear
|
||||
in the file. The format of the file is the standard DNS Zone file format.
|
||||
Default is "", or no trust anchor file.
|
||||
.It \fBtrust-anchor:\fR <"Resource Record">
|
||||
.TP
|
||||
.B trust\-anchor: \fI<"Resource Record">
|
||||
A DS or DNSKEY RR for a key to use for validation. Multiple entries can be
|
||||
given to specify multiple trusted keys, in addition to the trust-anchor-files.
|
||||
given to specify multiple trusted keys, in addition to the trust\-anchor\-files.
|
||||
The resource record is entered in the same format as 'dig' or 'drill' prints
|
||||
them, the same format as in the zone file. Has to be on a single line, with
|
||||
"" around it. A TTL can be specified for ease of cut and paste, but is ignored.
|
||||
A class can be specified, but class IN is default.
|
||||
.It \fBtrusted-keys-file:\fR <filename>
|
||||
.TP
|
||||
.B trusted\-keys\-file: \fI<filename>
|
||||
File with trusted keys for validation. Specify more than one file
|
||||
with several entries, one file per entry. Like \fBtrust-anchor-file\fR
|
||||
but has a different file format. Format is BIND-9 style format,
|
||||
the trusted-keys { name flag proto algo "key"; }; clauses are read.
|
||||
.It \fBval-override-date:\fR <rrsig-style date spec>
|
||||
with several entries, one file per entry. Like \fBtrust\-anchor\-file\fR
|
||||
but has a different file format. Format is BIND\-9 style format,
|
||||
the trusted\-keys { name flag proto algo "key"; }; clauses are read.
|
||||
.TP
|
||||
.B val\-override\-date: \fI<rrsig\-style date spec>
|
||||
Default is "" or "0", which disables this debugging feature. If enabled by
|
||||
giving a RRSIG style date, that date is used for verifying RRSIG inception
|
||||
and expiration dates, instead of the current date. Do not set this unless
|
||||
you are debugging signature inception and expiration.
|
||||
.It \fBval-bogus-ttl:\fR <number>
|
||||
.TP
|
||||
.B val\-bogus\-ttl: \fI<number>
|
||||
The time to live for bogus data. This is data that has failed validation;
|
||||
due to invalid signatures or other checks. The TTL from that data cannot be
|
||||
trusted, and this value is used instead. The value is in seconds, default 900.
|
||||
The time interval prevents repeated revalidation of bogus data.
|
||||
.It \fBval-clean-additional:\fR <yes or no>
|
||||
.TP
|
||||
.B val\-clean\-additional: \fI<yes or no>
|
||||
Instruct the validator to remove data from the additional section of secure
|
||||
messages that are not signed properly. Messages that are insecure, bogus,
|
||||
indeterminate or unchecked are not affected. Default is yes. Use this setting
|
||||
to protect the users that rely on this validator for authentication from
|
||||
protentially bad data in the additional section.
|
||||
.It \fBval-permissive-mode:\fR <yes or no>
|
||||
.TP
|
||||
.B val\-permissive\-mode: \fI<yes or no>
|
||||
Instruct the validator to mark bogus messages as indeterminate. The security
|
||||
checks are performed, but if the result is bogus (failed security), the
|
||||
reply is not withheld from the client with SERVFAIL as usual. The client
|
||||
receives the bogus data. For messages that are found to be secure the AD bit
|
||||
is set in replies. Also logging is performed as for full validation.
|
||||
The default value is "no".
|
||||
.It \fBval-nsec3-keysize-iterations:\fR <"list of values">
|
||||
.TP
|
||||
.B val\-nsec3\-keysize\-iterations: \fI<"list of values">
|
||||
List of keysize and iteration count values, separated by spaces, surrounded
|
||||
by quotes. Default is "1024 150 2048 500 4096 2500". This determines the
|
||||
maximum allowed NSEC3 iteration count before a message is simply marked
|
||||
@@ -315,188 +395,250 @@ insecure instead of performing the many hashing iterations. The list must
|
||||
be in ascending order and have at least one entry. If you set it to
|
||||
"1024 65535" there is no restriction to NSEC3 iteration values.
|
||||
This table must be kept short; a very long list could cause slower operation.
|
||||
.It \fBkey-cache-size:\fR <number>
|
||||
.TP
|
||||
.B key\-cache\-size: \fI<number>
|
||||
Number of bytes size of the key cache. Default is 4 megabytes.
|
||||
.It \fBkey-cache-slabs:\fR <number>
|
||||
A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes
|
||||
or gigabytes (1024*1024 bytes in a megabyte).
|
||||
.TP
|
||||
.B key\-cache\-slabs: \fI<number>
|
||||
Number of slabs in the key cache. Slabs reduce lock contention by threads.
|
||||
Must be set to a power of 2. Setting (close) to the number of cpus is a
|
||||
reasonable guess.
|
||||
.It \fBlocal-zone:\fR <zone> <type>
|
||||
.TP
|
||||
.B local\-zone: \fI<zone> <type>
|
||||
Configure a local zone. The type determines the answer to give if there is
|
||||
no match from local-data. The types are deny, refuse, static, transparent,
|
||||
no match from local\-data. The types are deny, refuse, static, transparent,
|
||||
redirect, nodefault, and are explained below. After that the default settings
|
||||
are listed. Use local-data: to enter data into the local zone. Answers for
|
||||
are listed. Use local\-data: to enter data into the local zone. Answers for
|
||||
local zones are authoritative DNS answers. By default the zones are class IN.
|
||||
.Bl -tag -width indent
|
||||
.It \fIdeny\fR
|
||||
.IP
|
||||
If you need more complicated authoritative data, with referrals, wildcards,
|
||||
CNAME/DNAME support, or DNSSEC authoritative service, setup a stub\-zone for
|
||||
it as detailed in the stub zone section below.
|
||||
.TP 10
|
||||
\h'5'\fIdeny\fR
|
||||
Do not send an answer, drop the query.
|
||||
If there is a match from local data, the query is answered.
|
||||
.It \fIrefuse\fR
|
||||
.TP 10
|
||||
\h'5'\fIrefuse\fR
|
||||
Send an error message reply, with rcode REFUSED.
|
||||
If there is a match from local data, the query is answered.
|
||||
.It \fIstatic\fR
|
||||
.TP 10
|
||||
\h'5'\fIstatic\fR
|
||||
If there is a match from local data, the query is answered.
|
||||
Otherwise, the query is answered with nodata or nxdomain.
|
||||
For a negative answer a SOA is included in the answer if present
|
||||
as local-data for the zone apex domain.
|
||||
.It \fItransparent\fR
|
||||
as local\-data for the zone apex domain.
|
||||
.TP 10
|
||||
\h'5'\fItransparent\fR
|
||||
If there is a match from local data, the query is answered.
|
||||
Otherwise, the query is resolved normally.
|
||||
If no local-zone is given local-data causes a transparent zone
|
||||
If no local\-zone is given local\-data causes a transparent zone
|
||||
to be created by default.
|
||||
.It \fIredirect\fR
|
||||
.TP 10
|
||||
\h'5'\fIredirect\fR
|
||||
The query is answered from the local data for the zone name.
|
||||
There may be no local data beneath the zone name.
|
||||
This answers queries for the zone, and all subdomains of the zone
|
||||
with the local data for the zone.
|
||||
It can be used to redirect a domain to a different address, with
|
||||
local-zone: "example.com." redirect and
|
||||
local-data: "example.com. A 127.0.0.1"
|
||||
local\-zone: "example.com." redirect and
|
||||
local\-data: "example.com. A 127.0.0.1"
|
||||
queries for www.example.com and www.foo.example.com are redirected.
|
||||
.It \fInodefault\fR
|
||||
.TP 10
|
||||
\h'5'\fInodefault\fR
|
||||
Used to turn off default contents for AS112 zones. The other types
|
||||
also turn off default contents for the zone. The 'nodefault' option
|
||||
has no other effect than turning off default contents for the
|
||||
given zone.
|
||||
.El
|
||||
.P
|
||||
The default zones are localhost, reverse 127.0.0.1 and ::1, and the AS112
|
||||
zones. The AS112 zones are reverse DNS zones for private use and reserved
|
||||
IP addresses for which the servers on the internet cannot provide correct
|
||||
answers. They are configured by default to give nxdomain (no reverse
|
||||
information) answers. The defaults can be turned off by specifying your
|
||||
own local-zone of that name, or using the 'nodefault' type. Below is a
|
||||
own local\-zone of that name, or using the 'nodefault' type. Below is a
|
||||
list of the default zone contents.
|
||||
.Bl -tag -width indent
|
||||
.It \fIlocalhost\fR
|
||||
.TP 10
|
||||
\h'5'\fIlocalhost\fR
|
||||
The IP4 and IP6 localhost information is given. NS and SOA records are provided
|
||||
for completeness and to satisfy some DNS update tools. Default content:
|
||||
.nf
|
||||
local-zone: "localhost." static
|
||||
local-data: "localhost. 10800 IN NS localhost."
|
||||
local-data: "localhost. 10800 IN SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
|
||||
local-data: "localhost. 10800 IN A 127.0.0.1"
|
||||
local-data: "localhost. 10800 IN AAAA ::1"
|
||||
local\-zone: "localhost." static
|
||||
local\-data: "localhost. 10800 IN NS localhost."
|
||||
local\-data: "localhost. 10800 IN
|
||||
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
|
||||
local\-data: "localhost. 10800 IN A 127.0.0.1"
|
||||
local\-data: "localhost. 10800 IN AAAA ::1"
|
||||
.fi
|
||||
.It \fIreverse IPv4 loopback\fR
|
||||
.TP 10
|
||||
\h'5'\fIreverse IPv4 loopback\fR
|
||||
Default content:
|
||||
.nf
|
||||
local-zone: "127.in-addr.arpa." static
|
||||
local-data: "127.in-addr.arpa. 10800 IN NS localhost."
|
||||
local-data: "127.in-addr.arpa. 10800 IN SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
|
||||
local-data: "1.0.0.127.in-addr.arpa. 10800 IN PTR localhost."
|
||||
local\-zone: "127.in\-addr.arpa." static
|
||||
local\-data: "127.in\-addr.arpa. 10800 IN NS localhost."
|
||||
local\-data: "127.in\-addr.arpa. 10800 IN
|
||||
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
|
||||
local\-data: "1.0.0.127.in\-addr.arpa. 10800 IN
|
||||
PTR localhost."
|
||||
.fi
|
||||
.It \fIreverse IPv6 loopback\fR
|
||||
.TP 10
|
||||
\h'5'\fIreverse IPv6 loopback\fR
|
||||
Default content:
|
||||
.nf
|
||||
local-zone: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." static
|
||||
local-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN NS localhost."
|
||||
local-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
|
||||
local-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN PTR localhost."
|
||||
local\-zone: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." static
|
||||
local\-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN
|
||||
NS localhost."
|
||||
local\-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN
|
||||
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
|
||||
local\-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN
|
||||
PTR localhost."
|
||||
.fi
|
||||
.It \fIreverse RFC1918 local use zones\fR
|
||||
Reverse data for zones 10.in-addr.arpa, 16.172.in-addr.arpa to
|
||||
31.172.in-addr.arpa, 168.192.in-addr.arpa.
|
||||
The \fBlocal-zone:\fR is set static and as \fBlocal-data:\fR SOA and NS
|
||||
.TP 10
|
||||
\h'5'\fIreverse RFC1918 local use zones\fR
|
||||
Reverse data for zones 10.in\-addr.arpa, 16.172.in\-addr.arpa to
|
||||
31.172.in\-addr.arpa, 168.192.in\-addr.arpa.
|
||||
The \fBlocal\-zone:\fR is set static and as \fBlocal\-data:\fR SOA and NS
|
||||
records are provided.
|
||||
.It \fIreverse RFC3330 IP4 this, link-local, testnet and broadcast\fR
|
||||
Reverse data for zones 0.in-addr.arpa, 254.169.in-addr.arpa,
|
||||
2.0.192.in-addr.arpa, 255.255.255.255.in-addr.arpa.
|
||||
.It \fIreverse RFC4291 IP6 unspecified\fR
|
||||
Reverse data for zone 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.
|
||||
.It \fIreverse RFC4193 IPv6 Locally Assigned Local Addresses\fR
|
||||
.TP 10
|
||||
\h'5'\fIreverse RFC3330 IP4 this, link\-local, testnet and broadcast\fR
|
||||
Reverse data for zones 0.in\-addr.arpa, 254.169.in\-addr.arpa,
|
||||
2.0.192.in\-addr.arpa, 255.255.255.255.in\-addr.arpa.
|
||||
.TP 10
|
||||
\h'5'\fIreverse RFC4291 IP6 unspecified\fR
|
||||
Reverse data for zone
|
||||
.nf
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.
|
||||
.fi
|
||||
.TP 10
|
||||
\h'5'\fIreverse RFC4193 IPv6 Locally Assigned Local Addresses\fR
|
||||
Reverse data for zone D.F.ip6.arpa.
|
||||
.It \fIreverse RFC4291 IPv6 Link Local Addresses\fR
|
||||
.TP 10
|
||||
\h'5'\fIreverse RFC4291 IPv6 Link Local Addresses\fR
|
||||
Reverse data for zones 8.E.F.ip6.arpa to B.E.F.ip6.arpa.
|
||||
.El
|
||||
.\" End of local-zone listing.
|
||||
.It \fBlocal-data:\fR "<resource record string>"
|
||||
.TP 5
|
||||
.B local\-data: \fI"<resource record string>"
|
||||
Configure local data, which is served in reply to queries for it.
|
||||
The query has to match exactly unless you configure the local-zone as
|
||||
redirect. If not matched exactly, the local-zone type determines
|
||||
further processing. If local-data is configured that is not a subdomain of
|
||||
a local-zone, a transparent local-zone is configured.
|
||||
The query has to match exactly unless you configure the local\-zone as
|
||||
redirect. If not matched exactly, the local\-zone type determines
|
||||
further processing. If local\-data is configured that is not a subdomain of
|
||||
a local\-zone, a transparent local\-zone is configured.
|
||||
For record types such as TXT, use single quotes, as in
|
||||
local-data: 'example. TXT "text"'.
|
||||
.El
|
||||
.Ss Stub Zone Options
|
||||
local\-data: 'example. TXT "text"'.
|
||||
.IP
|
||||
If you need more complicated authoritative data, with referrals, wildcards,
|
||||
CNAME/DNAME support, or DNSSEC authoritative service, setup a stub\-zone for
|
||||
it as detailed in the stub zone section below.
|
||||
.SS "Stub Zone Options"
|
||||
.LP
|
||||
There may be multiple
|
||||
.Ic stub-zone:
|
||||
.B 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>
|
||||
.P
|
||||
The stub zone can be used to configure authoritative data to be used
|
||||
by the resolver that cannot be accessed using the public internet servers.
|
||||
This is useful for company\-local data or private zones. Setup an
|
||||
authoritative server on a different host (or different port). Enter a config
|
||||
entry for unbound with
|
||||
.B stub\-addr:
|
||||
<ip address of host[@port]>.
|
||||
The unbound resolver can then access the data, without referring to the
|
||||
public internet for it.
|
||||
.P
|
||||
This setup allows DNSSEC signed zones to be served by that
|
||||
authoritative server, in which case a trusted key entry with the public key
|
||||
can be put in config, so that unbound can validate the data and set the AD
|
||||
bit on replies for the private zone (authoritative servers do not set the
|
||||
AD bit). This setup makes unbound capable of answering queries for the
|
||||
private zone, and can even set the AD bit ('authentic'), but the AA
|
||||
('authoritative') bit is not set on these replies.
|
||||
.TP
|
||||
.B name: \fI<domain name>
|
||||
Name of the stub zone.
|
||||
.It \fBstub-host:\fR <domain name>
|
||||
.TP
|
||||
.B stub\-host: \fI<domain name>
|
||||
Name of stub zone nameserver. Is itself resolved before it is used.
|
||||
.It \fBstub-addr:\fR <IP address>
|
||||
.TP
|
||||
.B stub\-addr: \fI<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
|
||||
.SS "Forward Zone Options"
|
||||
.LP
|
||||
There may be multiple
|
||||
.Ic forward-zone:
|
||||
.B 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
|
||||
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>
|
||||
.TP
|
||||
.B name: \fI<domain name>
|
||||
Name of the forward zone.
|
||||
.It \fBforward-host:\fR <domain name>
|
||||
.TP
|
||||
.B forward\-host: \fI<domain name>
|
||||
Name of server to forward to. Is itself resolved before it is used.
|
||||
.It \fBforward-addr:\fR <IP address>
|
||||
.TP
|
||||
.B forward\-addr: \fI<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 MEMORY CONTROL EXAMPLE
|
||||
.SH "MEMORY CONTROL EXAMPLE"
|
||||
In the example config settings below memory usage is reduced. Some service
|
||||
levels are lower, notable very large data and a high TCP load are no longer
|
||||
supported. Very large data and high TCP loads are exceptional for the DNS.
|
||||
DNSSEC validation is enabled, just add trust anchors.
|
||||
If you do not have to worry about programs using more than 1 meg of memory,
|
||||
the below example is not for you. Use the defaults to receive full service.
|
||||
.Pp
|
||||
.P
|
||||
.nf
|
||||
# example settings that reduce memory usage
|
||||
server:
|
||||
num-threads: 1
|
||||
outgoing-num-tcp: 1 # this limits TCP service, uses less buffers.
|
||||
incoming-num-tcp: 1
|
||||
outgoing-range: 1 # uses less memory, but less port randomness.
|
||||
msg-buffer-size: 8192 # note this limits service, 'no huge stuff'.
|
||||
msg-cache-size: 102400 # 100 Kb.
|
||||
msg-cache-slabs: 1
|
||||
rrset-cache-size: 102400 # 100 Kb.
|
||||
rrset-cache-slabs: 1
|
||||
infra-cache-numhosts: 200
|
||||
infra-cache-numlame: 10
|
||||
key-cache-size: 102400 # 100 Kb.
|
||||
key-cache-slabs: 1
|
||||
num-queries-per-thread: 30
|
||||
target-fetch-policy: "2 1 0 0 0 0"
|
||||
harden-large-queries: "yes"
|
||||
harden-short-bufsize: "yes"
|
||||
do-ip6: no # save a bit of memory if not used.
|
||||
num\-threads: 1
|
||||
outgoing\-num\-tcp: 1 # this limits TCP service, uses less buffers.
|
||||
incoming\-num\-tcp: 1
|
||||
outgoing\-range: 1 # uses less memory, but less port randomness.
|
||||
msg\-buffer\-size: 8192 # note this limits service, 'no huge stuff'.
|
||||
msg\-cache\-size: 102400 # 100 Kb.
|
||||
msg\-cache\-slabs: 1
|
||||
rrset\-cache\-size: 102400 # 100 Kb.
|
||||
rrset\-cache\-slabs: 1
|
||||
infra\-cache\-numhosts: 200
|
||||
infra\-cache\-numlame: 10
|
||||
key\-cache\-size: 102400 # 100 Kb.
|
||||
key\-cache\-slabs: 1
|
||||
num\-queries\-per\-thread: 30
|
||||
target\-fetch\-policy: "2 1 0 0 0 0"
|
||||
harden\-large\-queries: "yes"
|
||||
harden\-short\-bufsize: "yes"
|
||||
do\-ip6: no # save a bit of memory if not used.
|
||||
.fi
|
||||
.Sh FILES
|
||||
.Bl -tag -width indent
|
||||
.It Pa /etc/unbound
|
||||
.SH "FILES"
|
||||
.TP
|
||||
.I /etc/unbound
|
||||
default unbound working directory and default
|
||||
.Xr chroot 2
|
||||
\fIchroot\fR(2)
|
||||
location.
|
||||
.It Pa unbound.conf
|
||||
.TP
|
||||
.I unbound.conf
|
||||
unbound configuration file.
|
||||
.It Pa unbound.pid
|
||||
.TP
|
||||
.I unbound.pid
|
||||
default unbound pidfile with process ID of the running daemon.
|
||||
.It Pa unbound.log
|
||||
.TP
|
||||
.I unbound.log
|
||||
unbound log file. default is to log to
|
||||
.Xr syslog 3 .
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr unbound 8 ,
|
||||
.Xr unbound-checkconf 8 .
|
||||
.Sh AUTHORS
|
||||
.Ic Unbound
|
||||
\fIsyslog\fR(3).
|
||||
.SH "SEE ALSO"
|
||||
\fIunbound\fR(8),
|
||||
\fIunbound\-checkconf\fR(8).
|
||||
.SH "AUTHORS"
|
||||
.B Unbound
|
||||
was written by NLnet Labs. Please see CREDITS file
|
||||
in the distribution for further details.
|
||||
|
||||
@@ -103,7 +103,7 @@ donotq_insert(struct iter_donotq* dq, struct sockaddr_storage* addr,
|
||||
node->net = net;
|
||||
node->parent = NULL;
|
||||
if(!rbtree_insert(dq->tree, &node->node)) {
|
||||
verbose(VERB_DETAIL, "duplicate donotquery address ignored.");
|
||||
verbose(VERB_QUERY, "duplicate donotquery address ignored.");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
+2
-2
@@ -228,8 +228,8 @@ read_forwards(struct iter_forwards* fwd, struct config_file* cfg)
|
||||
return 0;
|
||||
if(!forwards_insert(fwd, LDNS_RR_CLASS_IN, dp))
|
||||
return 0;
|
||||
verbose(VERB_DETAIL, "Forward zone server list:");
|
||||
delegpt_log(VERB_DETAIL, dp);
|
||||
verbose(VERB_QUERY, "Forward zone server list:");
|
||||
delegpt_log(VERB_QUERY, dp);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ read_stubs(struct iter_hints* hints, struct config_file* cfg)
|
||||
return 0;
|
||||
if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp))
|
||||
return 0;
|
||||
delegpt_log(VERB_DETAIL, dp);
|
||||
delegpt_log(VERB_QUERY, dp);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -314,7 +314,7 @@ read_root_hints(struct iter_hints* hints, char* fname)
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
verbose(VERB_DETAIL, "Reading root hints from %s", fname);
|
||||
verbose(VERB_QUERY, "Reading root hints from %s", fname);
|
||||
while(!feof(f)) {
|
||||
status = ldns_rr_new_frm_fp_l(&rr, f,
|
||||
&default_ttl, &origin, &prev_rr, &lineno);
|
||||
@@ -391,7 +391,7 @@ read_root_hints(struct iter_hints* hints, char* fname)
|
||||
if(!hints_insert(hints, c, dp)) {
|
||||
return 0;
|
||||
}
|
||||
delegpt_log(VERB_DETAIL, dp);
|
||||
delegpt_log(VERB_QUERY, dp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -403,7 +403,12 @@ read_root_hints_list(struct iter_hints* hints, struct config_file* cfg)
|
||||
for(p = cfg->root_hints; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
if(p->str && p->str[0]) {
|
||||
if(!read_root_hints(hints, p->str))
|
||||
char* f = p->str;
|
||||
if(cfg->chrootdir && cfg->chrootdir[0] &&
|
||||
strncmp(p->str, cfg->chrootdir,
|
||||
strlen(cfg->chrootdir)) == 0)
|
||||
f += strlen(cfg->chrootdir);
|
||||
if(!read_root_hints(hints, f))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -434,7 +439,6 @@ hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg)
|
||||
return 0;
|
||||
if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp))
|
||||
return 0;
|
||||
delegpt_log(VERB_DETAIL, dp);
|
||||
}
|
||||
|
||||
init_parents(hints);
|
||||
|
||||
@@ -60,11 +60,11 @@ 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
|
||||
if(verbosity >= VERB_QUERY
|
||||
&& (*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,
|
||||
log_nametypeclass(VERB_QUERY, str, buf,
|
||||
(*rrset)->type, ntohs((*rrset)->rrset_class));
|
||||
}
|
||||
if(prev)
|
||||
|
||||
@@ -102,7 +102,7 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
|
||||
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",
|
||||
verbose(VERB_QUERY, "target fetch policy for level %d is %d",
|
||||
i, iter_env->target_fetch_policy[i]);
|
||||
|
||||
if(!iter_env->hints)
|
||||
@@ -331,7 +331,7 @@ iter_mark_cycle_targets(struct module_qstate* qstate, struct delegpt* dp)
|
||||
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 "
|
||||
log_nametypeclass(VERB_QUERY, "skipping target due "
|
||||
"to dependency cycle (harden-glue: no may "
|
||||
"fix some of the cycles)",
|
||||
ns->name, LDNS_RR_TYPE_A,
|
||||
|
||||
+28
-25
@@ -80,7 +80,7 @@ void
|
||||
iter_deinit(struct module_env* env, int id)
|
||||
{
|
||||
struct iter_env* iter_env;
|
||||
if(!env || !env->modinfo || !env->modinfo[id])
|
||||
if(!env || !env->modinfo[id])
|
||||
return;
|
||||
iter_env = (struct iter_env*)env->modinfo[id];
|
||||
free(iter_env->target_fetch_policy);
|
||||
@@ -188,7 +188,8 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
|
||||
/* not interested */
|
||||
verbose(VERB_ALGO, "subq error, but not interested");
|
||||
log_query_info(VERB_ALGO, "superq", &super->qinfo);
|
||||
delegpt_log(VERB_ALGO, super_iq->dp);
|
||||
if(super_iq->dp)
|
||||
delegpt_log(VERB_ALGO, super_iq->dp);
|
||||
log_assert(0);
|
||||
return;
|
||||
}
|
||||
@@ -215,7 +216,7 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
|
||||
static int
|
||||
error_response(struct module_qstate* qstate, int id, int rcode)
|
||||
{
|
||||
verbose(VERB_DETAIL, "return error response %s",
|
||||
verbose(VERB_QUERY, "return error response %s",
|
||||
ldns_lookup_by_id(ldns_rcodes, rcode)?
|
||||
ldns_lookup_by_id(ldns_rcodes, rcode)->name:"??");
|
||||
qstate->return_rcode = rcode;
|
||||
@@ -502,7 +503,7 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
{
|
||||
struct delegpt* dp;
|
||||
struct module_qstate* subq;
|
||||
verbose(VERB_ALGO, "priming . NS %s",
|
||||
verbose(VERB_DETAIL, "priming . %s NS",
|
||||
ldns_lookup_by_id(ldns_rr_classes, (int)qclass)?
|
||||
ldns_lookup_by_id(ldns_rr_classes, (int)qclass)->name:"??");
|
||||
dp = hints_lookup_root(ie->hints, qclass);
|
||||
@@ -661,7 +662,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
/* We enforce a maximum number of query restarts. This is primarily a
|
||||
* cheap way to prevent CNAME loops. */
|
||||
if(iq->query_restart_count > MAX_RESTART_COUNT) {
|
||||
verbose(VERB_DETAIL, "request has exceeded the maximum number"
|
||||
verbose(VERB_QUERY, "request has exceeded the maximum number"
|
||||
" of query restarts with %d", iq->query_restart_count);
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
@@ -672,7 +673,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* of work required to answer a given query. */
|
||||
verbose(VERB_ALGO, "request has dependency depth of %d", iq->depth);
|
||||
if(iq->depth > ie->max_dependency_depth) {
|
||||
verbose(VERB_DETAIL, "request has exceeded the maximum "
|
||||
verbose(VERB_QUERY, "request has exceeded the maximum "
|
||||
"dependency depth with depth of %d", iq->depth);
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
@@ -791,7 +792,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
if(iter_dp_is_useless(qstate, iq->dp)) {
|
||||
if(dname_is_root(iq->dp->name)) {
|
||||
/* use safety belt */
|
||||
verbose(VERB_DETAIL, "Cache has root NS but "
|
||||
verbose(VERB_QUERY, "Cache has root NS but "
|
||||
"no addresses. Fallback to the safety belt.");
|
||||
iq->dp = hints_lookup_root(ie->hints,
|
||||
iq->qchase.qclass);
|
||||
@@ -860,7 +861,7 @@ static int
|
||||
processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
struct iter_env* ie, int id)
|
||||
{
|
||||
log_query_info(VERB_DETAIL, "resolving (init part 2): ",
|
||||
log_query_info(VERB_QUERY, "resolving (init part 2): ",
|
||||
&qstate->qinfo);
|
||||
|
||||
/* Check to see if we need to prime a stub zone. */
|
||||
@@ -887,7 +888,7 @@ processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
static int
|
||||
processInitRequest3(struct module_qstate* qstate, struct iter_qstate* iq)
|
||||
{
|
||||
log_query_info(VERB_DETAIL, "resolving (init part 3): ",
|
||||
log_query_info(VERB_QUERY, "resolving (init part 3): ",
|
||||
&qstate->qinfo);
|
||||
/* If the RD flag wasn't set, then we just finish with the
|
||||
* cached referral as the response. */
|
||||
@@ -937,7 +938,7 @@ generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
subiq->dp = delegpt_copy(iq->dp, subq->region);
|
||||
}
|
||||
}
|
||||
log_nametypeclass(VERB_DETAIL, "new target", name, qtype, qclass);
|
||||
log_nametypeclass(VERB_QUERY, "new target", name, qtype, qclass);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1051,7 +1052,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* needs to send a query to. That is, at least one per referral,
|
||||
* more if some targets timeout or return throwaway answers. */
|
||||
|
||||
log_query_info(VERB_DETAIL, "processQueryTargets:", &qstate->qinfo);
|
||||
log_query_info(VERB_QUERY, "processQueryTargets:", &qstate->qinfo);
|
||||
verbose(VERB_ALGO, "processQueryTargets: targetqueries %d, "
|
||||
"currentqueries %d", iq->num_target_queries,
|
||||
iq->num_current_queries);
|
||||
@@ -1060,7 +1061,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
/* Make sure that we haven't run away */
|
||||
/* FIXME: is this check even necessary? */
|
||||
if(iq->referral_count > MAX_REFERRAL_COUNT) {
|
||||
verbose(VERB_DETAIL, "request has exceeded the maximum "
|
||||
verbose(VERB_QUERY, "request has exceeded the maximum "
|
||||
"number of referrrals with %d", iq->referral_count);
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
@@ -1068,7 +1069,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
/* Make sure we have a delegation point, otherwise priming failed
|
||||
* or another failure occurred */
|
||||
if(!iq->dp) {
|
||||
verbose(VERB_DETAIL, "Failed to get a delegation, giving up");
|
||||
verbose(VERB_QUERY, "Failed to get a delegation, giving up");
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
delegpt_log(VERB_ALGO, iq->dp);
|
||||
@@ -1136,7 +1137,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
/* Since a target query might have been made, we
|
||||
* need to check again. */
|
||||
if(iq->num_target_queries == 0) {
|
||||
verbose(VERB_DETAIL, "out of query targets -- "
|
||||
verbose(VERB_QUERY, "out of query targets -- "
|
||||
"returning SERVFAIL");
|
||||
/* fail -- no more targets, no more hope
|
||||
* of targets, no hope of a response. */
|
||||
@@ -1163,8 +1164,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
}
|
||||
|
||||
/* We have a valid target. */
|
||||
log_query_info(VERB_DETAIL, "sending query:", &iq->qchase);
|
||||
log_name_addr(VERB_DETAIL, "sending to target:", iq->dp->name,
|
||||
log_query_info(VERB_QUERY, "sending query:", &iq->qchase);
|
||||
log_name_addr(VERB_QUERY, "sending to target:", iq->dp->name,
|
||||
&target->addr, target->addrlen);
|
||||
log_assert(fptr_whitelist_modenv_send_query(qstate->env->send_query));
|
||||
outq = (*qstate->env->send_query)(
|
||||
@@ -1173,8 +1174,9 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
iq->chase_flags, EDNS_DO|BIT_CD,
|
||||
&target->addr, target->addrlen, qstate);
|
||||
if(!outq) {
|
||||
log_err("error sending query to auth server; skip this address");
|
||||
log_addr(0, "error for address:",
|
||||
verbose(VERB_OPS, "error sending query to auth server; "
|
||||
"skip this address");
|
||||
log_addr(VERB_OPS, "error for address:",
|
||||
&target->addr, target->addrlen);
|
||||
return next_state(iq, QUERYTARGETS_STATE);
|
||||
}
|
||||
@@ -1527,7 +1529,7 @@ static int
|
||||
processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
int id)
|
||||
{
|
||||
log_query_info(VERB_DETAIL, "finishing processing for",
|
||||
log_query_info(VERB_QUERY, "finishing processing for",
|
||||
&qstate->qinfo);
|
||||
|
||||
if(!iq->response) {
|
||||
@@ -1726,7 +1728,8 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
iq->response = dns_alloc_msg(pkt, prs, qstate->region);
|
||||
if(!iq->response)
|
||||
goto handle_it;
|
||||
log_name_addr(VERB_DETAIL, "incoming packet from target:", iq->dp->name,
|
||||
log_query_info(VERB_DETAIL, "reponse for", &qstate->qinfo);
|
||||
log_name_addr(VERB_DETAIL, "reply from", iq->dp->name,
|
||||
&qstate->reply->addr, qstate->reply->addrlen);
|
||||
if(verbosity >= VERB_ALGO)
|
||||
log_dns_msg("incoming scrubbed packet:", &iq->response->qinfo,
|
||||
@@ -1743,12 +1746,12 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int id,
|
||||
{
|
||||
struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
|
||||
struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
|
||||
verbose(VERB_DETAIL, "iterator[module %d] operate: extstate:%s event:%s",
|
||||
verbose(VERB_QUERY, "iterator[module %d] operate: extstate:%s event:%s",
|
||||
id, strextstate(qstate->ext_state[id]), strmodulevent(event));
|
||||
if(iq) log_query_info(VERB_DETAIL, "iterator operate: query",
|
||||
if(iq) log_query_info(VERB_QUERY, "iterator operate: query",
|
||||
&qstate->qinfo);
|
||||
if(iq && qstate->qinfo.qname != iq->qchase.qname)
|
||||
log_query_info(VERB_DETAIL, "iterator operate: chased to",
|
||||
log_query_info(VERB_QUERY, "iterator operate: chased to",
|
||||
&iq->qchase);
|
||||
|
||||
/* perform iterator state machine */
|
||||
@@ -1762,11 +1765,11 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int id,
|
||||
process_request(qstate, iq, ie, id);
|
||||
return;
|
||||
}
|
||||
if(event == module_event_pass) {
|
||||
if(iq && event == module_event_pass) {
|
||||
iter_handle(qstate, iq, ie, id);
|
||||
return;
|
||||
}
|
||||
if(outbound) {
|
||||
if(iq && outbound) {
|
||||
process_response(qstate, iq, ie, id, outbound, event);
|
||||
return;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* libunbound/context.c - validating context for unbound internal use
|
||||
*
|
||||
* 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 validator context structure.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "libunbound/context.h"
|
||||
#include "util/module.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
#include "services/modstack.h"
|
||||
#include "services/localzone.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
|
||||
int
|
||||
context_finalize(struct ub_ctx* ctx)
|
||||
{
|
||||
struct config_file* cfg = ctx->env->cfg;
|
||||
verbosity = cfg->verbosity;
|
||||
if(ctx->logfile_override)
|
||||
log_file(ctx->log_out);
|
||||
else log_init(cfg->logfile, cfg->use_syslog, NULL);
|
||||
config_apply(cfg);
|
||||
if(!modstack_setup(&ctx->mods, cfg->module_conf, ctx->env))
|
||||
return UB_INITFAIL;
|
||||
ctx->local_zones = local_zones_create();
|
||||
if(!ctx->local_zones)
|
||||
return UB_NOMEM;
|
||||
if(!local_zones_apply_cfg(ctx->local_zones, cfg))
|
||||
return UB_INITFAIL;
|
||||
if(!ctx->env->msg_cache ||
|
||||
cfg->msg_cache_size != slabhash_get_size(ctx->env->msg_cache) ||
|
||||
cfg->msg_cache_slabs != ctx->env->msg_cache->size) {
|
||||
slabhash_delete(ctx->env->msg_cache);
|
||||
ctx->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(!ctx->env->msg_cache)
|
||||
return UB_NOMEM;
|
||||
}
|
||||
ctx->env->rrset_cache = rrset_cache_adjust(ctx->env->rrset_cache,
|
||||
ctx->env->cfg, ctx->env->alloc);
|
||||
if(!ctx->env->rrset_cache)
|
||||
return UB_NOMEM;
|
||||
ctx->env->infra_cache = infra_adjust(ctx->env->infra_cache, cfg);
|
||||
if(!ctx->env->infra_cache)
|
||||
return UB_NOMEM;
|
||||
ctx->finalized = 1;
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int context_query_cmp(const void* a, const void* b)
|
||||
{
|
||||
if( *(int*)a < *(int*)b )
|
||||
return -1;
|
||||
if( *(int*)a > *(int*)b )
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
context_query_delete(struct ctx_query* q)
|
||||
{
|
||||
if(!q) return;
|
||||
ub_resolve_free(q->res);
|
||||
free(q->msg);
|
||||
free(q);
|
||||
}
|
||||
|
||||
/** How many times to try to find an unused query-id-number for async */
|
||||
#define NUM_ID_TRIES 100000
|
||||
/** find next useful id number of 0 on error */
|
||||
static int
|
||||
find_id(struct ub_ctx* ctx, int* id)
|
||||
{
|
||||
size_t tries = 0;
|
||||
ctx->next_querynum++;
|
||||
while(rbtree_search(&ctx->queries, &ctx->next_querynum)) {
|
||||
ctx->next_querynum++; /* numerical wraparound is fine */
|
||||
if(tries++ > NUM_ID_TRIES)
|
||||
return 0;
|
||||
}
|
||||
*id = ctx->next_querynum;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct ctx_query*
|
||||
context_new(struct ub_ctx* ctx, char* name, int rrtype, int rrclass,
|
||||
ub_callback_t cb, void* cbarg)
|
||||
{
|
||||
struct ctx_query* q = (struct ctx_query*)calloc(1, sizeof(*q));
|
||||
if(!q) return NULL;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(!find_id(ctx, &q->querynum)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
q->node.key = &q->querynum;
|
||||
q->async = (cb != NULL);
|
||||
q->cb = cb;
|
||||
q->cb_arg = cbarg;
|
||||
q->res = (struct ub_result*)calloc(1, sizeof(*q->res));
|
||||
if(!q->res) {
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
q->res->qname = strdup(name);
|
||||
if(!q->res->qname) {
|
||||
free(q->res);
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
q->res->qtype = rrtype;
|
||||
q->res->qclass = rrclass;
|
||||
|
||||
/* add to query list */
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(q->async)
|
||||
ctx->num_async ++;
|
||||
(void)rbtree_insert(&ctx->queries, &q->node);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return q;
|
||||
}
|
||||
|
||||
struct alloc_cache*
|
||||
context_obtain_alloc(struct ub_ctx* ctx, int locking)
|
||||
{
|
||||
struct alloc_cache* a;
|
||||
int tnum = 0;
|
||||
if(locking) {
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
}
|
||||
a = ctx->alloc_list;
|
||||
if(a)
|
||||
ctx->alloc_list = a->super; /* snip off list */
|
||||
else tnum = ctx->thr_next_num++;
|
||||
if(locking) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
if(a) {
|
||||
a->super = &ctx->superalloc;
|
||||
return a;
|
||||
}
|
||||
a = (struct alloc_cache*)calloc(1, sizeof(*a));
|
||||
if(!a)
|
||||
return NULL;
|
||||
alloc_init(a, &ctx->superalloc, tnum);
|
||||
return a;
|
||||
}
|
||||
|
||||
void
|
||||
context_release_alloc(struct ub_ctx* ctx, struct alloc_cache* alloc,
|
||||
int locking)
|
||||
{
|
||||
if(!ctx || !alloc)
|
||||
return;
|
||||
if(locking) {
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
}
|
||||
alloc->super = ctx->alloc_list;
|
||||
ctx->alloc_list = alloc;
|
||||
if(locking) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
context_serialize_new_query(struct ctx_query* q, uint32_t* len)
|
||||
{
|
||||
/* format for new query is
|
||||
* o uint32 cmd
|
||||
* o uint32 id
|
||||
* o uint32 type
|
||||
* o uint32 class
|
||||
* o rest queryname (string)
|
||||
*/
|
||||
uint8_t* p;
|
||||
size_t slen = strlen(q->res->qname) + 1/*end of string*/;
|
||||
*len = sizeof(uint32_t)*4 + slen;
|
||||
p = (uint8_t*)malloc(*len);
|
||||
if(!p) return NULL;
|
||||
ldns_write_uint32(p, UB_LIBCMD_NEWQUERY);
|
||||
ldns_write_uint32(p+sizeof(uint32_t), (uint32_t)q->querynum);
|
||||
ldns_write_uint32(p+2*sizeof(uint32_t), (uint32_t)q->res->qtype);
|
||||
ldns_write_uint32(p+3*sizeof(uint32_t), (uint32_t)q->res->qclass);
|
||||
memmove(p+4*sizeof(uint32_t), q->res->qname, slen);
|
||||
return p;
|
||||
}
|
||||
|
||||
struct ctx_query*
|
||||
context_deserialize_new_query(struct ub_ctx* ctx, uint8_t* p, uint32_t len)
|
||||
{
|
||||
struct ctx_query* q = (struct ctx_query*)calloc(1, sizeof(*q));
|
||||
if(!q) return NULL;
|
||||
if(len < 4*sizeof(uint32_t)+1) {
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
log_assert( ldns_read_uint32(p) == UB_LIBCMD_NEWQUERY);
|
||||
q->querynum = (int)ldns_read_uint32(p+sizeof(uint32_t));
|
||||
q->node.key = &q->querynum;
|
||||
q->async = 1;
|
||||
q->res = (struct ub_result*)calloc(1, sizeof(*q->res));
|
||||
if(!q->res) {
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
q->res->qtype = (int)ldns_read_uint32(p+2*sizeof(uint32_t));
|
||||
q->res->qclass = (int)ldns_read_uint32(p+3*sizeof(uint32_t));
|
||||
q->res->qname = strdup((char*)(p+4*sizeof(uint32_t)));
|
||||
if(!q->res->qname) {
|
||||
free(q->res);
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** add to query list */
|
||||
ctx->num_async++;
|
||||
(void)rbtree_insert(&ctx->queries, &q->node);
|
||||
return q;
|
||||
}
|
||||
|
||||
struct ctx_query*
|
||||
context_lookup_new_query(struct ub_ctx* ctx, uint8_t* p, uint32_t len)
|
||||
{
|
||||
struct ctx_query* q;
|
||||
int querynum;
|
||||
if(len < 4*sizeof(uint32_t)+1) {
|
||||
return NULL;
|
||||
}
|
||||
log_assert( ldns_read_uint32(p) == UB_LIBCMD_NEWQUERY);
|
||||
querynum = (int)ldns_read_uint32(p+sizeof(uint32_t));
|
||||
q = (struct ctx_query*)rbtree_search(&ctx->queries, &querynum);
|
||||
if(!q) {
|
||||
return NULL;
|
||||
}
|
||||
log_assert(q->async);
|
||||
return q;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
context_serialize_answer(struct ctx_query* q, int err, ldns_buffer* pkt,
|
||||
uint32_t* len)
|
||||
{
|
||||
/* answer format
|
||||
* o uint32 cmd
|
||||
* o uint32 id
|
||||
* o uint32 error_code
|
||||
* o uint32 msg_security
|
||||
* o the remainder is the answer msg from resolver lookup.
|
||||
* remainder can be length 0.
|
||||
*/
|
||||
size_t pkt_len = pkt?ldns_buffer_remaining(pkt):0;
|
||||
uint8_t* p;
|
||||
*len = sizeof(uint32_t)*4 + pkt_len;
|
||||
p = (uint8_t*)malloc(*len);
|
||||
if(!p) return NULL;
|
||||
ldns_write_uint32(p, UB_LIBCMD_ANSWER);
|
||||
ldns_write_uint32(p+sizeof(uint32_t), (uint32_t)q->querynum);
|
||||
ldns_write_uint32(p+2*sizeof(uint32_t), (uint32_t)err);
|
||||
ldns_write_uint32(p+3*sizeof(uint32_t), (uint32_t)q->msg_security);
|
||||
if(pkt_len > 0)
|
||||
memmove(p+4*sizeof(uint32_t), ldns_buffer_begin(pkt), pkt_len);
|
||||
return p;
|
||||
}
|
||||
|
||||
struct ctx_query*
|
||||
context_deserialize_answer(struct ub_ctx* ctx,
|
||||
uint8_t* p, uint32_t len, int* err)
|
||||
{
|
||||
struct ctx_query* q = NULL ;
|
||||
int id;
|
||||
if(len < 4*sizeof(uint32_t)) return NULL;
|
||||
log_assert( ldns_read_uint32(p) == UB_LIBCMD_ANSWER);
|
||||
id = (int)ldns_read_uint32(p+sizeof(uint32_t));
|
||||
q = (struct ctx_query*)rbtree_search(&ctx->queries, &id);
|
||||
if(!q) return NULL;
|
||||
*err = (int)ldns_read_uint32(p+2*sizeof(uint32_t));
|
||||
q->msg_security = ldns_read_uint32(p+3*sizeof(uint32_t));
|
||||
if(len > 4*sizeof(uint32_t)) {
|
||||
q->msg_len = len - 4*sizeof(uint32_t);
|
||||
q->msg = (uint8_t*)memdup(p+4*sizeof(uint32_t), q->msg_len);
|
||||
if(!q->msg) {
|
||||
/* pass malloc failure to the user callback */
|
||||
q->msg_len = 0;
|
||||
*err = UB_NOMEM;
|
||||
return q;
|
||||
}
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
context_serialize_cancel(struct ctx_query* q, uint32_t* len)
|
||||
{
|
||||
/* format of cancel:
|
||||
* o uint32 cmd
|
||||
* o uint32 async-id */
|
||||
uint8_t* p = (uint8_t*)malloc(2*sizeof(uint32_t));
|
||||
if(!p) return NULL;
|
||||
*len = 2*sizeof(uint32_t);
|
||||
ldns_write_uint32(p, UB_LIBCMD_CANCEL);
|
||||
ldns_write_uint32(p+sizeof(uint32_t), (uint32_t)q->querynum);
|
||||
return p;
|
||||
}
|
||||
|
||||
struct ctx_query* context_deserialize_cancel(struct ub_ctx* ctx,
|
||||
uint8_t* p, uint32_t len)
|
||||
{
|
||||
struct ctx_query* q;
|
||||
int id;
|
||||
if(len != 2*sizeof(uint32_t)) return NULL;
|
||||
log_assert( ldns_read_uint32(p) == UB_LIBCMD_CANCEL);
|
||||
id = (int)ldns_read_uint32(p+sizeof(uint32_t));
|
||||
q = (struct ctx_query*)rbtree_search(&ctx->queries, &id);
|
||||
return q;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
context_serialize_quit(uint32_t* len)
|
||||
{
|
||||
uint8_t* p = (uint8_t*)malloc(sizeof(uint32_t));
|
||||
if(!p)
|
||||
return NULL;
|
||||
*len = sizeof(uint32_t);
|
||||
ldns_write_uint32(p, UB_LIBCMD_QUIT);
|
||||
return p;
|
||||
}
|
||||
|
||||
enum ub_ctx_cmd context_serial_getcmd(uint8_t* p, uint32_t len)
|
||||
{
|
||||
uint32_t v;
|
||||
if((size_t)len < sizeof(v))
|
||||
return UB_LIBCMD_QUIT;
|
||||
v = ldns_read_uint32(p);
|
||||
return v;
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* libunbound/context.h - validating context for unbound internal use
|
||||
*
|
||||
* 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 validator context structure.
|
||||
*/
|
||||
#ifndef LIBUNBOUND_CONTEXT_H
|
||||
#define LIBUNBOUND_CONTEXT_H
|
||||
#include "util/locks.h"
|
||||
#include "util/alloc.h"
|
||||
#include "util/rbtree.h"
|
||||
#include "services/modstack.h"
|
||||
#include "libunbound/unbound.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
struct libworker;
|
||||
|
||||
/**
|
||||
* The context structure
|
||||
*
|
||||
* Contains two pipes for async service
|
||||
* qq : write queries to the async service pid/tid.
|
||||
* rr : read results from the async service pid/tid.
|
||||
*/
|
||||
struct ub_ctx {
|
||||
/* --- pipes --- */
|
||||
/** mutex on query write pipe */
|
||||
lock_basic_t qqpipe_lock;
|
||||
/** the query write pipe, [0] read from, [1] write on */
|
||||
int qqpipe[2];
|
||||
/** mutex on result read pipe */
|
||||
lock_basic_t rrpipe_lock;
|
||||
/** the result read pipe, [0] read from, [1] write on */
|
||||
int rrpipe[2];
|
||||
|
||||
/* --- shared data --- */
|
||||
/** mutex for access to env.cfg, finalized and dothread */
|
||||
lock_basic_t cfglock;
|
||||
/**
|
||||
* The context has been finalized
|
||||
* This is after config when the first resolve is done.
|
||||
* The modules are inited (module-init()) and shared caches created.
|
||||
*/
|
||||
int finalized;
|
||||
|
||||
/** is bg worker created yet ? */
|
||||
int created_bg;
|
||||
/** pid of bg worker process */
|
||||
pid_t bg_pid;
|
||||
/** tid of bg worker thread */
|
||||
ub_thread_t bg_tid;
|
||||
|
||||
/** do threading (instead of forking) for async resolution */
|
||||
int dothread;
|
||||
/** next thread number for new threads */
|
||||
int thr_next_num;
|
||||
/** if logfile is overriden */
|
||||
int logfile_override;
|
||||
/** what logfile to use instead */
|
||||
FILE* log_out;
|
||||
/**
|
||||
* List of alloc-cache-id points per threadnum for notinuse threads.
|
||||
* Simply the entire struct alloc_cache with the 'super' member used
|
||||
* to link a simply linked list. Reset super member to the superalloc
|
||||
* before use.
|
||||
*/
|
||||
struct alloc_cache* alloc_list;
|
||||
|
||||
/** shared caches, and so on */
|
||||
struct alloc_cache superalloc;
|
||||
/** module env master value */
|
||||
struct module_env* env;
|
||||
/** module stack */
|
||||
struct module_stack mods;
|
||||
/** local authority zones */
|
||||
struct local_zones* local_zones;
|
||||
/** random state used to seed new random state structures */
|
||||
struct ub_randstate* seed_rnd;
|
||||
|
||||
/** next query number (to try) to use */
|
||||
int next_querynum;
|
||||
/** number of async queries outstanding */
|
||||
size_t num_async;
|
||||
/**
|
||||
* Tree of outstanding queries. Indexed by querynum
|
||||
* Used when results come in for async to lookup.
|
||||
* Used when cancel is done for lookup (and delete).
|
||||
* Used to see if querynum is free for use.
|
||||
* Content of type ctx_query.
|
||||
*/
|
||||
rbtree_t queries;
|
||||
};
|
||||
|
||||
/**
|
||||
* The queries outstanding for the libunbound resolver.
|
||||
* These are outstanding for async resolution.
|
||||
* But also, outstanding for sync resolution by one of the threads that
|
||||
* has joined the threadpool.
|
||||
*/
|
||||
struct ctx_query {
|
||||
/** node in rbtree, must be first entry, key is ptr to the querynum */
|
||||
struct rbnode_t node;
|
||||
/** query id number, key for node */
|
||||
int querynum;
|
||||
/** was this an async query? */
|
||||
int async;
|
||||
/** was this query cancelled (for bg worker) */
|
||||
int cancelled;
|
||||
|
||||
/** for async query, the callback function */
|
||||
ub_callback_t cb;
|
||||
/** for async query, the callback user arg */
|
||||
void* cb_arg;
|
||||
|
||||
/** answer message, result from resolver lookup. */
|
||||
uint8_t* msg;
|
||||
/** resulting message length. */
|
||||
size_t msg_len;
|
||||
/** validation status on security */
|
||||
enum sec_status msg_security;
|
||||
/** store libworker that is handling this query */
|
||||
struct libworker* w;
|
||||
|
||||
/** result structure, also contains original query, type, class.
|
||||
* malloced ptr ready to hand to the client. */
|
||||
struct ub_result* res;
|
||||
};
|
||||
|
||||
/**
|
||||
* The error constants
|
||||
*/
|
||||
enum ub_ctx_err {
|
||||
/** no error */
|
||||
UB_NOERROR = 0,
|
||||
/** socket operation. Set to -1, so that if an error from _fd() is
|
||||
* passed (-1) it gives a socket error. */
|
||||
UB_SOCKET = -1,
|
||||
/** alloc failure */
|
||||
UB_NOMEM = -2,
|
||||
/** syntax error */
|
||||
UB_SYNTAX = -3,
|
||||
/** DNS service failed */
|
||||
UB_SERVFAIL = -4,
|
||||
/** fork() failed */
|
||||
UB_FORKFAIL = -5,
|
||||
/** cfg change after finalize() */
|
||||
UB_AFTERFINAL = -6,
|
||||
/** initialization failed (bad settings) */
|
||||
UB_INITFAIL = -7,
|
||||
/** error in pipe communication with async bg worker */
|
||||
UB_PIPE = -8,
|
||||
/** error reading from file (resolv.conf) */
|
||||
UB_READFILE = -9
|
||||
};
|
||||
|
||||
/**
|
||||
* Command codes for libunbound pipe.
|
||||
*
|
||||
* Serialization looks like this:
|
||||
* o length (of remainder) uint32.
|
||||
* o uint32 command code.
|
||||
* o per command format.
|
||||
*/
|
||||
enum ub_ctx_cmd {
|
||||
/** QUIT */
|
||||
UB_LIBCMD_QUIT = 0,
|
||||
/** New query, sent to bg worker */
|
||||
UB_LIBCMD_NEWQUERY,
|
||||
/** Cancel query, sent to bg worker */
|
||||
UB_LIBCMD_CANCEL,
|
||||
/** Query result, originates from bg worker */
|
||||
UB_LIBCMD_ANSWER
|
||||
};
|
||||
|
||||
/**
|
||||
* finalize a context.
|
||||
* @param ctx: context to finalize. creates shared data.
|
||||
* @return 0 if OK, or errcode.
|
||||
*/
|
||||
int context_finalize(struct ub_ctx* ctx);
|
||||
|
||||
/** compare two ctx_query elements */
|
||||
int context_query_cmp(const void* a, const void* b);
|
||||
|
||||
/**
|
||||
* delete context query
|
||||
* @param q: query to delete, including message packet and prealloc result
|
||||
*/
|
||||
void context_query_delete(struct ctx_query* q);
|
||||
|
||||
/**
|
||||
* Create new query in context, add to querynum list.
|
||||
* @param ctx: context
|
||||
* @param name: query name
|
||||
* @param rrtype: type
|
||||
* @param rrclass: class
|
||||
* @param cb: callback for async, or NULL for sync.
|
||||
* @param cbarg: user arg for async queries.
|
||||
* @return new ctx_query or NULL for malloc failure.
|
||||
*/
|
||||
struct ctx_query* context_new(struct ub_ctx* ctx, char* name, int rrtype,
|
||||
int rrclass, ub_callback_t cb, void* cbarg);
|
||||
|
||||
/**
|
||||
* Get a new alloc. Creates a new one or uses a cached one.
|
||||
* @param ctx: context
|
||||
* @param locking: if true, cfglock is locked while getting alloc.
|
||||
* @return an alloc, or NULL on mem error.
|
||||
*/
|
||||
struct alloc_cache* context_obtain_alloc(struct ub_ctx* ctx, int locking);
|
||||
|
||||
/**
|
||||
* Release an alloc. Puts it into the cache.
|
||||
* @param ctx: context
|
||||
* @param locking: if true, cfglock is locked while releasing alloc.
|
||||
* @param alloc: alloc to relinquish.
|
||||
*/
|
||||
void context_release_alloc(struct ub_ctx* ctx, struct alloc_cache* alloc,
|
||||
int locking);
|
||||
|
||||
/**
|
||||
* Serialize a context query that questions data.
|
||||
* This serializes the query name, type, ...
|
||||
* As well as command code 'new_query'.
|
||||
* @param q: context query
|
||||
* @param len: the length of the allocation is returned.
|
||||
* @return: an alloc, or NULL on mem error.
|
||||
*/
|
||||
uint8_t* context_serialize_new_query(struct ctx_query* q, uint32_t* len);
|
||||
|
||||
/**
|
||||
* Serialize a context_query result to hand back to user.
|
||||
* This serializes the query name, type, ..., and result.
|
||||
* As well as command code 'answer'.
|
||||
* @param q: context query
|
||||
* @param err: error code to pass to client.
|
||||
* @param pkt: the packet to add, can be NULL.
|
||||
* @param len: the length of the allocation is returned.
|
||||
* @return: an alloc, or NULL on mem error.
|
||||
*/
|
||||
uint8_t* context_serialize_answer(struct ctx_query* q, int err,
|
||||
ldns_buffer* pkt, uint32_t* len);
|
||||
|
||||
/**
|
||||
* Serialize a query cancellation. Serializes query async id
|
||||
* as well as command code 'cancel'
|
||||
* @param q: context query
|
||||
* @param len: the length of the allocation is returned.
|
||||
* @return: an alloc, or NULL on mem error.
|
||||
*/
|
||||
uint8_t* context_serialize_cancel(struct ctx_query* q, uint32_t* len);
|
||||
|
||||
/**
|
||||
* Serialize a 'quit' command.
|
||||
* @param len: the length of the allocation is returned.
|
||||
* @return: an alloc, or NULL on mem error.
|
||||
*/
|
||||
uint8_t* context_serialize_quit(uint32_t* len);
|
||||
|
||||
/**
|
||||
* Obtain command code from serialized buffer
|
||||
* @param p: buffer serialized.
|
||||
* @param len: length of buffer.
|
||||
* @return command code or QUIT on error.
|
||||
*/
|
||||
enum ub_ctx_cmd context_serial_getcmd(uint8_t* p, uint32_t len);
|
||||
|
||||
/**
|
||||
* Lookup query from new_query buffer.
|
||||
* @param ctx: context
|
||||
* @param p: buffer serialized.
|
||||
* @param len: length of buffer.
|
||||
* @return looked up ctx_query or NULL for malloc failure.
|
||||
*/
|
||||
struct ctx_query* context_lookup_new_query(struct ub_ctx* ctx,
|
||||
uint8_t* p, uint32_t len);
|
||||
|
||||
/**
|
||||
* Deserialize a new_query buffer.
|
||||
* @param ctx: context
|
||||
* @param p: buffer serialized.
|
||||
* @param len: length of buffer.
|
||||
* @return new ctx_query or NULL for malloc failure.
|
||||
*/
|
||||
struct ctx_query* context_deserialize_new_query(struct ub_ctx* ctx,
|
||||
uint8_t* p, uint32_t len);
|
||||
|
||||
/**
|
||||
* Deserialize an answer buffer.
|
||||
* @param ctx: context
|
||||
* @param p: buffer serialized.
|
||||
* @param len: length of buffer.
|
||||
* @param err: error code to be returned to client is passed.
|
||||
* @return ctx_query with answer added or NULL for malloc failure.
|
||||
*/
|
||||
struct ctx_query* context_deserialize_answer(struct ub_ctx* ctx,
|
||||
uint8_t* p, uint32_t len, int* err);
|
||||
|
||||
/**
|
||||
* Deserialize a cancel buffer.
|
||||
* @param ctx: context
|
||||
* @param p: buffer serialized.
|
||||
* @param len: length of buffer.
|
||||
* @return ctx_query to cancel or NULL for failure.
|
||||
*/
|
||||
struct ctx_query* context_deserialize_cancel(struct ub_ctx* ctx,
|
||||
uint8_t* p, uint32_t len);
|
||||
|
||||
#endif /* LIBUNBOUND_CONTEXT_H */
|
||||
@@ -0,0 +1,923 @@
|
||||
/*
|
||||
* unbound.c - unbound validating resolver public API implementation
|
||||
*
|
||||
* 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 resolve DNS queries and
|
||||
* validate the answers. Synchonously and asynchronously.
|
||||
*
|
||||
*/
|
||||
|
||||
/* include the public api first, it should be able to stand alone */
|
||||
#include "libunbound/unbound.h"
|
||||
#include "config.h"
|
||||
#include "libunbound/context.h"
|
||||
#include "libunbound/libworker.h"
|
||||
#include "util/locks.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/alloc.h"
|
||||
#include "util/module.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/log.h"
|
||||
#include "util/random.h"
|
||||
#include "util/net_help.h"
|
||||
#include "services/modstack.h"
|
||||
#include "services/localzone.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "services/cache/rrset.h"
|
||||
|
||||
struct ub_ctx*
|
||||
ub_ctx_create()
|
||||
{
|
||||
struct ub_ctx* ctx;
|
||||
unsigned int seed;
|
||||
log_init(NULL, 0, NULL); /* logs to stderr */
|
||||
log_ident_set("libunbound");
|
||||
verbosity = 0; /* errors only */
|
||||
checklock_start();
|
||||
ctx = (struct ub_ctx*)calloc(1, sizeof(*ctx));
|
||||
if(!ctx) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
alloc_init(&ctx->superalloc, NULL, 0);
|
||||
seed = (unsigned int)time(NULL) ^ (unsigned int)getpid();
|
||||
if(!(ctx->seed_rnd = ub_initstate(seed, NULL))) {
|
||||
seed = 0;
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
seed = 0;
|
||||
if(socketpair(AF_UNIX, SOCK_STREAM, 0, ctx->qqpipe) == -1) {
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
if(socketpair(AF_UNIX, SOCK_STREAM, 0, ctx->rrpipe) == -1) {
|
||||
int e = errno;
|
||||
close(ctx->qqpipe[0]);
|
||||
close(ctx->qqpipe[1]);
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
errno = e;
|
||||
return NULL;
|
||||
}
|
||||
if(!fd_set_nonblock(ctx->rrpipe[0]) ||
|
||||
!fd_set_nonblock(ctx->rrpipe[1]) ||
|
||||
!fd_set_nonblock(ctx->qqpipe[0]) ||
|
||||
!fd_set_nonblock(ctx->qqpipe[1])) {
|
||||
int e = errno;
|
||||
close(ctx->rrpipe[0]);
|
||||
close(ctx->rrpipe[1]);
|
||||
close(ctx->qqpipe[0]);
|
||||
close(ctx->qqpipe[1]);
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
errno = e;
|
||||
return NULL;
|
||||
}
|
||||
lock_basic_init(&ctx->qqpipe_lock);
|
||||
lock_basic_init(&ctx->rrpipe_lock);
|
||||
lock_basic_init(&ctx->cfglock);
|
||||
ctx->env = (struct module_env*)calloc(1, sizeof(*ctx->env));
|
||||
if(!ctx->env) {
|
||||
close(ctx->rrpipe[0]);
|
||||
close(ctx->rrpipe[1]);
|
||||
close(ctx->qqpipe[0]);
|
||||
close(ctx->qqpipe[1]);
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
ctx->env->cfg = config_create_forlib();
|
||||
if(!ctx->env->cfg) {
|
||||
close(ctx->rrpipe[0]);
|
||||
close(ctx->rrpipe[1]);
|
||||
close(ctx->qqpipe[0]);
|
||||
close(ctx->qqpipe[1]);
|
||||
free(ctx->env);
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
ctx->env->alloc = &ctx->superalloc;
|
||||
ctx->env->worker = NULL;
|
||||
ctx->env->need_to_validate = 0;
|
||||
modstack_init(&ctx->mods);
|
||||
rbtree_init(&ctx->queries, &context_query_cmp);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/** delete q */
|
||||
static void
|
||||
delq(rbnode_t* n, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct ctx_query* q = (struct ctx_query*)n;
|
||||
context_query_delete(q);
|
||||
}
|
||||
|
||||
void
|
||||
ub_ctx_delete(struct ub_ctx* ctx)
|
||||
{
|
||||
struct alloc_cache* a, *na;
|
||||
if(!ctx) return;
|
||||
/* stop the bg thread */
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->created_bg) {
|
||||
uint8_t* msg;
|
||||
uint32_t len;
|
||||
uint32_t cmd = UB_LIBCMD_QUIT;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
lock_basic_lock(&ctx->qqpipe_lock);
|
||||
(void)libworker_write_msg(ctx->qqpipe[1], (uint8_t*)&cmd,
|
||||
(uint32_t)sizeof(cmd), 0);
|
||||
lock_basic_unlock(&ctx->qqpipe_lock);
|
||||
lock_basic_lock(&ctx->rrpipe_lock);
|
||||
while(libworker_read_msg(ctx->rrpipe[0], &msg, &len, 0)) {
|
||||
/* discard all results except a quit confirm */
|
||||
if(context_serial_getcmd(msg, len) == UB_LIBCMD_QUIT) {
|
||||
free(msg);
|
||||
break;
|
||||
}
|
||||
free(msg);
|
||||
}
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
|
||||
/* if bg worker is a thread, wait for it to exit, so that all
|
||||
* resources are really gone. */
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->dothread) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
ub_thread_join(ctx->bg_tid);
|
||||
} else {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
}
|
||||
else {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
|
||||
|
||||
modstack_desetup(&ctx->mods, ctx->env);
|
||||
a = ctx->alloc_list;
|
||||
while(a) {
|
||||
na = a->super;
|
||||
a->super = &ctx->superalloc;
|
||||
alloc_clear(a);
|
||||
free(a);
|
||||
a = na;
|
||||
}
|
||||
local_zones_delete(ctx->local_zones);
|
||||
lock_basic_destroy(&ctx->qqpipe_lock);
|
||||
lock_basic_destroy(&ctx->rrpipe_lock);
|
||||
lock_basic_destroy(&ctx->cfglock);
|
||||
if(ctx->qqpipe[0] != -1)
|
||||
close(ctx->qqpipe[0]);
|
||||
if(ctx->qqpipe[1] != -1)
|
||||
close(ctx->qqpipe[1]);
|
||||
if(ctx->rrpipe[0] != -1)
|
||||
close(ctx->rrpipe[0]);
|
||||
if(ctx->rrpipe[1] != -1)
|
||||
close(ctx->rrpipe[1]);
|
||||
ctx->qqpipe[0] = -1;
|
||||
ctx->qqpipe[1] = -1;
|
||||
ctx->rrpipe[0] = -1;
|
||||
ctx->rrpipe[1] = -1;
|
||||
if(ctx->env) {
|
||||
slabhash_delete(ctx->env->msg_cache);
|
||||
rrset_cache_delete(ctx->env->rrset_cache);
|
||||
infra_delete(ctx->env->infra_cache);
|
||||
config_delete(ctx->env->cfg);
|
||||
free(ctx->env);
|
||||
}
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
alloc_clear(&ctx->superalloc);
|
||||
traverse_postorder(&ctx->queries, delq, NULL);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_set_option(struct ub_ctx* ctx, char* opt, char* val)
|
||||
{
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
if(!config_set_option(ctx->env->cfg, opt, val)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_SYNTAX;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_config(struct ub_ctx* ctx, char* fname)
|
||||
{
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
if(!config_read(ctx->env->cfg, fname)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_SYNTAX;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_add_ta(struct ub_ctx* ctx, char* ta)
|
||||
{
|
||||
char* dup = strdup(ta);
|
||||
if(!dup) return UB_NOMEM;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_list, dup)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
free(dup);
|
||||
return UB_NOMEM;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_add_ta_file(struct ub_ctx* ctx, char* fname)
|
||||
{
|
||||
char* dup = strdup(fname);
|
||||
if(!dup) return UB_NOMEM;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_file_list, dup)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
free(dup);
|
||||
return UB_NOMEM;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_trustedkeys(struct ub_ctx* ctx, char* fname)
|
||||
{
|
||||
char* dup = strdup(fname);
|
||||
if(!dup) return UB_NOMEM;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
if(!cfg_strlist_insert(&ctx->env->cfg->trusted_keys_file_list, dup)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
free(dup);
|
||||
return UB_NOMEM;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_debuglevel(struct ub_ctx* ctx, int d)
|
||||
{
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
verbosity = d;
|
||||
ctx->env->cfg->verbosity = d;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int ub_ctx_debugout(struct ub_ctx* ctx, void* out)
|
||||
{
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
log_file((FILE*)out);
|
||||
ctx->logfile_override = 1;
|
||||
ctx->log_out = out;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_async(struct ub_ctx* ctx, int dothread)
|
||||
{
|
||||
#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
|
||||
if(dothread) /* cannot do threading */
|
||||
return UB_NOERROR;
|
||||
#endif
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
ctx->dothread = dothread;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
/** perform a select() on the result read pipe */
|
||||
static int
|
||||
pollit(struct ub_ctx* ctx, struct timeval* t)
|
||||
{
|
||||
fd_set r;
|
||||
#ifndef S_SPLINT_S
|
||||
FD_ZERO(&r);
|
||||
FD_SET(ctx->rrpipe[0], &r);
|
||||
#endif
|
||||
if(select(ctx->rrpipe[0]+1, &r, NULL, NULL, t) == -1) {
|
||||
return 0;
|
||||
}
|
||||
errno = 0;
|
||||
return FD_ISSET(ctx->rrpipe[0], &r);
|
||||
}
|
||||
|
||||
int
|
||||
ub_poll(struct ub_ctx* ctx)
|
||||
{
|
||||
struct timeval t;
|
||||
memset(&t, 0, sizeof(t));
|
||||
/* no need to hold lock while testing for readability. */
|
||||
return pollit(ctx, &t);
|
||||
}
|
||||
|
||||
int
|
||||
ub_fd(struct ub_ctx* ctx)
|
||||
{
|
||||
return ctx->rrpipe[0];
|
||||
}
|
||||
|
||||
/** process answer from bg worker */
|
||||
static int
|
||||
process_answer_detail(struct ub_ctx* ctx, uint8_t* msg, uint32_t len,
|
||||
ub_callback_t* cb, void** cbarg, int* err,
|
||||
struct ub_result** res)
|
||||
{
|
||||
struct ctx_query* q;
|
||||
if(context_serial_getcmd(msg, len) != UB_LIBCMD_ANSWER) {
|
||||
log_err("error: bad data from bg worker %d",
|
||||
(int)context_serial_getcmd(msg, len));
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
q = context_deserialize_answer(ctx, msg, len, err);
|
||||
if(!q) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
/* probably simply the lookup that failed, i.e.
|
||||
* response returned before cancel was sent out, so noerror */
|
||||
return 1;
|
||||
}
|
||||
log_assert(q->async);
|
||||
|
||||
/* grab cb while locked */
|
||||
if(q->cancelled) {
|
||||
*cb = NULL;
|
||||
*cbarg = NULL;
|
||||
} else {
|
||||
*cb = q->cb;
|
||||
*cbarg = q->cb_arg;
|
||||
}
|
||||
if(*err) {
|
||||
*res = NULL;
|
||||
ub_resolve_free(q->res);
|
||||
} else {
|
||||
/* parse the message, extract rcode, fill result */
|
||||
ldns_buffer* buf = ldns_buffer_new(q->msg_len);
|
||||
struct regional* region = regional_create();
|
||||
*res = q->res;
|
||||
(*res)->rcode = LDNS_RCODE_SERVFAIL;
|
||||
if(region && buf) {
|
||||
ldns_buffer_clear(buf);
|
||||
ldns_buffer_write(buf, q->msg, q->msg_len);
|
||||
ldns_buffer_flip(buf);
|
||||
libworker_enter_result(*res, buf, region,
|
||||
q->msg_security);
|
||||
}
|
||||
ldns_buffer_free(buf);
|
||||
regional_destroy(region);
|
||||
}
|
||||
q->res = NULL;
|
||||
/* delete the q from list */
|
||||
(void)rbtree_delete(&ctx->queries, q->node.key);
|
||||
ctx->num_async--;
|
||||
context_query_delete(q);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
|
||||
if(*cb) return 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** process answer from bg worker */
|
||||
static int
|
||||
process_answer(struct ub_ctx* ctx, uint8_t* msg, uint32_t len)
|
||||
{
|
||||
int err;
|
||||
ub_callback_t cb;
|
||||
void* cbarg;
|
||||
struct ub_result* res;
|
||||
int r;
|
||||
|
||||
r = process_answer_detail(ctx, msg, len, &cb, &cbarg, &err, &res);
|
||||
|
||||
/* no locks held while calling callback, so that library is
|
||||
* re-entrant. */
|
||||
if(r == 2)
|
||||
(*cb)(cbarg, err, res);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
ub_process(struct ub_ctx* ctx)
|
||||
{
|
||||
int r;
|
||||
uint8_t* msg;
|
||||
uint32_t len;
|
||||
while(1) {
|
||||
msg = NULL;
|
||||
lock_basic_lock(&ctx->rrpipe_lock);
|
||||
r = libworker_read_msg(ctx->rrpipe[0], &msg, &len, 1);
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
if(r == 0)
|
||||
return UB_PIPE;
|
||||
else if(r == -1)
|
||||
return UB_NOERROR;
|
||||
if(!process_answer(ctx, msg, len)) {
|
||||
free(msg);
|
||||
return UB_PIPE;
|
||||
}
|
||||
free(msg);
|
||||
}
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_wait(struct ub_ctx* ctx)
|
||||
{
|
||||
int err;
|
||||
ub_callback_t cb;
|
||||
void* cbarg;
|
||||
struct ub_result* res;
|
||||
int r;
|
||||
uint8_t* msg;
|
||||
uint32_t len;
|
||||
/* this is basically the same loop as _process(), but with changes.
|
||||
* holds the rrpipe lock and waits with pollit */
|
||||
while(1) {
|
||||
lock_basic_lock(&ctx->rrpipe_lock);
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->num_async == 0) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
break;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
|
||||
/* keep rrpipe locked, while
|
||||
* o waiting for pipe readable
|
||||
* o parsing message
|
||||
* o possibly decrementing num_async
|
||||
* do callback without lock
|
||||
*/
|
||||
r = pollit(ctx, NULL);
|
||||
if(r) {
|
||||
r = libworker_read_msg(ctx->rrpipe[0], &msg, &len, 1);
|
||||
if(r == 0) {
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
return UB_PIPE;
|
||||
}
|
||||
if(r == -1) {
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
continue;
|
||||
}
|
||||
r = process_answer_detail(ctx, msg, len,
|
||||
&cb, &cbarg, &err, &res);
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
if(r == 0)
|
||||
return UB_PIPE;
|
||||
if(r == 2)
|
||||
(*cb)(cbarg, err, res);
|
||||
} else {
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
}
|
||||
}
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_resolve(struct ub_ctx* ctx, char* name, int rrtype,
|
||||
int rrclass, struct ub_result** result)
|
||||
{
|
||||
struct ctx_query* q;
|
||||
int r;
|
||||
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(!ctx->finalized) {
|
||||
r = context_finalize(ctx);
|
||||
if(r) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
/* create new ctx_query and attempt to add to the list */
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
q = context_new(ctx, name, rrtype, rrclass, NULL, NULL);
|
||||
if(!q)
|
||||
return UB_NOMEM;
|
||||
/* become a resolver thread for a bit */
|
||||
*result = NULL;
|
||||
|
||||
r = libworker_fg(ctx, q);
|
||||
if(r) {
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
(void)rbtree_delete(&ctx->queries, q->node.key);
|
||||
context_query_delete(q);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return r;
|
||||
}
|
||||
*result = q->res;
|
||||
q->res = NULL;
|
||||
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
(void)rbtree_delete(&ctx->queries, q->node.key);
|
||||
context_query_delete(q);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype,
|
||||
int rrclass, void* mydata, ub_callback_t callback, int* async_id)
|
||||
{
|
||||
struct ctx_query* q;
|
||||
uint8_t* msg = NULL;
|
||||
uint32_t len = 0;
|
||||
|
||||
if(async_id)
|
||||
*async_id = 0;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(!ctx->finalized) {
|
||||
int r = context_finalize(ctx);
|
||||
if(r) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if(!ctx->created_bg) {
|
||||
int r;
|
||||
ctx->created_bg = 1;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
r = libworker_bg(ctx);
|
||||
if(r) {
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
ctx->created_bg = 0;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
|
||||
/* create new ctx_query and attempt to add to the list */
|
||||
q = context_new(ctx, name, rrtype, rrclass, callback, mydata);
|
||||
if(!q)
|
||||
return UB_NOMEM;
|
||||
|
||||
/* write over pipe to background worker */
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
msg = context_serialize_new_query(q, &len);
|
||||
if(!msg) {
|
||||
(void)rbtree_delete(&ctx->queries, q->node.key);
|
||||
ctx->num_async--;
|
||||
context_query_delete(q);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOMEM;
|
||||
}
|
||||
if(async_id)
|
||||
*async_id = q->querynum;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
|
||||
lock_basic_lock(&ctx->qqpipe_lock);
|
||||
if(!libworker_write_msg(ctx->qqpipe[1], msg, len, 0)) {
|
||||
lock_basic_unlock(&ctx->qqpipe_lock);
|
||||
free(msg);
|
||||
return UB_PIPE;
|
||||
}
|
||||
lock_basic_unlock(&ctx->qqpipe_lock);
|
||||
free(msg);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_cancel(struct ub_ctx* ctx, int async_id)
|
||||
{
|
||||
struct ctx_query* q;
|
||||
uint8_t* msg = NULL;
|
||||
uint32_t len = 0;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
q = (struct ctx_query*)rbtree_search(&ctx->queries, &async_id);
|
||||
if(!q || !q->async) {
|
||||
/* it is not there, so nothing to do */
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
log_assert(q->async);
|
||||
q->cancelled = 1;
|
||||
|
||||
/* delete it */
|
||||
if(!ctx->dothread) { /* if forked */
|
||||
(void)rbtree_delete(&ctx->queries, q->node.key);
|
||||
ctx->num_async--;
|
||||
context_query_delete(q);
|
||||
msg = context_serialize_cancel(q, &len);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
if(!msg) {
|
||||
return UB_NOMEM;
|
||||
}
|
||||
/* send cancel to background worker */
|
||||
lock_basic_lock(&ctx->qqpipe_lock);
|
||||
if(!libworker_write_msg(ctx->qqpipe[1], msg, len, 0)) {
|
||||
lock_basic_unlock(&ctx->qqpipe_lock);
|
||||
free(msg);
|
||||
return UB_PIPE;
|
||||
}
|
||||
lock_basic_unlock(&ctx->qqpipe_lock);
|
||||
free(msg);
|
||||
} else {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
void
|
||||
ub_resolve_free(struct ub_result* result)
|
||||
{
|
||||
char** p;
|
||||
if(!result) return;
|
||||
free(result->qname);
|
||||
if(result->canonname != result->qname)
|
||||
free(result->canonname);
|
||||
if(result->data)
|
||||
for(p = result->data; *p; p++)
|
||||
free(*p);
|
||||
free(result->data);
|
||||
free(result->len);
|
||||
free(result);
|
||||
}
|
||||
|
||||
const char*
|
||||
ub_strerror(int err)
|
||||
{
|
||||
switch(err) {
|
||||
case UB_NOERROR: return "no error";
|
||||
case UB_SOCKET: return "socket io error";
|
||||
case UB_NOMEM: return "out of memory";
|
||||
case UB_SYNTAX: return "syntax error";
|
||||
case UB_SERVFAIL: return "server failure";
|
||||
case UB_FORKFAIL: return "could not fork";
|
||||
case UB_INITFAIL: return "initialization failure";
|
||||
case UB_AFTERFINAL: return "setting change after finalize";
|
||||
case UB_PIPE: return "error in pipe communication with async";
|
||||
case UB_READFILE: return "error reading file";
|
||||
default: return "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_set_fwd(struct ub_ctx* ctx, char* addr)
|
||||
{
|
||||
struct sockaddr_storage storage;
|
||||
socklen_t stlen;
|
||||
struct config_stub* s;
|
||||
char* dupl;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
errno=EINVAL;
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
if(!addr) {
|
||||
/* disable fwd mode - the root stub should be first. */
|
||||
if(ctx->env->cfg->forwards &&
|
||||
strcmp(ctx->env->cfg->forwards->name, ".") == 0) {
|
||||
s = ctx->env->cfg->forwards;
|
||||
ctx->env->cfg->forwards = s->next;
|
||||
s->next = NULL;
|
||||
config_delstubs(s);
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
|
||||
/* check syntax for addr */
|
||||
if(!extstrtoaddr(addr, &storage, &stlen)) {
|
||||
errno=EINVAL;
|
||||
return UB_SYNTAX;
|
||||
}
|
||||
|
||||
/* it parses, add root stub in front of list */
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(!ctx->env->cfg->forwards ||
|
||||
strcmp(ctx->env->cfg->forwards->name, ".") != 0) {
|
||||
s = calloc(1, sizeof(*s));
|
||||
if(!s) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
errno=ENOMEM;
|
||||
return UB_NOMEM;
|
||||
}
|
||||
s->name = strdup(".");
|
||||
if(!s->name) {
|
||||
free(s);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
errno=ENOMEM;
|
||||
return UB_NOMEM;
|
||||
}
|
||||
s->next = ctx->env->cfg->forwards;
|
||||
ctx->env->cfg->forwards = s;
|
||||
} else {
|
||||
log_assert(ctx->env->cfg->forwards);
|
||||
s = ctx->env->cfg->forwards;
|
||||
}
|
||||
dupl = strdup(addr);
|
||||
if(!dupl) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
errno=ENOMEM;
|
||||
return UB_NOMEM;
|
||||
}
|
||||
if(!cfg_strlist_insert(&s->addrs, dupl)) {
|
||||
free(dupl);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
errno=ENOMEM;
|
||||
return UB_NOMEM;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_resolvconf(struct ub_ctx* ctx, char* fname)
|
||||
{
|
||||
FILE* in;
|
||||
int numserv = 0;
|
||||
char buf[1024];
|
||||
char* parse, *addr;
|
||||
int r;
|
||||
if(fname == NULL)
|
||||
fname = "/etc/resolv.conf";
|
||||
in = fopen(fname, "r");
|
||||
if(!in) {
|
||||
/* error in errno! perror(fname) */
|
||||
return UB_READFILE;
|
||||
}
|
||||
while(fgets(buf, (int)sizeof(buf), in)) {
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
parse=buf;
|
||||
while(*parse == ' ' || *parse == '\t')
|
||||
parse++;
|
||||
if(strncmp(parse, "nameserver", 10) == 0) {
|
||||
numserv++;
|
||||
parse += 10; /* skip 'nameserver' */
|
||||
/* skip whitespace */
|
||||
while(*parse == ' ' || *parse == '\t')
|
||||
parse++;
|
||||
addr = parse;
|
||||
/* skip [0-9a-fA-F.:]*, i.e. IP4 and IP6 address */
|
||||
while(isxdigit(*parse) || *parse=='.' || *parse==':')
|
||||
parse++;
|
||||
/* terminate after the address, remove newline */
|
||||
*parse = 0;
|
||||
|
||||
if((r = ub_ctx_set_fwd(ctx, addr)) != UB_NOERROR) {
|
||||
fclose(in);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(in);
|
||||
if(numserv == 0) {
|
||||
/* from resolv.conf(5) if none given, use localhost */
|
||||
return ub_ctx_set_fwd(ctx, "127.0.0.1");
|
||||
}
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_hosts(struct ub_ctx* ctx, char* fname)
|
||||
{
|
||||
FILE* in;
|
||||
char buf[1024], ldata[1024];
|
||||
char* parse, *addr, *name, *ins;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
errno=EINVAL;
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
if(fname == NULL)
|
||||
fname = "/etc/hosts";
|
||||
in = fopen(fname, "r");
|
||||
if(!in) {
|
||||
/* error in errno! perror(fname) */
|
||||
return UB_READFILE;
|
||||
}
|
||||
while(fgets(buf, (int)sizeof(buf), in)) {
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
parse=buf;
|
||||
while(*parse == ' ' || *parse == '\t')
|
||||
parse++;
|
||||
if(*parse == '#')
|
||||
continue; /* skip comment */
|
||||
/* format: <addr> spaces <name> spaces <name> ... */
|
||||
addr = parse;
|
||||
/* skip addr */
|
||||
while(isxdigit(*parse) || *parse == '.' || *parse == ':')
|
||||
parse++;
|
||||
if(*parse != ' ' && *parse != '\t') {
|
||||
/* must have whitespace after address */
|
||||
fclose(in);
|
||||
errno=EINVAL;
|
||||
return UB_SYNTAX;
|
||||
}
|
||||
*parse++ = 0; /* end delimiter for addr ... */
|
||||
/* go to names and add them */
|
||||
while(*parse) {
|
||||
while(*parse == ' ' || *parse == '\t' || *parse=='\n')
|
||||
parse++;
|
||||
if(*parse == 0 || *parse == '#')
|
||||
break;
|
||||
/* skip name, allows (too) many printable characters */
|
||||
name = parse;
|
||||
while('!' <= *parse && *parse <= '~')
|
||||
parse++;
|
||||
if(*parse)
|
||||
*parse++ = 0; /* end delimiter for name */
|
||||
snprintf(ldata, sizeof(ldata), "%s %s %s",
|
||||
name, str_is_ip6(addr)?"AAAA":"A", addr);
|
||||
ins = strdup(ldata);
|
||||
if(!ins) {
|
||||
/* out of memory */
|
||||
fclose(in);
|
||||
errno=ENOMEM;
|
||||
return UB_NOMEM;
|
||||
}
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(!cfg_strlist_insert(&ctx->env->cfg->local_data,
|
||||
ins)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
fclose(in);
|
||||
free(ins);
|
||||
errno=ENOMEM;
|
||||
return UB_NOMEM;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
}
|
||||
fclose(in);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* libunbound/worker.h - worker thread or process that resolves
|
||||
*
|
||||
* 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 worker process or thread that performs
|
||||
* the DNS resolving and validation. The worker is called by a procedure
|
||||
* and if in the background continues until exit, if in the foreground
|
||||
* returns from the procedure when done.
|
||||
*/
|
||||
#ifndef LIBUNBOUND_WORKER_H
|
||||
#define LIBUNBOUND_WORKER_H
|
||||
#include "util/data/packed_rrset.h"
|
||||
struct ub_ctx;
|
||||
struct ub_result;
|
||||
struct module_env;
|
||||
struct comm_base;
|
||||
struct outside_network;
|
||||
struct ub_randstate;
|
||||
struct ctx_query;
|
||||
struct outbound_entry;
|
||||
struct module_qstate;
|
||||
struct comm_point;
|
||||
struct comm_reply;
|
||||
struct libworker_res_list;
|
||||
struct regional;
|
||||
|
||||
/**
|
||||
* The library-worker status structure
|
||||
* Internal to the worker.
|
||||
*/
|
||||
struct libworker {
|
||||
/** every worker has a unique thread_num. (first in struct) */
|
||||
int thread_num;
|
||||
/** context we are operating under */
|
||||
struct ub_ctx* ctx;
|
||||
|
||||
/** is this the bg worker? */
|
||||
int is_bg;
|
||||
/** is this a bg worker that is threaded (not forked)? */
|
||||
int is_bg_thread;
|
||||
|
||||
/** copy of the module environment with worker local entries. */
|
||||
struct module_env* env;
|
||||
/** the event base this worker works with */
|
||||
struct comm_base* base;
|
||||
/** the backside outside network interface to the auth servers */
|
||||
struct outside_network* back;
|
||||
/** random() table for this worker. */
|
||||
struct ub_randstate* rndstate;
|
||||
|
||||
/** commpoint to listen to commands */
|
||||
struct comm_point* cmd_com;
|
||||
/** are we currently reading a command, 0 if not, else bytecount */
|
||||
size_t cmd_read;
|
||||
/** size of current read command, may be partially read */
|
||||
uint32_t cmd_len;
|
||||
/** the current read command content, malloced, can be partially read*/
|
||||
uint8_t* cmd_msg;
|
||||
|
||||
/** commpoint to write results back */
|
||||
struct comm_point* res_com;
|
||||
/** are we curently writing a result, 0 if not, else bytecount into
|
||||
* the res_list first entry. */
|
||||
size_t res_write;
|
||||
/** list of outstanding results to be written back */
|
||||
struct libworker_res_list* res_list;
|
||||
/** last in list */
|
||||
struct libworker_res_list* res_last;
|
||||
};
|
||||
|
||||
/**
|
||||
* List of results (arbitrary command serializations) to write back
|
||||
*/
|
||||
struct libworker_res_list {
|
||||
/** next in list */
|
||||
struct libworker_res_list* next;
|
||||
/** serialized buffer to write */
|
||||
uint8_t* buf;
|
||||
/** length to write */
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a background worker
|
||||
* @param ctx: is updated with pid/tid of the background worker.
|
||||
* a new allocation cache is obtained from ctx. It contains the
|
||||
* threadnumber and unique id for further (shared) cache insertions.
|
||||
* @return 0 if OK, else error.
|
||||
* Further communication is done via the pipes in ctx.
|
||||
*/
|
||||
int libworker_bg(struct ub_ctx* ctx);
|
||||
|
||||
/**
|
||||
* Create a foreground worker.
|
||||
* This worker will join the threadpool of resolver threads.
|
||||
* It exits when the query answer has been obtained (or error).
|
||||
* This routine blocks until the worker is finished.
|
||||
* @param ctx: new allocation cache obtained and returned to it.
|
||||
* @param q: query (result is stored in here).
|
||||
* @return 0 if finished OK, else error.
|
||||
*/
|
||||
int libworker_fg(struct ub_ctx* ctx, struct ctx_query* q);
|
||||
|
||||
/** cleanup the cache to remove all rrset IDs from it, arg is libworker */
|
||||
void libworker_alloc_cleanup(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 libworker_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* libworker_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);
|
||||
|
||||
/** process incoming replies from the network */
|
||||
int libworker_handle_reply(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* reply_info);
|
||||
|
||||
/** process incoming serviced query replies from the network */
|
||||
int libworker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* reply_info);
|
||||
|
||||
/** handle control command coming into server */
|
||||
int libworker_handle_control_cmd(struct comm_point* c, void* arg,
|
||||
int err, struct comm_reply* rep);
|
||||
|
||||
/** handle opportunity to write result back */
|
||||
int libworker_handle_result_write(struct comm_point* c, void* arg,
|
||||
int err, struct comm_reply* rep);
|
||||
|
||||
/**
|
||||
* Write length bytes followed by message.
|
||||
* @param fd: the socket to write on. Is nonblocking.
|
||||
* Set to blocking by the function,
|
||||
* and back to non-blocking at exit of function.
|
||||
* @param buf: the message.
|
||||
* @param len: length of message.
|
||||
* @param nonblock: if set to true, the first write is nonblocking.
|
||||
* If the first write fails the function returns -1.
|
||||
* If set false, the first write is blocking.
|
||||
* @return: all remainder writes are nonblocking.
|
||||
* return 0 on error, in that case blocking/nonblocking of socket is
|
||||
* unknown.
|
||||
* return 1 if all OK.
|
||||
*/
|
||||
int libworker_write_msg(int fd, uint8_t* buf, uint32_t len, int nonblock);
|
||||
|
||||
/**
|
||||
* Read length bytes followed by message.
|
||||
* @param fd: the socket to write on. Is nonblocking.
|
||||
* Set to blocking by the function,
|
||||
* and back to non-blocking at exit of function.
|
||||
* @param buf: the message, malloced.
|
||||
* @param len: length of message, returned.
|
||||
* @param nonblock: if set to true, the first read is nonblocking.
|
||||
* If the first read fails the function returns -1.
|
||||
* If set false, the first read is blocking.
|
||||
* @return: all remainder reads are nonblocking.
|
||||
* return 0 on error, in that case blocking/nonblocking of socket is
|
||||
* unknown. On EOF 0 is returned.
|
||||
* return 1 if all OK.
|
||||
*/
|
||||
int libworker_read_msg(int fd, uint8_t** buf, uint32_t* len, int nonblock);
|
||||
|
||||
/**
|
||||
* fill result from parsed message, on error fills servfail
|
||||
* @param res: is clear at start, filled in at end.
|
||||
* @param buf: contains DNS message.
|
||||
* @param temp: temporary buffer for parse.
|
||||
* @param msg_security: security status of the DNS message.
|
||||
* On error, the res may contain a different status
|
||||
* (out of memory is not secure, not bogus).
|
||||
*/
|
||||
void libworker_enter_result(struct ub_result* res, ldns_buffer* buf,
|
||||
struct regional* temp, enum sec_status msg_security);
|
||||
|
||||
#endif /* LIBUNBOUND_WORKER_H */
|
||||
@@ -0,0 +1,22 @@
|
||||
ub_ctx_create
|
||||
ub_ctx_delete
|
||||
ub_ctx_set_option
|
||||
ub_ctx_config
|
||||
ub_ctx_set_fwd
|
||||
ub_ctx_resolvconf
|
||||
ub_ctx_hosts
|
||||
ub_ctx_add_ta
|
||||
ub_ctx_add_ta_file
|
||||
ub_ctx_trustedkeys
|
||||
ub_ctx_debugout
|
||||
ub_ctx_debuglevel
|
||||
ub_ctx_async
|
||||
ub_poll
|
||||
ub_wait
|
||||
ub_fd
|
||||
ub_process
|
||||
ub_resolve
|
||||
ub_resolve_async
|
||||
ub_cancel
|
||||
ub_resolve_free
|
||||
ub_strerror
|
||||
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* unbound.h - unbound validating resolver public API
|
||||
*
|
||||
* 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 resolve DNS queries and
|
||||
* validate the answers. Synchonously and asynchronously.
|
||||
*
|
||||
* Several ways to use this interface from an application wishing
|
||||
* to perform (validated) DNS lookups.
|
||||
*
|
||||
* All start with
|
||||
* ctx = ub_ctx_create();
|
||||
* err = ub_ctx_add_ta(ctx, "...");
|
||||
* err = ub_ctx_add_ta(ctx, "...");
|
||||
* ... some lookups
|
||||
* ... call ub_ctx_delete(ctx); when you want to stop.
|
||||
*
|
||||
* Application not threaded. Blocking.
|
||||
* int err = ub_resolve(ctx, "www.example.com", ...
|
||||
* if(err) fprintf(stderr, "lookup error: %s\n", ub_strerror(err));
|
||||
* ... use the answer
|
||||
*
|
||||
* Application not threaded. Non-blocking ('asynchronous').
|
||||
* err = ub_resolve_async(ctx, "www.example.com", ... my_callback);
|
||||
* ... application resumes processing ...
|
||||
* ... and when either ub_poll(ctx) is true
|
||||
* ... or when the file descriptor ub_fd(ctx) is readable,
|
||||
* ... or whenever, the app calls ...
|
||||
* ub_process(ctx);
|
||||
* ... if no result is ready, the app resumes processing above,
|
||||
* ... or process() calls my_callback() with results.
|
||||
*
|
||||
* ... if the application has nothing more to do, wait for answer
|
||||
* ub_wait(ctx);
|
||||
*
|
||||
* Application threaded. Blocking.
|
||||
* Blocking, same as above. The current thread does the work.
|
||||
* Multiple threads can use the *same context*, each does work and uses
|
||||
* shared cache data from the context.
|
||||
*
|
||||
* Application threaded. Non-blocking ('asynchronous').
|
||||
* ... setup threaded-asynchronous config option
|
||||
* err = ub_ctx_async(ctx, 1);
|
||||
* ... same as async for non-threaded
|
||||
* ... the callbacks are called in the thread that calls process(ctx)
|
||||
*
|
||||
* If no threading is compiled in, the above async example uses fork(2) to
|
||||
* create a process to perform the work. The forked process exits when the
|
||||
* calling process exits, or ctx_delete() is called.
|
||||
* Otherwise, for asynchronous with threading, a worker thread is created.
|
||||
*
|
||||
* The blocking calls use shared ctx-cache when threaded. Thus
|
||||
* ub_resolve() and ub_resolve_async() && ub_wait() are
|
||||
* not the same. The first makes the current thread do the work, setting
|
||||
* up buffers, etc, to perform the work (but using shared cache data).
|
||||
* The second calls another worker thread (or process) to perform the work.
|
||||
* And no buffers need to be set up, but a context-switch happens.
|
||||
*/
|
||||
#ifndef _UB_UNBOUND_H
|
||||
#define _UB_UNBOUND_H
|
||||
|
||||
/**
|
||||
* The validation context is created to hold the resolver status,
|
||||
* validation keys and a small cache (containing messages, rrsets,
|
||||
* roundtrip times, trusted keys, lameness information).
|
||||
*
|
||||
* Its contents are internally defined.
|
||||
*/
|
||||
struct ub_ctx;
|
||||
|
||||
/**
|
||||
* The validation and resolution results.
|
||||
* Allocated by the resolver, and need to be freed by the application
|
||||
* with ub_resolve_free().
|
||||
*/
|
||||
struct ub_result {
|
||||
/** The original question, name text string. */
|
||||
char* qname;
|
||||
/** the type asked for */
|
||||
int qtype;
|
||||
/** the class asked for */
|
||||
int qclass;
|
||||
|
||||
/**
|
||||
* a list of network order DNS rdata items, terminated with a
|
||||
* NULL pointer, so that data[0] is the first result entry,
|
||||
* data[1] the second, and the last entry is NULL.
|
||||
* If there was no data, data[0] is NULL.
|
||||
*/
|
||||
char** data;
|
||||
|
||||
/** the length in bytes of the data items, len[i] for data[i] */
|
||||
int* len;
|
||||
|
||||
/**
|
||||
* canonical name for the result (the final cname).
|
||||
* zero terminated string.
|
||||
* May be NULL if no canonical name exists.
|
||||
*/
|
||||
char* canonname;
|
||||
|
||||
/**
|
||||
* DNS RCODE for the result. May contain additional error code if
|
||||
* there was no data due to an error. 0 (NOERROR) if okay.
|
||||
*/
|
||||
int rcode;
|
||||
|
||||
/**
|
||||
* If there is any data, this is true.
|
||||
* If false, there was no data (nxdomain may be true, rcode can be set).
|
||||
*/
|
||||
int havedata;
|
||||
|
||||
/**
|
||||
* If there was no data, and the domain did not exist, this is true.
|
||||
* If it is false, and there was no data, then the domain name
|
||||
* is purported to exist, but the requested data type is not available.
|
||||
*/
|
||||
int nxdomain;
|
||||
|
||||
/**
|
||||
* True, if the result is validated securely.
|
||||
* False, if validation failed or domain queried has no security info.
|
||||
*
|
||||
* It is possible to get a result with no data (havedata is false),
|
||||
* and secure is true. This means that the non-existance of the data
|
||||
* was cryptographically proven (with signatures).
|
||||
*/
|
||||
int secure;
|
||||
|
||||
/**
|
||||
* If the result was not secure (secure==0), and this result is due
|
||||
* to a security failure, bogus is true.
|
||||
* This means the data has been actively tampered with, signatures
|
||||
* failed, expected signatures were not present, timestamps on
|
||||
* signatures were out of date and so on.
|
||||
*
|
||||
* If !secure and !bogus, this can happen if the data is not secure
|
||||
* because security is disabled for that domain name.
|
||||
* This means the data is from a domain where data is not signed.
|
||||
*/
|
||||
int bogus;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for results of async queries.
|
||||
* The readable function definition looks like:
|
||||
* void my_callback(void* my_arg, int err, struct ub_result* result);
|
||||
* It is called with
|
||||
* void* my_arg: your pointer to a (struct of) data of your choice,
|
||||
* or NULL.
|
||||
* int err: if 0 all is OK, otherwise an error occured and no results
|
||||
* are forthcoming.
|
||||
* struct result: pointer to more detailed result structure.
|
||||
* This structure is allocated on the heap and needs to be
|
||||
* freed with ub_resolve_free(result);
|
||||
*/
|
||||
typedef void (*ub_callback_t)(void*, int, struct ub_result*);
|
||||
|
||||
/**
|
||||
* Create a resolving and validation context.
|
||||
* The information from /etc/resolv.conf and /etc/hosts is not utilised by
|
||||
* default. Use ub_ctx_resolvconf and ub_ctx_hosts to read them.
|
||||
* @return a new context. default initialisation.
|
||||
* returns NULL on error.
|
||||
*/
|
||||
struct ub_ctx* ub_ctx_create(void);
|
||||
|
||||
/**
|
||||
* Destroy a validation context and free all its resources.
|
||||
* Outstanding async queries are killed and callbacks are not called for them.
|
||||
* @param ctx: context to delete.
|
||||
*/
|
||||
void ub_ctx_delete(struct ub_ctx* ctx);
|
||||
|
||||
/**
|
||||
* Set an option for the context.
|
||||
* @param ctx: context.
|
||||
* @param opt: option name from the unbound.conf config file format.
|
||||
* (not all settings applicable). The name includes the trailing ':'
|
||||
* for example ub_ctx_set_option("logfile:", "mylog.txt");
|
||||
* This is a power-users interface that lets you specify all sorts
|
||||
* of options.
|
||||
* For some specific options, such as adding trust anchors, special
|
||||
* routines exist.
|
||||
* @param val: value of the option.
|
||||
* @return: 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_set_option(struct ub_ctx* ctx, char* opt, char* val);
|
||||
|
||||
/**
|
||||
* setup configuration for the given context.
|
||||
* @param ctx: context.
|
||||
* @param fname: unbound config file (not all settings applicable).
|
||||
* This is a power-users interface that lets you specify all sorts
|
||||
* of options.
|
||||
* For some specific options, such as adding trust anchors, special
|
||||
* routines exist.
|
||||
* @return: 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_config(struct ub_ctx* ctx, char* fname);
|
||||
|
||||
/**
|
||||
* Set machine to forward DNS queries to, the caching resolver to use.
|
||||
* IP4 or IP6 address. Forwards all DNS requests to that machine, which
|
||||
* is expected to run a recursive resolver. If the proxy is not
|
||||
* DNSSEC-capable, validation may fail. Can be called several times, in
|
||||
* that case the addresses are used as backup servers.
|
||||
*
|
||||
* To read the list of nameservers from /etc/resolv.conf (from DHCP or so),
|
||||
* use the call ub_ctx_resolvconf.
|
||||
*
|
||||
* @param ctx: context.
|
||||
* At this time it is only possible to set configuration before the
|
||||
* first resolve is done.
|
||||
* @param addr: address, IP4 or IP6 in string format.
|
||||
* If the addr is NULL, forwarding is disabled.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_set_fwd(struct ub_ctx* ctx, char* addr);
|
||||
|
||||
/**
|
||||
* Read list of nameservers to use from the filename given.
|
||||
* Usually "/etc/resolv.conf". Uses those nameservers as caching proxies.
|
||||
* If they do not support DNSSEC, validation may fail.
|
||||
*
|
||||
* Only nameservers are picked up, the searchdomain, ndots and other
|
||||
* settings from resolv.conf(5) are ignored.
|
||||
*
|
||||
* @param ctx: context.
|
||||
* At this time it is only possible to set configuration before the
|
||||
* first resolve is done.
|
||||
* @param fname: file name string. If NULL "/etc/resolv.conf" is used.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_resolvconf(struct ub_ctx* ctx, char* fname);
|
||||
|
||||
/**
|
||||
* Read list of hosts from the filename given.
|
||||
* Usually "/etc/hosts".
|
||||
* These addresses are not flagged as DNSSEC secure when queried for.
|
||||
*
|
||||
* @param ctx: context.
|
||||
* At this time it is only possible to set configuration before the
|
||||
* first resolve is done.
|
||||
* @param fname: file name string. If NULL "/etc/hosts" is used.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_hosts(struct ub_ctx* ctx, char* fname);
|
||||
|
||||
/**
|
||||
* Add a trust anchor to the given context.
|
||||
* The trust anchor is a string, on one line, that holds a valid DNSKEY or
|
||||
* DS RR.
|
||||
* @param ctx: context.
|
||||
* At this time it is only possible to add trusted keys before the
|
||||
* first resolve is done.
|
||||
* @param ta: string, with zone-format RR on one line.
|
||||
* [domainname] [TTL optional] [type] [class optional] [rdata contents]
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_add_ta(struct ub_ctx* ctx, char* ta);
|
||||
|
||||
/**
|
||||
* Add trust anchors to the given context.
|
||||
* Pass name of a file with DS and DNSKEY records (like from dig or drill).
|
||||
* @param ctx: context.
|
||||
* At this time it is only possible to add trusted keys before the
|
||||
* first resolve is done.
|
||||
* @param fname: filename of file with keyfile with trust anchors.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_add_ta_file(struct ub_ctx* ctx, char* fname);
|
||||
|
||||
/**
|
||||
* Add trust anchors to the given context.
|
||||
* Pass the name of a bind-style config file with trusted-keys{}.
|
||||
* @param ctx: context.
|
||||
* At this time it is only possible to add trusted keys before the
|
||||
* first resolve is done.
|
||||
* @param fname: filename of file with bind-style config entries with trust
|
||||
* anchors.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_trustedkeys(struct ub_ctx* ctx, char* fname);
|
||||
|
||||
/**
|
||||
* Set debug output (and error output) to the specified stream.
|
||||
* Pass NULL to disable. Default is stderr.
|
||||
* @param ctx: context.
|
||||
* @param out: FILE* out file stream to log to.
|
||||
* Type void* to avoid stdio dependency of this header file.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_debugout(struct ub_ctx* ctx, void* out);
|
||||
|
||||
/**
|
||||
* Set debug verbosity for the context
|
||||
* Output is directed to stderr.
|
||||
* @param ctx: context.
|
||||
* @param d: debug level, 0 is off, 1 is very minimal, 2 is detailed,
|
||||
* and 3 is lots.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_debuglevel(struct ub_ctx* ctx, int d);
|
||||
|
||||
/**
|
||||
* Set a context behaviour for asynchronous action.
|
||||
* @param ctx: context.
|
||||
* @param dothread: if true, enables threading and a call to resolve_async()
|
||||
* creates a thread to handle work in the background.
|
||||
* If false, a process is forked to handle work in the background.
|
||||
* Changes to this setting after async() calls have been made have
|
||||
* no effect (delete and re-create the context to change).
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_async(struct ub_ctx* ctx, int dothread);
|
||||
|
||||
/**
|
||||
* Poll a context to see if it has any new results
|
||||
* Do not poll in a loop, instead extract the fd below to poll for readiness,
|
||||
* and then check, or wait using the wait routine.
|
||||
* @param ctx: context.
|
||||
* @return: 0 if nothing to read, or nonzero if a result is available.
|
||||
* If nonzero, call ctx_process() to do callbacks.
|
||||
*/
|
||||
int ub_poll(struct ub_ctx* ctx);
|
||||
|
||||
/**
|
||||
* Wait for a context to finish with results. Calls ub_process() after
|
||||
* the wait for you. After the wait, there are no more outstanding
|
||||
* asynchronous queries.
|
||||
* @param ctx: context.
|
||||
* @return: 0 if OK, else error.
|
||||
*/
|
||||
int ub_wait(struct ub_ctx* ctx);
|
||||
|
||||
/**
|
||||
* Get file descriptor. Wait for it to become readable, at this point
|
||||
* answers are returned from the asynchronous validating resolver.
|
||||
* Then call the ub_process to continue processing.
|
||||
* This routine works immediately after context creation, the fd
|
||||
* does not change.
|
||||
* @param ctx: context.
|
||||
* @return: -1 on error, or file descriptor to use select(2) with.
|
||||
*/
|
||||
int ub_fd(struct ub_ctx* ctx);
|
||||
|
||||
/**
|
||||
* Call this routine to continue processing results from the validating
|
||||
* resolver (when the fd becomes readable).
|
||||
* Will perform necessary callbacks.
|
||||
* @param ctx: context
|
||||
* @return: 0 if OK, else error.
|
||||
*/
|
||||
int ub_process(struct ub_ctx* ctx);
|
||||
|
||||
/**
|
||||
* Perform resolution and validation of the target name.
|
||||
* @param ctx: context.
|
||||
* The context is finalized, and can no longer accept config changes.
|
||||
* @param name: domain name in text format (a zero terminated text string).
|
||||
* @param rrtype: type of RR in host order, 1 is A (address).
|
||||
* @param rrclass: class of RR in host order, 1 is IN (for internet).
|
||||
* @param result: the result data is returned in a newly allocated result
|
||||
* structure. May be NULL on return, return value is set to an error
|
||||
* in that case (out of memory).
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_resolve(struct ub_ctx* ctx, char* name, int rrtype,
|
||||
int rrclass, struct ub_result** result);
|
||||
|
||||
/**
|
||||
* Perform resolution and validation of the target name.
|
||||
* Asynchronous, after a while, the callback will be called with your
|
||||
* data and the result.
|
||||
* @param ctx: context.
|
||||
* If no thread or process has been created yet to perform the
|
||||
* work in the background, it is created now.
|
||||
* The context is finalized, and can no longer accept config changes.
|
||||
* @param name: domain name in text format (a string).
|
||||
* @param rrtype: type of RR in host order, 1 is A.
|
||||
* @param rrclass: class of RR in host order, 1 is IN (for internet).
|
||||
* @param mydata: this data is your own data (you can pass NULL),
|
||||
* and is passed on to the callback function.
|
||||
* @param callback: this is called on completion of the resolution.
|
||||
* It is called as:
|
||||
* void callback(void* mydata, int err, struct ub_result* result)
|
||||
* with mydata: the same as passed here, you may pass NULL,
|
||||
* with err: is 0 when a result has been found.
|
||||
* with result: a newly allocated result structure.
|
||||
* The result may be NULL, in that case err is set.
|
||||
*
|
||||
* If an error happens during processing, your callback will be called
|
||||
* with error set to a nonzero value (and result==NULL).
|
||||
* @param async_id: if you pass a non-NULL value, an identifier number is
|
||||
* returned for the query as it is in progress. It can be used to
|
||||
* cancel the query.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype,
|
||||
int rrclass, void* mydata, ub_callback_t callback, int* async_id);
|
||||
|
||||
/**
|
||||
* Cancel an async query in progress.
|
||||
* Its callback will not be called.
|
||||
*
|
||||
* @param ctx: context.
|
||||
* @param async_id: which query to cancel.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_cancel(struct ub_ctx* ctx, int async_id);
|
||||
|
||||
/**
|
||||
* Free storage associated with a result structure.
|
||||
* @param result: to free
|
||||
*/
|
||||
void ub_resolve_free(struct ub_result* result);
|
||||
|
||||
/**
|
||||
* Convert error value to a human readable string.
|
||||
* @param err: error code from one of the ub_val* functions.
|
||||
* @return pointer to constant text string, zero terminated.
|
||||
*/
|
||||
const char* ub_strerror(int err);
|
||||
|
||||
#endif /* _UB_UNBOUND_H */
|
||||
+9
-1
@@ -146,7 +146,9 @@ if test -z "$LDNSDIR"; then
|
||||
# try to autodetect from Makefile (if present)
|
||||
if test -f Makefile; then
|
||||
eval `grep 'ldnsdir=' Makefile`
|
||||
LDNSDIR="$ldnsdir"
|
||||
if echo "$ldnsdir" | grep -v ldns-src/ >/dev/null 2>&1; then
|
||||
LDNSDIR="$ldnsdir"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -182,6 +184,7 @@ flex -i -t util/configlexer.lex >> util/configlexer.c || error_cleanup "Failed
|
||||
bison -y -d -o util/configparser.c util/configparser.y || error_cleanup "Failed to create configparser"
|
||||
|
||||
# check shared code, ldns-testpkts from ldns examples, if possible.
|
||||
cd ../..
|
||||
if test ! -z "$LDNSDIR"; then
|
||||
if diff -q $LDNSDIR/examples/ldns-testpkts.c testcode/ldns-testpkts.c &&
|
||||
diff -q $LDNSDIR/examples/ldns-testpkts.h testcode/ldns-testpkts.h; then
|
||||
@@ -190,6 +193,7 @@ if test ! -z "$LDNSDIR"; then
|
||||
error_cleanup "ldns-testpkts is different in ldns and unbound"
|
||||
fi
|
||||
fi
|
||||
cd $temp_dir/unbound
|
||||
|
||||
find . -name .c-mode-rc.el -exec rm {} \;
|
||||
find . -name .cvsignore -exec rm {} \;
|
||||
@@ -209,6 +213,10 @@ fi
|
||||
|
||||
replace_all doc/README
|
||||
replace_all doc/unbound.8
|
||||
replace_all doc/unbound.conf.5
|
||||
replace_all doc/unbound-checkconf.8
|
||||
replace_all doc/unbound-host.1
|
||||
replace_all doc/libunbound.3
|
||||
|
||||
info "Renaming Unbound directory to unbound-$version."
|
||||
cd ..
|
||||
|
||||
Vendored
+2
-4
@@ -64,12 +64,11 @@ infra_host_compfunc(void* key1, void* key2)
|
||||
}
|
||||
|
||||
void
|
||||
infra_host_delkeyfunc(void* k, void* ATTR_UNUSED(arg), int il)
|
||||
infra_host_delkeyfunc(void* k, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
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);
|
||||
}
|
||||
@@ -309,12 +308,11 @@ infra_lame_compfunc(void* key1, void* key2)
|
||||
}
|
||||
|
||||
void
|
||||
infra_lame_delkeyfunc(void* k, void* ATTR_UNUSED(arg), int il)
|
||||
infra_lame_delkeyfunc(void* k, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
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);
|
||||
|
||||
Vendored
+2
-2
@@ -267,7 +267,7 @@ size_t infra_host_sizefunc(void* k, void* d);
|
||||
int infra_host_compfunc(void* key1, void* key2);
|
||||
|
||||
/** delete key, and destroy the lock */
|
||||
void infra_host_delkeyfunc(void* k, void* arg, int il);
|
||||
void infra_host_delkeyfunc(void* k, void* arg);
|
||||
|
||||
/** delete data and destroy the lameness hashtable */
|
||||
void infra_host_deldatafunc(void* d, void* arg);
|
||||
@@ -280,7 +280,7 @@ size_t infra_lame_sizefunc(void* k, void* d);
|
||||
int infra_lame_compfunc(void* key1, void* key2);
|
||||
|
||||
/** free key, lock and zonename */
|
||||
void infra_lame_delkeyfunc(void* k, void* arg, int il);
|
||||
void infra_lame_delkeyfunc(void* k, void* arg);
|
||||
|
||||
/** free the lameness data */
|
||||
void infra_lame_deldatafunc(void* d, void* arg);
|
||||
|
||||
Vendored
+9
@@ -47,6 +47,13 @@
|
||||
#include "util/regional.h"
|
||||
#include "util/alloc.h"
|
||||
|
||||
void
|
||||
rrset_markdel(void* key)
|
||||
{
|
||||
struct ub_packed_rrset_key* r = (struct ub_packed_rrset_key*)key;
|
||||
r->id = 0;
|
||||
}
|
||||
|
||||
struct rrset_cache* rrset_cache_create(struct config_file* cfg,
|
||||
struct alloc_cache* alloc)
|
||||
{
|
||||
@@ -57,6 +64,7 @@ struct rrset_cache* rrset_cache_create(struct config_file* cfg,
|
||||
struct rrset_cache *r = (struct rrset_cache*)slabhash_create(slabs,
|
||||
startarray, maxmem, ub_rrset_sizefunc, ub_rrset_compare,
|
||||
ub_rrset_key_delete, rrset_data_delete, alloc);
|
||||
slabhash_setmarkdel(&r->table, &rrset_markdel);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -65,6 +73,7 @@ void rrset_cache_delete(struct rrset_cache* r)
|
||||
if(!r)
|
||||
return;
|
||||
slabhash_delete(&r->table);
|
||||
/* slabhash delete also does free(r), since table is first in struct*/
|
||||
}
|
||||
|
||||
struct rrset_cache* rrset_cache_adjust(struct rrset_cache *r,
|
||||
|
||||
Vendored
+3
@@ -207,4 +207,7 @@ void rrset_update_sec_status(struct rrset_cache* r,
|
||||
void rrset_check_sec_status(struct rrset_cache* r,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
|
||||
/** mark rrset to be deleted, set id=0 */
|
||||
void rrset_markdel(void* key);
|
||||
|
||||
#endif /* SERVICES_CACHE_RRSET_H */
|
||||
|
||||
+112
-29
@@ -86,11 +86,13 @@ verbose_print_addr(struct addrinfo *addr)
|
||||
}
|
||||
|
||||
int
|
||||
create_udp_sock(struct addrinfo *addr)
|
||||
create_udp_sock(struct addrinfo *addr, int v6only)
|
||||
{
|
||||
int s;
|
||||
# if defined(IPV6_V6ONLY)
|
||||
# if defined(IPV6_USE_MIN_MTU)
|
||||
int on=1;
|
||||
# else
|
||||
(void)v6only;
|
||||
# endif
|
||||
verbose_print_addr(addr);
|
||||
if((s = socket(addr->ai_family, addr->ai_socktype, 0)) == -1) {
|
||||
@@ -99,11 +101,14 @@ create_udp_sock(struct addrinfo *addr)
|
||||
}
|
||||
if(addr->ai_family == AF_INET6) {
|
||||
# if defined(IPV6_V6ONLY)
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
&on, (socklen_t)sizeof(on)) < 0) {
|
||||
log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
if(v6only) {
|
||||
int val=(v6only==2)?0:1;
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
&val, (socklen_t)sizeof(val)) < 0) {
|
||||
log_err("setsockopt(..., IPV6_V6ONLY"
|
||||
", ...) failed: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
# if defined(IPV6_USE_MIN_MTU)
|
||||
@@ -135,10 +140,11 @@ create_udp_sock(struct addrinfo *addr)
|
||||
/**
|
||||
* Create and bind TCP listening socket
|
||||
* @param addr: address info ready to make socket.
|
||||
* @param v6only: enable ip6 only flag on ip6 sockets.
|
||||
* @return: the socket. -1 on error.
|
||||
*/
|
||||
static int
|
||||
create_tcp_accept_sock(struct addrinfo *addr)
|
||||
create_tcp_accept_sock(struct addrinfo *addr, int v6only)
|
||||
{
|
||||
int s, flag;
|
||||
#if defined(SO_REUSEADDR) || defined(IPV6_V6ONLY)
|
||||
@@ -158,7 +164,7 @@ create_tcp_accept_sock(struct addrinfo *addr)
|
||||
}
|
||||
#endif /* SO_REUSEADDR */
|
||||
#if defined(IPV6_V6ONLY)
|
||||
if(addr->ai_family == AF_INET6) {
|
||||
if(addr->ai_family == AF_INET6 && v6only) {
|
||||
if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
&on, (socklen_t)sizeof(on)) < 0) {
|
||||
log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
|
||||
@@ -166,6 +172,8 @@ create_tcp_accept_sock(struct addrinfo *addr)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)v6only;
|
||||
#endif /* IPV6_V6ONLY */
|
||||
if(bind(s, (struct sockaddr*)addr->ai_addr, addr->ai_addrlen) != 0) {
|
||||
log_err("can't bind socket: %s", strerror(errno));
|
||||
@@ -192,7 +200,7 @@ create_tcp_accept_sock(struct addrinfo *addr)
|
||||
*/
|
||||
static int
|
||||
make_sock(int stype, const char* ifname, const char* port,
|
||||
struct addrinfo *hints)
|
||||
struct addrinfo *hints, int v6only)
|
||||
{
|
||||
struct addrinfo *res = NULL;
|
||||
int r, s;
|
||||
@@ -204,8 +212,8 @@ make_sock(int stype, const char* ifname, const char* port,
|
||||
return -1;
|
||||
}
|
||||
if(stype == SOCK_DGRAM)
|
||||
s = create_udp_sock(res);
|
||||
else s = create_tcp_accept_sock(res);
|
||||
s = create_udp_sock(res, v6only);
|
||||
else s = create_tcp_accept_sock(res, v6only);
|
||||
freeaddrinfo(res);
|
||||
return s;
|
||||
}
|
||||
@@ -214,11 +222,11 @@ make_sock(int stype, const char* ifname, const char* port,
|
||||
* Add port to open ports list.
|
||||
* @param list: list head. changed.
|
||||
* @param s: fd.
|
||||
* @param is_udp: if fd is UDP.
|
||||
* @param ftype: if fd is UDP.
|
||||
* @return false on failure. list in unchanged then.
|
||||
*/
|
||||
static int
|
||||
port_insert(struct listen_port** list, int s, int is_udp)
|
||||
port_insert(struct listen_port** list, int s, enum listen_type ftype)
|
||||
{
|
||||
struct listen_port* item = (struct listen_port*)malloc(
|
||||
sizeof(struct listen_port));
|
||||
@@ -226,14 +234,67 @@ port_insert(struct listen_port** list, int s, int is_udp)
|
||||
return 0;
|
||||
item->next = *list;
|
||||
item->fd = s;
|
||||
item->is_udp = is_udp;
|
||||
item->ftype = ftype;
|
||||
*list = item;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** set fd to receive source address packet info */
|
||||
static int
|
||||
set_recvpktinfo(int s, int family)
|
||||
{
|
||||
int on = 1;
|
||||
if(family == AF_INET6) {
|
||||
# ifdef IPV6_RECVPKTINFO
|
||||
if(setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
|
||||
&on, (socklen_t)sizeof(on)) < 0) {
|
||||
log_err("setsockopt(..., IPV6_RECVPKTINFO, ...) failed: %s",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
# elif defined(IPV6_PKTINFO)
|
||||
if(setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO,
|
||||
&on, (socklen_t)sizeof(on)) < 0) {
|
||||
log_err("setsockopt(..., IPV6_PKTINFO, ...) failed: %s",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
# else
|
||||
log_err("no IPV6_RECVPKTINFO and no IPV6_PKTINFO option, please "
|
||||
"disable interface-automatic in config");
|
||||
return 0;
|
||||
# endif /* defined IPV6_RECVPKTINFO */
|
||||
|
||||
} else if(family == AF_INET) {
|
||||
# ifdef IP_RECVDSTADDR
|
||||
if(setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
|
||||
&on, (socklen_t)sizeof(on)) < 0) {
|
||||
log_err("setsockopt(..., IP_RECVDSTADDR, ...) failed: %s",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
# elif defined(IP_PKTINFO)
|
||||
if(setsockopt(s, IPPROTO_IP, IP_PKTINFO,
|
||||
&on, (socklen_t)sizeof(on)) < 0) {
|
||||
log_err("setsockopt(..., IP_PKTINFO, ...) failed: %s",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
# else
|
||||
log_err("no IP_RECVDSTADDR or IP_PKTINFO option, please disable "
|
||||
"interface-automatic in config");
|
||||
return 0;
|
||||
# endif /* IP_PKTINFO */
|
||||
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for ports_open. Creates one interface (or NULL for default).
|
||||
* @param ifname: The interface ip address.
|
||||
* @param do_auto: use automatic interface detection.
|
||||
* If enabled, then ifname must be the wildcard name.
|
||||
* @param do_udp: if udp should be used.
|
||||
* @param do_tcp: if udp should be used.
|
||||
* @param hints: for getaddrinfo. family and flags have to be set by caller.
|
||||
@@ -242,25 +303,36 @@ port_insert(struct listen_port** list, int s, int is_udp)
|
||||
* @return: returns false on error.
|
||||
*/
|
||||
static int
|
||||
ports_create_if(const char* ifname, int do_udp, int do_tcp,
|
||||
ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
|
||||
struct addrinfo *hints, const char* port, struct listen_port** list)
|
||||
{
|
||||
int s;
|
||||
if(!do_udp && !do_tcp)
|
||||
return 0;
|
||||
if(do_udp) {
|
||||
if((s = make_sock(SOCK_DGRAM, ifname, port, hints)) == -1)
|
||||
if(do_auto) {
|
||||
if((s = make_sock(SOCK_DGRAM, ifname, port, hints, 1)) == -1)
|
||||
return 0;
|
||||
if(!port_insert(list, s, 1)) {
|
||||
/* getting source addr packet info is highly non-portable */
|
||||
if(!set_recvpktinfo(s, hints->ai_family))
|
||||
return 0;
|
||||
if(!port_insert(list, s, listen_type_udpancil)) {
|
||||
close(s);
|
||||
return 0;
|
||||
}
|
||||
} else if(do_udp) {
|
||||
/* regular udp socket */
|
||||
if((s = make_sock(SOCK_DGRAM, ifname, port, hints, 1)) == -1)
|
||||
return 0;
|
||||
if(!port_insert(list, s, listen_type_udp)) {
|
||||
close(s);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if(do_tcp) {
|
||||
if((s = make_sock(SOCK_STREAM, ifname, port, hints)) == -1) {
|
||||
if((s = make_sock(SOCK_STREAM, ifname, port, hints, 1)) == -1) {
|
||||
return 0;
|
||||
}
|
||||
if(!port_insert(list, s, 0)) {
|
||||
if(!port_insert(list, s, listen_type_tcp)) {
|
||||
close(s);
|
||||
return 0;
|
||||
}
|
||||
@@ -306,11 +378,15 @@ listen_create(struct comm_base* base, struct listen_port* ports,
|
||||
/* create comm points as needed */
|
||||
while(ports) {
|
||||
struct comm_point* cp = NULL;
|
||||
if(ports->is_udp)
|
||||
if(ports->ftype == listen_type_udp)
|
||||
cp = comm_point_create_udp(base, ports->fd,
|
||||
front->udp_buff, cb, cb_arg);
|
||||
else cp = comm_point_create_tcp(base, ports->fd,
|
||||
else if(ports->ftype == listen_type_tcp)
|
||||
cp = comm_point_create_tcp(base, ports->fd,
|
||||
tcp_accept_count, bufsize, cb, cb_arg);
|
||||
else if(ports->ftype == listen_type_udpancil)
|
||||
cp = comm_point_create_udp_ancil(base, ports->fd,
|
||||
front->udp_buff, cb, cb_arg);
|
||||
if(!cp) {
|
||||
log_err("can't create commpoint");
|
||||
listen_delete(front);
|
||||
@@ -383,12 +459,13 @@ listening_ports_open(struct config_file* cfg)
|
||||
struct listen_port* list = NULL;
|
||||
struct addrinfo hints;
|
||||
int i, do_ip4, do_ip6;
|
||||
int do_tcp;
|
||||
int do_tcp, do_auto;
|
||||
char portbuf[32];
|
||||
snprintf(portbuf, sizeof(portbuf), "%d", cfg->port);
|
||||
do_ip4 = cfg->do_ip4;
|
||||
do_ip6 = cfg->do_ip6;
|
||||
do_tcp = cfg->do_tcp;
|
||||
do_auto = cfg->if_automatic && cfg->do_udp;
|
||||
if(cfg->incoming_num_tcp == 0)
|
||||
do_tcp = 0;
|
||||
|
||||
@@ -405,11 +482,16 @@ listening_ports_open(struct config_file* cfg)
|
||||
if(!do_ip4 && !do_ip6) {
|
||||
return NULL;
|
||||
}
|
||||
if(do_auto && (!do_ip4 || !do_ip6)) {
|
||||
log_warn("interface_automatic option does not work when IP4 or IP6 is not enabled. Disabling option.");
|
||||
do_auto = 0;
|
||||
}
|
||||
/* create ip4 and ip6 ports so that return addresses are nice. */
|
||||
if(cfg->num_ifs == 0) {
|
||||
if(do_auto || cfg->num_ifs == 0) {
|
||||
if(do_ip6) {
|
||||
hints.ai_family = AF_INET6;
|
||||
if(!ports_create_if("::1", cfg->do_udp, do_tcp,
|
||||
if(!ports_create_if(do_auto?"::0":"::1",
|
||||
do_auto, cfg->do_udp, do_tcp,
|
||||
&hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
@@ -417,7 +499,8 @@ listening_ports_open(struct config_file* cfg)
|
||||
}
|
||||
if(do_ip4) {
|
||||
hints.ai_family = AF_INET;
|
||||
if(!ports_create_if("127.0.0.1", cfg->do_udp, do_tcp,
|
||||
if(!ports_create_if(do_auto?"0.0.0.0":"127.0.0.1",
|
||||
do_auto, cfg->do_udp, do_tcp,
|
||||
&hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
@@ -428,7 +511,7 @@ listening_ports_open(struct config_file* cfg)
|
||||
if(!do_ip6)
|
||||
continue;
|
||||
hints.ai_family = AF_INET6;
|
||||
if(!ports_create_if(cfg->ifs[i], cfg->do_udp,
|
||||
if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp,
|
||||
do_tcp, &hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
@@ -437,7 +520,7 @@ listening_ports_open(struct config_file* cfg)
|
||||
if(!do_ip4)
|
||||
continue;
|
||||
hints.ai_family = AF_INET;
|
||||
if(!ports_create_if(cfg->ifs[i], cfg->do_udp,
|
||||
if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp,
|
||||
do_tcp, &hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
|
||||
@@ -74,6 +74,18 @@ struct listen_list {
|
||||
struct comm_point* com;
|
||||
};
|
||||
|
||||
/**
|
||||
* type of ports
|
||||
*/
|
||||
enum listen_type {
|
||||
/** udp type */
|
||||
listen_type_udp,
|
||||
/** tcp type */
|
||||
listen_type_tcp,
|
||||
/** udp ipv6 (v4mapped) for use with ancillary data */
|
||||
listen_type_udpancil
|
||||
};
|
||||
|
||||
/**
|
||||
* Single linked list to store shared ports that have been
|
||||
* opened for use by all threads.
|
||||
@@ -84,7 +96,7 @@ struct listen_port {
|
||||
/** file descriptor, open and ready for use */
|
||||
int fd;
|
||||
/** type of file descriptor, udp or tcp */
|
||||
int is_udp;
|
||||
enum listen_type ftype;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -154,8 +166,10 @@ size_t listen_get_mem(struct listen_dnsport* listen);
|
||||
/**
|
||||
* Create and bind nonblocking UDP socket
|
||||
* @param addr: address info ready to make socket.
|
||||
* @param v6only: if enabled, IP6 sockets get IP6ONLY option set.
|
||||
* if enabled with value 2 IP6ONLY option is disabled.
|
||||
* @return: the socket. -1 on error.
|
||||
*/
|
||||
int create_udp_sock(struct addrinfo* addr);
|
||||
int create_udp_sock(struct addrinfo* addr, int v6only);
|
||||
|
||||
#endif /* LISTEN_DNSPORT_H */
|
||||
|
||||
@@ -195,6 +195,7 @@ lz_enter_zone(struct local_zones* zones, const char* name, const char* type,
|
||||
t = local_zone_redirect;
|
||||
else {
|
||||
log_err("bad lz_enter_zone type %s %s", name, type);
|
||||
free(nm);
|
||||
return NULL;
|
||||
}
|
||||
if(!(z=lz_enter_zone_dname(zones, nm, len, labs, t, dclass))) {
|
||||
@@ -234,6 +235,8 @@ get_rr_content(const char* str, uint8_t** nm, uint16_t* type,
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
log_err("error converting RR '%s' to wireformat: %s",
|
||||
str, ldns_get_errorstr_by_id(status));
|
||||
free(*nm);
|
||||
*nm = NULL;
|
||||
return 0;
|
||||
}
|
||||
ldns_buffer_flip(rdata);
|
||||
@@ -434,6 +437,7 @@ lz_enter_rr_into_zone(struct local_zone* z, ldns_buffer* buf,
|
||||
query_dname_compare(z->name, nm) != 0) {
|
||||
log_err("local-data in redirect zone must reside at top of zone"
|
||||
", not at %s", rrstr);
|
||||
free(nm);
|
||||
return 0;
|
||||
}
|
||||
nmlabs = dname_count_size_labels(nm, &nmlen);
|
||||
|
||||
+161
-32
@@ -88,8 +88,7 @@ mesh_state_ref_compare(const void* ap, const void* bp)
|
||||
}
|
||||
|
||||
struct mesh_area*
|
||||
mesh_create(int num_modules, struct module_func_block** modfunc,
|
||||
struct module_env* env)
|
||||
mesh_create(struct module_stack* stack, struct module_env* env)
|
||||
{
|
||||
struct mesh_area* mesh = calloc(1, sizeof(struct mesh_area));
|
||||
if(!mesh) {
|
||||
@@ -102,8 +101,7 @@ mesh_create(int num_modules, struct module_func_block** modfunc,
|
||||
log_err("mesh area alloc: out of memory");
|
||||
return NULL;
|
||||
}
|
||||
mesh->num_modules = num_modules;
|
||||
mesh->modfunc = modfunc;
|
||||
mesh->mods = *stack;
|
||||
mesh->env = env;
|
||||
rbtree_init(&mesh->run, &mesh_state_compare);
|
||||
rbtree_init(&mesh->all, &mesh_state_compare);
|
||||
@@ -157,9 +155,9 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
mesh->num_detached_states++;
|
||||
added = 1;
|
||||
}
|
||||
if(!s->reply_list && s->super_set.count == 0)
|
||||
if(!s->reply_list && !s->cb_list && s->super_set.count == 0)
|
||||
was_detached = 1;
|
||||
if(!s->reply_list)
|
||||
if(!s->reply_list && !s->cb_list)
|
||||
was_noreply = 1;
|
||||
/* add reply to s */
|
||||
if(!mesh_state_add_reply(s, edns, rep, qid, qflags)) {
|
||||
@@ -184,6 +182,52 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
mesh_run(mesh, s, module_event_new, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
uint16_t qflags, struct edns_data* edns, ldns_buffer* buf,
|
||||
uint16_t qid, mesh_cb_func_t cb, void* cb_arg)
|
||||
{
|
||||
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) {
|
||||
return 0;
|
||||
}
|
||||
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->cb_list && s->super_set.count == 0)
|
||||
was_detached = 1;
|
||||
if(!s->reply_list && !s->cb_list)
|
||||
was_noreply = 1;
|
||||
/* add reply to s */
|
||||
if(!mesh_state_add_cb(s, edns, buf, cb, cb_arg, qid, qflags)) {
|
||||
if(added)
|
||||
mesh_state_delete(&s->s);
|
||||
return 0;
|
||||
}
|
||||
/* 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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
|
||||
int is_ok, struct comm_reply* reply)
|
||||
{
|
||||
@@ -237,7 +281,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
|
||||
mstate->s.env = env;
|
||||
mstate->s.mesh_info = mstate;
|
||||
/* init modules */
|
||||
for(i=0; i<env->mesh->num_modules; i++) {
|
||||
for(i=0; i<env->mesh->mods.num; i++) {
|
||||
mstate->s.minfo[i] = NULL;
|
||||
mstate->s.ext_state[i] = module_state_initial;
|
||||
}
|
||||
@@ -253,9 +297,9 @@ mesh_state_cleanup(struct mesh_state* mstate)
|
||||
return;
|
||||
/* de-init modules */
|
||||
mesh = mstate->s.env->mesh;
|
||||
for(i=0; i<mesh->num_modules; i++) {
|
||||
log_assert(fptr_whitelist_mod_clear(mesh->modfunc[i]->clear));
|
||||
(*mesh->modfunc[i]->clear)(&mstate->s, i);
|
||||
for(i=0; i<mesh->mods.num; i++) {
|
||||
log_assert(fptr_whitelist_mod_clear(mesh->mods.mod[i]->clear));
|
||||
(*mesh->mods.mod[i]->clear)(&mstate->s, i);
|
||||
mstate->s.minfo[i] = NULL;
|
||||
mstate->s.ext_state[i] = module_finished;
|
||||
}
|
||||
@@ -273,11 +317,12 @@ mesh_state_delete(struct module_qstate* qstate)
|
||||
mstate = qstate->mesh_info;
|
||||
mesh = mstate->s.env->mesh;
|
||||
mesh_detach_subs(&mstate->s);
|
||||
if(!mstate->reply_list && mstate->super_set.count == 0) {
|
||||
if(!mstate->reply_list && !mstate->cb_list
|
||||
&& mstate->super_set.count == 0) {
|
||||
log_assert(mesh->num_detached_states > 0);
|
||||
mesh->num_detached_states--;
|
||||
}
|
||||
if(mstate->reply_list) {
|
||||
if(mstate->reply_list || mstate->cb_list) {
|
||||
log_assert(mesh->num_reply_states > 0);
|
||||
mesh->num_reply_states--;
|
||||
}
|
||||
@@ -301,7 +346,8 @@ void mesh_detach_subs(struct module_qstate* qstate)
|
||||
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) {
|
||||
if(!ref->s->reply_list && !ref->s->cb_list
|
||||
&& ref->s->super_set.count == 0) {
|
||||
mesh->num_detached_states++;
|
||||
log_assert(mesh->num_detached_states +
|
||||
mesh->num_reply_states <= mesh->all.count);
|
||||
@@ -336,7 +382,7 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
|
||||
*newq = NULL;
|
||||
if(!mesh_state_attachment(qstate->mesh_info, sub))
|
||||
return 0;
|
||||
if(!sub->reply_list && sub->super_set.count == 1) {
|
||||
if(!sub->reply_list && !sub->cb_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--;
|
||||
@@ -415,6 +461,49 @@ timeval_divide(struct timeval* avg, struct timeval* sum, size_t d)
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* callback results to mesh cb 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: callback entry
|
||||
*/
|
||||
static void
|
||||
mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
struct mesh_cb* r)
|
||||
{
|
||||
int secure;
|
||||
/* bogus messages are not made into servfail, sec_status passed
|
||||
* to the callback function */
|
||||
if(rep && rep->security == sec_status_secure)
|
||||
secure = 1;
|
||||
else secure = 0;
|
||||
if(!rep && rcode == LDNS_RCODE_NOERROR)
|
||||
rcode = LDNS_RCODE_SERVFAIL;
|
||||
/* send the reply */
|
||||
if(rcode) {
|
||||
(*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked);
|
||||
} else {
|
||||
size_t udp_size = r->edns.udp_size;
|
||||
ldns_buffer_clear(r->buf);
|
||||
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->buf, 0, 1,
|
||||
m->s.env->scratch, udp_size, &r->edns,
|
||||
(int)(r->edns.bits & EDNS_DO), secure))
|
||||
{
|
||||
(*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf,
|
||||
sec_status_unchecked);
|
||||
}
|
||||
else (*r->cb)(r->cb_arg, LDNS_RCODE_NOERROR, r->buf,
|
||||
rep->security);
|
||||
}
|
||||
m->s.env->mesh->num_reply_addrs--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send reply to mesh reply entry
|
||||
* @param m: mesh state to send it for.
|
||||
@@ -436,6 +525,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
if(rep && rep->security == sec_status_secure)
|
||||
secure = 1;
|
||||
else secure = 0;
|
||||
if(!rep && rcode == LDNS_RCODE_NOERROR)
|
||||
rcode = LDNS_RCODE_SERVFAIL;
|
||||
/* send the reply */
|
||||
if(rcode) {
|
||||
error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo,
|
||||
@@ -477,11 +568,15 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
void mesh_query_done(struct mesh_state* mstate)
|
||||
{
|
||||
struct mesh_reply* r;
|
||||
struct mesh_cb* c;
|
||||
struct reply_info* rep = (mstate->s.return_msg?
|
||||
mstate->s.return_msg->rep:NULL);
|
||||
for(r = mstate->reply_list; r; r = r->next) {
|
||||
mesh_send_reply(mstate, mstate->s.return_rcode, rep, r);
|
||||
}
|
||||
for(c = mstate->cb_list; c; c = c->next) {
|
||||
mesh_do_callback(mstate, mstate->s.return_rcode, rep, c);
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate)
|
||||
@@ -493,8 +588,8 @@ void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate)
|
||||
(void)rbtree_insert(&mesh->run, &ref->s->run_node);
|
||||
/* callback the function to inform super of result */
|
||||
log_assert(fptr_whitelist_mod_inform_super(
|
||||
mesh->modfunc[ref->s->s.curmod]->inform_super));
|
||||
(*mesh->modfunc[ref->s->s.curmod]->inform_super)(&mstate->s,
|
||||
mesh->mods.mod[ref->s->s.curmod]->inform_super));
|
||||
(*mesh->mods.mod[ref->s->s.curmod]->inform_super)(&mstate->s,
|
||||
ref->s->s.curmod, &ref->s->s);
|
||||
}
|
||||
}
|
||||
@@ -514,6 +609,26 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh,
|
||||
return result;
|
||||
}
|
||||
|
||||
int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
|
||||
ldns_buffer* buf, mesh_cb_func_t cb, void* cb_arg,
|
||||
uint16_t qid, uint16_t qflags)
|
||||
{
|
||||
struct mesh_cb* r = regional_alloc(s->s.region,
|
||||
sizeof(struct mesh_cb));
|
||||
if(!r)
|
||||
return 0;
|
||||
r->buf = buf;
|
||||
r->cb = cb;
|
||||
r->cb_arg = cb_arg;
|
||||
r->edns = *edns;
|
||||
r->qid = qid;
|
||||
r->qflags = qflags;
|
||||
r->next = s->cb_list;
|
||||
s->cb_list = r;
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
|
||||
struct comm_reply* rep, uint16_t qid, uint16_t qflags)
|
||||
{
|
||||
@@ -558,16 +673,16 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate,
|
||||
if(mstate->num_activated > MESH_MAX_ACTIVATION) {
|
||||
/* module is looping. Stop it. */
|
||||
log_err("internal error: looping module stopped");
|
||||
log_query_info(VERB_DETAIL, "pass error for qstate",
|
||||
log_query_info(VERB_QUERY, "pass error for qstate",
|
||||
&mstate->s.qinfo);
|
||||
s = module_error;
|
||||
}
|
||||
if(s == module_wait_module) {
|
||||
/* start next module */
|
||||
mstate->s.curmod++;
|
||||
if(mesh->num_modules == mstate->s.curmod) {
|
||||
if(mesh->mods.num == mstate->s.curmod) {
|
||||
log_err("Cannot pass to next module; at last module");
|
||||
log_query_info(VERB_DETAIL, "pass error for qstate",
|
||||
log_query_info(VERB_QUERY, "pass error for qstate",
|
||||
&mstate->s.qinfo);
|
||||
mstate->s.curmod--;
|
||||
return mesh_continue(mesh, mstate, module_error, ev);
|
||||
@@ -602,8 +717,8 @@ void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
|
||||
while(mstate) {
|
||||
/* run the module */
|
||||
log_assert(fptr_whitelist_mod_operate(
|
||||
mesh->modfunc[mstate->s.curmod]->operate));
|
||||
(*mesh->modfunc[mstate->s.curmod]->operate)
|
||||
mesh->mods.mod[mstate->s.curmod]->operate));
|
||||
(*mesh->mods.mod[mstate->s.curmod]->operate)
|
||||
(&mstate->s, ev, mstate->s.curmod, e);
|
||||
|
||||
/* examine results */
|
||||
@@ -611,7 +726,7 @@ void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
|
||||
regional_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));
|
||||
mesh->mods.mod[mstate->s.curmod]->name, strextstate(s));
|
||||
e = NULL;
|
||||
if(mesh_continue(mesh, mstate, s, &ev))
|
||||
continue;
|
||||
@@ -637,13 +752,15 @@ mesh_log_list(struct mesh_area* mesh)
|
||||
struct mesh_state* m;
|
||||
int num = 0;
|
||||
RBTREE_FOR(m, struct mesh_state*, &mesh->all) {
|
||||
snprintf(buf, sizeof(buf), "%d%s%s%s%s%s mod%d %s",
|
||||
snprintf(buf, sizeof(buf), "%d%s%s%s%s%s mod%d %s%s",
|
||||
num++, (m->s.is_priming)?"p":"", /* prime */
|
||||
(m->s.query_flags&BIT_RD)?"RD":"",
|
||||
(m->s.query_flags&BIT_CD)?"CD":"",
|
||||
(m->super_set.count==0)?"d":"", /* detached */
|
||||
(m->sub_set.count!=0)?"c":"", /* children */
|
||||
m->s.curmod, (m->reply_list)?"hr":"nr"); /*hasreply*/
|
||||
m->s.curmod, (m->reply_list)?"rep":"", /*hasreply*/
|
||||
(m->cb_list)?"cb":"" /* callbacks */
|
||||
);
|
||||
log_query_info(VERB_ALGO, buf, &m->s.qinfo);
|
||||
}
|
||||
}
|
||||
@@ -651,23 +768,35 @@ mesh_log_list(struct mesh_area* mesh)
|
||||
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,
|
||||
verbose(VERB_DETAIL, "%s %u recursion states (%u with reply, "
|
||||
"%u detached), %u waiting replies, %u recursion replies "
|
||||
"sent", str, (unsigned)mesh->all.count,
|
||||
(unsigned)mesh->num_reply_states,
|
||||
(unsigned)mesh->num_detached_states,
|
||||
(unsigned)mesh->num_reply_addrs);
|
||||
(unsigned)mesh->num_reply_addrs,
|
||||
(unsigned)mesh->replies_sent);
|
||||
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);
|
||||
log_info("average recursion processing time "
|
||||
"%d.%6.6d sec", (int)avg.tv_sec, (int)avg.tv_usec);
|
||||
log_info("histogram of recursion processing times");
|
||||
timehist_log(mesh->histogram, "recursions");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mesh_stats_clear(struct mesh_area* mesh)
|
||||
{
|
||||
if(!mesh)
|
||||
return;
|
||||
mesh->replies_sent = 0;
|
||||
mesh->replies_sum_wait.tv_sec = 0;
|
||||
mesh->replies_sum_wait.tv_usec = 0;
|
||||
timehist_clear(mesh->histogram);
|
||||
}
|
||||
|
||||
size_t
|
||||
mesh_get_mem(struct mesh_area* mesh)
|
||||
{
|
||||
|
||||
+79
-9
@@ -50,8 +50,10 @@
|
||||
#include "util/netevent.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/module.h"
|
||||
#include "services/modstack.h"
|
||||
struct mesh_state;
|
||||
struct mesh_reply;
|
||||
struct mesh_cb;
|
||||
struct query_info;
|
||||
struct reply_info;
|
||||
struct outbound_entry;
|
||||
@@ -67,10 +69,8 @@ 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;
|
||||
/** active module stack */
|
||||
struct module_stack mods;
|
||||
/** environment for new states */
|
||||
struct module_env* env;
|
||||
|
||||
@@ -117,6 +117,8 @@ struct mesh_state {
|
||||
struct module_qstate s;
|
||||
/** the list of replies to clients for the results */
|
||||
struct mesh_reply* reply_list;
|
||||
/** the list of callbacks for the results */
|
||||
struct mesh_cb* cb_list;
|
||||
/** set of superstates (that want this state's result)
|
||||
* contains struct mesh_state_ref* */
|
||||
rbtree_t super_set;
|
||||
@@ -156,18 +158,45 @@ struct mesh_reply {
|
||||
uint16_t qflags;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mesh result callback func.
|
||||
* called as func(cb_arg, rcode, buffer_with_reply, security);
|
||||
* */
|
||||
typedef void (*mesh_cb_func_t)(void*, int, ldns_buffer*, enum sec_status);
|
||||
|
||||
/**
|
||||
* Callback to result routine
|
||||
*/
|
||||
struct mesh_cb {
|
||||
/** next in list */
|
||||
struct mesh_cb* next;
|
||||
/** edns data from query */
|
||||
struct edns_data edns;
|
||||
/** id of query, in network byteorder. */
|
||||
uint16_t qid;
|
||||
/** flags of query, for reply flags */
|
||||
uint16_t qflags;
|
||||
/** buffer for reply */
|
||||
ldns_buffer* buf;
|
||||
|
||||
/** callback routine for results. if rcode != 0 buf has message.
|
||||
* called as cb(cb_arg, rcode, buf);
|
||||
*/
|
||||
mesh_cb_func_t cb;
|
||||
/** user arg for callback */
|
||||
void* cb_arg;
|
||||
};
|
||||
|
||||
/* ------------------- 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 stack: module stack to activate, copied (as readonly reference).
|
||||
* @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);
|
||||
struct mesh_area* mesh_create(struct module_stack* stack,
|
||||
struct module_env* env);
|
||||
|
||||
/**
|
||||
* Delete mesh, and all query states and replies in it.
|
||||
@@ -191,6 +220,25 @@ 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);
|
||||
|
||||
/**
|
||||
* New query with callback. Create new query state if needed, and
|
||||
* add mesh_cb to it.
|
||||
* 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 buf: buffer for reply contents.
|
||||
* @param qid: query id to reply with.
|
||||
* @param cb: callback function.
|
||||
* @param cb_arg: callback user arg.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
uint16_t qflags, struct edns_data* edns, ldns_buffer* buf,
|
||||
uint16_t qid, mesh_cb_func_t cb, void* cb_arg);
|
||||
|
||||
/**
|
||||
* Handle new event from the wire. A serviced query has returned.
|
||||
* The query state will be made runnable, and the mesh_area will process
|
||||
@@ -332,6 +380,22 @@ int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub);
|
||||
int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
|
||||
struct comm_reply* rep, uint16_t qid, uint16_t qflags);
|
||||
|
||||
/**
|
||||
* Create new callback 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 buf: buffer for reply
|
||||
* @param cb: callback to call with results.
|
||||
* @param cb_arg: callback user arg.
|
||||
* @param qid: ID of reply.
|
||||
* @param qflags: original query flags.
|
||||
* @return: 0 on alloc error.
|
||||
*/
|
||||
int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
|
||||
ldns_buffer* buf, mesh_cb_func_t cb, void* cb_arg, 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
|
||||
@@ -351,6 +415,12 @@ void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
|
||||
*/
|
||||
void mesh_stats(struct mesh_area* mesh, const char* str);
|
||||
|
||||
/**
|
||||
* Clear the stats that the mesh keeps (number of queries serviced)
|
||||
* @param mesh: the mesh
|
||||
*/
|
||||
void mesh_stats_clear(struct mesh_area* mesh);
|
||||
|
||||
/**
|
||||
* Print all the states in the mesh to the log.
|
||||
* @param mesh: the mesh to print all states of.
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* services/modstack.c - stack of modules
|
||||
*
|
||||
* 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 maintain a stack of modules.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "services/modstack.h"
|
||||
#include "util/module.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "iterator/iterator.h"
|
||||
#include "validator/validator.h"
|
||||
|
||||
/** count number of modules (words) in the string */
|
||||
static int
|
||||
count_modules(const char* s)
|
||||
{
|
||||
int num = 0;
|
||||
if(!s)
|
||||
return 0;
|
||||
while(*s) {
|
||||
/* skip whitespace */
|
||||
while(*s && isspace((int)*s))
|
||||
s++;
|
||||
if(*s && !isspace((int)*s)) {
|
||||
/* skip identifier */
|
||||
num++;
|
||||
while(*s && !isspace((int)*s))
|
||||
s++;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
void
|
||||
modstack_init(struct module_stack* stack)
|
||||
{
|
||||
stack->num = 0;
|
||||
stack->mod = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
modstack_config(struct module_stack* stack, const char* module_conf)
|
||||
{
|
||||
int i;
|
||||
verbose(VERB_QUERY, "module config: \"%s\"", module_conf);
|
||||
stack->num = count_modules(module_conf);
|
||||
if(stack->num == 0) {
|
||||
log_err("error: no modules specified");
|
||||
return 0;
|
||||
}
|
||||
if(stack->num > MAX_MODULE) {
|
||||
log_err("error: too many modules (%d max %d)",
|
||||
stack->num, MAX_MODULE);
|
||||
return 0;
|
||||
}
|
||||
stack->mod = (struct module_func_block**)calloc((size_t)
|
||||
stack->num, sizeof(struct module_func_block*));
|
||||
if(!stack->mod) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
for(i=0; i<stack->num; i++) {
|
||||
stack->mod[i] = module_factory(&module_conf);
|
||||
if(!stack->mod[i]) {
|
||||
log_err("Unknown value for next module: '%s'",
|
||||
module_conf);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct
|
||||
module_func_block* module_factory(const char** str)
|
||||
{
|
||||
/* these are the modules available */
|
||||
int num = 2;
|
||||
const char* names[] = {"iterator", "validator", NULL};
|
||||
struct module_func_block* (*fb[])(void) =
|
||||
{&iter_get_funcblock, &val_get_funcblock, NULL};
|
||||
|
||||
int i;
|
||||
const char* s = *str;
|
||||
while(*s && isspace((int)*s))
|
||||
s++;
|
||||
for(i=0; i<num; i++) {
|
||||
if(strncmp(names[i], s, strlen(names[i])) == 0) {
|
||||
s += strlen(names[i]);
|
||||
*str = s;
|
||||
return (*fb[i])();
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
modstack_setup(struct module_stack* stack, const char* module_conf,
|
||||
struct module_env* env)
|
||||
{
|
||||
int i;
|
||||
if(stack->num != 0)
|
||||
modstack_desetup(stack, env);
|
||||
/* fixed setup of the modules */
|
||||
if(!modstack_config(stack, module_conf)) {
|
||||
return 0;
|
||||
}
|
||||
env->need_to_validate = 0; /* set by module init below */
|
||||
for(i=0; i<stack->num; i++) {
|
||||
verbose(VERB_OPS, "init module %d: %s",
|
||||
i, stack->mod[i]->name);
|
||||
log_assert(fptr_whitelist_mod_init(stack->mod[i]->init));
|
||||
if(!(*stack->mod[i]->init)(env, i)) {
|
||||
log_err("module init for module %s failed",
|
||||
stack->mod[i]->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
modstack_desetup(struct module_stack* stack, struct module_env* env)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<stack->num; i++) {
|
||||
log_assert(fptr_whitelist_mod_deinit(stack->mod[i]->deinit));
|
||||
(*stack->mod[i]->deinit)(env, i);
|
||||
}
|
||||
stack->num = 0;
|
||||
free(stack->mod);
|
||||
stack->mod = NULL;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* services/modstack.h - stack of modules
|
||||
*
|
||||
* 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 maintain a stack of modules.
|
||||
*/
|
||||
|
||||
#ifndef SERVICES_MODSTACK_H
|
||||
#define SERVICES_MODSTACK_H
|
||||
struct module_func_block;
|
||||
struct module_env;
|
||||
|
||||
/**
|
||||
* Stack of modules.
|
||||
*/
|
||||
struct module_stack {
|
||||
/** the number of modules */
|
||||
int num;
|
||||
/** the module callbacks, array of num_modules length (ref only) */
|
||||
struct module_func_block** mod;
|
||||
};
|
||||
|
||||
/**
|
||||
* Init a stack of modules
|
||||
* @param stack: initialised as empty.
|
||||
*/
|
||||
void modstack_init(struct module_stack* stack);
|
||||
|
||||
/**
|
||||
* Read config file module settings and set up the modfunc block
|
||||
* @param stack: the stack of modules (empty before call).
|
||||
* @param module_conf: string what modules to insert.
|
||||
* @return false on error
|
||||
*/
|
||||
int modstack_config(struct module_stack* stack, const char* module_conf);
|
||||
|
||||
/**
|
||||
* Get funcblock for module name
|
||||
* @param str: string with module name. Advanced to next value on success.
|
||||
* The string is assumed whitespace separated list of module names.
|
||||
* @return funcblock or NULL on error.
|
||||
*/
|
||||
struct module_func_block* module_factory(const char** str);
|
||||
|
||||
/**
|
||||
* Setup modules. Assigns ids and calls module_init.
|
||||
* @param stack: if not empty beforehand, it will be desetup()ed.
|
||||
* It is then modstack_configged().
|
||||
* @param module_conf: string what modules to insert.
|
||||
* @param env: module environment which is inited by the modules.
|
||||
* environment should have a superalloc, cfg,
|
||||
* env.need_to_validate is set by the modules.
|
||||
* @return on false a module init failed.
|
||||
*/
|
||||
int modstack_setup(struct module_stack* stack, const char* module_conf,
|
||||
struct module_env* env);
|
||||
|
||||
/**
|
||||
* Desetup the modules, deinit, delete.
|
||||
* @param stack: made empty.
|
||||
* @param env: module env for module deinit() calls.
|
||||
*/
|
||||
void modstack_desetup(struct module_stack* stack, struct module_env* env);
|
||||
|
||||
#endif /* SERVICES_MODSTACK_H */
|
||||
+22
-15
@@ -202,13 +202,13 @@ 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) {
|
||||
verbose(VERB_DETAIL, "outnettcp got tcp error %d", error);
|
||||
verbose(VERB_QUERY, "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_addr(VERB_DETAIL,
|
||||
log_addr(VERB_QUERY,
|
||||
"outnettcp: bad ID in reply, from:",
|
||||
&pend->query->addr, pend->query->addrlen);
|
||||
error = NETEVENT_CLOSED;
|
||||
@@ -230,11 +230,11 @@ outnet_udp_cb(struct comm_point* c, void* arg, int error,
|
||||
verbose(VERB_ALGO, "answer cb");
|
||||
|
||||
if(error != NETEVENT_NOERROR) {
|
||||
verbose(VERB_DETAIL, "outnetudp got udp error %d", error);
|
||||
verbose(VERB_QUERY, "outnetudp got udp error %d", error);
|
||||
return 0;
|
||||
}
|
||||
if(ldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
|
||||
verbose(VERB_DETAIL, "outnetudp udp too short");
|
||||
verbose(VERB_QUERY, "outnetudp udp too short");
|
||||
return 0;
|
||||
}
|
||||
log_assert(reply_info);
|
||||
@@ -251,7 +251,7 @@ outnet_udp_cb(struct comm_point* c, void* arg, int error,
|
||||
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 unwanted or unsolicited udp reply dropped.");
|
||||
verbose(VERB_QUERY, "received unwanted or unsolicited udp reply dropped.");
|
||||
log_buf(VERB_ALGO, "dropped message", c->buffer);
|
||||
return 0;
|
||||
}
|
||||
@@ -259,7 +259,7 @@ outnet_udp_cb(struct comm_point* c, void* arg, int error,
|
||||
verbose(VERB_ALGO, "received udp reply.");
|
||||
log_buf(VERB_ALGO, "udp message", c->buffer);
|
||||
if(p->c != c) {
|
||||
verbose(VERB_DETAIL, "received reply id,addr on wrong port. "
|
||||
verbose(VERB_QUERY, "received reply id,addr on wrong port. "
|
||||
"dropped.");
|
||||
return 0;
|
||||
}
|
||||
@@ -288,6 +288,11 @@ open_udp_port_range(const char* ifname, struct addrinfo* hints, int porthint)
|
||||
char portstr[32];
|
||||
if(porthint != -1)
|
||||
snprintf(portstr, sizeof(portstr), "%d", porthint);
|
||||
else if(!ifname) {
|
||||
if(hints->ai_family == AF_INET)
|
||||
ifname = "0.0.0.0";
|
||||
else ifname="::";
|
||||
}
|
||||
|
||||
if((r=getaddrinfo(ifname, ((porthint==-1)?NULL:portstr), hints,
|
||||
&res)) != 0 || !res) {
|
||||
@@ -297,7 +302,7 @@ open_udp_port_range(const char* ifname, struct addrinfo* hints, int porthint)
|
||||
r==EAI_SYSTEM?(char*)strerror(errno):"");
|
||||
return -1;
|
||||
}
|
||||
s = create_udp_sock(res);
|
||||
s = create_udp_sock(res, 1);
|
||||
freeaddrinfo(res);
|
||||
return s;
|
||||
}
|
||||
@@ -1024,14 +1029,15 @@ serviced_tcp_callback(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply r2;
|
||||
sq->pending = NULL; /* removed after this callback */
|
||||
if(error != NETEVENT_NOERROR)
|
||||
log_addr(VERB_DETAIL, "tcp error for address",
|
||||
log_addr(VERB_QUERY, "tcp error for address",
|
||||
&sq->addr, sq->addrlen);
|
||||
if(error==NETEVENT_NOERROR)
|
||||
infra_update_tcp_works(sq->outnet->infra, &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(error==NETEVENT_NOERROR && sq->status == serviced_query_TCP_EDNS &&
|
||||
(LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) ==
|
||||
LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(ldns_buffer_begin(
|
||||
c->buffer)) == LDNS_RCODE_NOTIMPL) ) {
|
||||
if(!infra_edns_update(sq->outnet->infra, &sq->addr,
|
||||
sq->addrlen, -1, time(0)))
|
||||
log_err("Out of memory caching no edns for host");
|
||||
@@ -1057,7 +1063,7 @@ serviced_tcp_initiate(struct outside_network* outnet,
|
||||
{
|
||||
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->addrlen, TCP_AUTH_QUERY_TIMEOUT, serviced_tcp_callback,
|
||||
sq, outnet->rnd);
|
||||
if(!sq->pending) {
|
||||
/* delete from tree so that a retry by above layer does not
|
||||
@@ -1104,8 +1110,9 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error,
|
||||
}
|
||||
}
|
||||
if(error == NETEVENT_NOERROR && sq->status == serviced_query_UDP_EDNS
|
||||
&& LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer))
|
||||
== LDNS_RCODE_FORMERR) {
|
||||
&& (LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer))
|
||||
== LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(
|
||||
ldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOTIMPL)) {
|
||||
/* note no EDNS, fallback without EDNS */
|
||||
if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen,
|
||||
-1, (time_t)now.tv_sec)) {
|
||||
@@ -1172,7 +1179,7 @@ outnet_serviced_query(struct outside_network* outnet,
|
||||
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))) {
|
||||
if(callback_list_find(sq, callback_arg, arg_compare)) {
|
||||
return sq;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +225,9 @@ struct serviced_query {
|
||||
serviced_query_TCP_EDNS,
|
||||
/** TCP without EDNS sent */
|
||||
serviced_query_TCP
|
||||
} status;
|
||||
}
|
||||
/** variable with current status */
|
||||
status;
|
||||
/** true if serviced_query is scheduled for deletion already */
|
||||
int to_be_deleted;
|
||||
/** number of UDP retries */
|
||||
|
||||
@@ -86,20 +86,58 @@ check_mod(struct config_file* cfg, struct module_func_block* fb)
|
||||
regional_destroy(env.scratch);
|
||||
}
|
||||
|
||||
/** check configuration for errors */
|
||||
/** check localzones */
|
||||
static void
|
||||
morechecks(struct config_file* cfg)
|
||||
localzonechecks(struct config_file* cfg)
|
||||
{
|
||||
struct local_zones* zs;
|
||||
if(!(zs = local_zones_create()))
|
||||
fatal_exit("out of memory");
|
||||
if(!local_zones_apply_cfg(zs, cfg))
|
||||
fatal_exit("failed local-zone, local-data configuration");
|
||||
local_zones_delete(zs);
|
||||
}
|
||||
|
||||
/** emit warnings for IP in hosts */
|
||||
static void
|
||||
warn_hosts(const char* typ, struct config_stub* list)
|
||||
{
|
||||
int i;
|
||||
struct sockaddr_storage a;
|
||||
socklen_t alen;
|
||||
struct config_str2list* acl;
|
||||
struct local_zones* zs;
|
||||
struct config_stub* s;
|
||||
struct config_strlist* h;
|
||||
for(s=list; s; s=s->next) {
|
||||
for(h=s->hosts; h; h=h->next) {
|
||||
if(extstrtoaddr(h->str, &a, &alen)) {
|
||||
fprintf(stderr, "unbound-checkconf: warning:"
|
||||
" %s %s: \"%s\" is an IP%s address, "
|
||||
"and when looked up as a host name "
|
||||
"during use may not resolve.\n",
|
||||
s->name, typ, h->str,
|
||||
addr_is_ip6(&a, alen)?"6":"4");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** check interface strings */
|
||||
static void
|
||||
interfacechecks(struct config_file* cfg)
|
||||
{
|
||||
struct sockaddr_storage a;
|
||||
socklen_t alen;
|
||||
int i, j;
|
||||
for(i=0; i<cfg->num_ifs; i++) {
|
||||
if(!ipstrtoaddr(cfg->ifs[i], UNBOUND_DNS_PORT, &a, &alen)) {
|
||||
fatal_exit("cannot parse interface specified as '%s'",
|
||||
cfg->ifs[i]);
|
||||
}
|
||||
for(j=0; j<cfg->num_ifs; j++) {
|
||||
if(i!=j && strcmp(cfg->ifs[i], cfg->ifs[j])==0)
|
||||
fatal_exit("interface: %s present twice, "
|
||||
"cannot bind same ports twice.",
|
||||
cfg->ifs[i]);
|
||||
}
|
||||
}
|
||||
for(i=0; i<cfg->num_out_ifs; i++) {
|
||||
if(!ipstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT,
|
||||
@@ -107,14 +145,56 @@ morechecks(struct config_file* cfg)
|
||||
fatal_exit("cannot parse outgoing-interface "
|
||||
"specified as '%s'", cfg->out_ifs[i]);
|
||||
}
|
||||
for(j=0; j<cfg->num_out_ifs; j++) {
|
||||
if(i!=j && strcmp(cfg->out_ifs[i], cfg->out_ifs[j])==0)
|
||||
fatal_exit("outgoing-interface: %s present "
|
||||
"twice, cannot bind same ports twice.",
|
||||
cfg->out_ifs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** check acl ips */
|
||||
static void
|
||||
aclchecks(struct config_file* cfg)
|
||||
{
|
||||
int d;
|
||||
struct sockaddr_storage a;
|
||||
socklen_t alen;
|
||||
struct config_str2list* acl;
|
||||
for(acl=cfg->acls; acl; acl = acl->next) {
|
||||
if(!netblockstrtoaddr(acl->str, UNBOUND_DNS_PORT, &a, &alen,
|
||||
&i)) {
|
||||
&d)) {
|
||||
fatal_exit("cannot parse access control address %s %s",
|
||||
acl->str, acl->str2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** check file list, every file must be inside the chroot location */
|
||||
static void
|
||||
check_chroot_filelist(const char* desc, struct config_strlist* list,
|
||||
const char* chrootdir)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
if(!chrootdir) return;
|
||||
for(p=list; p; p=p->next) {
|
||||
if(p->str && p->str[0] && strncmp(chrootdir, p->str,
|
||||
strlen(chrootdir)) != 0) {
|
||||
fatal_exit("%s: \"%s\" not in chrootdir %s",
|
||||
desc, p->str, chrootdir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** check configuration for errors */
|
||||
static void
|
||||
morechecks(struct config_file* cfg)
|
||||
{
|
||||
warn_hosts("stub-host", cfg->stubs);
|
||||
warn_hosts("forward-host", cfg->forwards);
|
||||
interfacechecks(cfg);
|
||||
aclchecks(cfg);
|
||||
|
||||
if(cfg->verbosity < 0)
|
||||
fatal_exit("verbosity value < 0");
|
||||
@@ -125,6 +205,10 @@ morechecks(struct config_file* cfg)
|
||||
if(!cfg->do_udp && !cfg->do_tcp)
|
||||
fatal_exit("udp and tcp are both disabled, pointless");
|
||||
|
||||
if(cfg->chrootdir && cfg->chrootdir[0] &&
|
||||
cfg->chrootdir[strlen(cfg->chrootdir)-1] == '/')
|
||||
fatal_exit("chootdir %s has trailing slash '/' please remove.",
|
||||
cfg->chrootdir);
|
||||
if(cfg->chrootdir && strncmp(cfg->chrootdir, cfg->directory,
|
||||
strlen(cfg->chrootdir)) != 0)
|
||||
fatal_exit("working directory %s not in chrootdir %s",
|
||||
@@ -139,6 +223,12 @@ morechecks(struct config_file* cfg)
|
||||
strlen(cfg->chrootdir)) != 0)
|
||||
fatal_exit("log file %s not in chrootdir %s",
|
||||
cfg->logfile, cfg->chrootdir);
|
||||
check_chroot_filelist("file with root-hints",
|
||||
cfg->root_hints, cfg->chrootdir);
|
||||
check_chroot_filelist("trust-anchor-file",
|
||||
cfg->trust_anchor_file_list, cfg->chrootdir);
|
||||
check_chroot_filelist("trusted-keys-file",
|
||||
cfg->trusted_keys_file_list, cfg->chrootdir);
|
||||
|
||||
if(strcmp(cfg->module_conf, "iterator") != 0 &&
|
||||
strcmp(cfg->module_conf, "validator iterator") != 0) {
|
||||
@@ -147,18 +237,12 @@ morechecks(struct config_file* cfg)
|
||||
}
|
||||
|
||||
if(cfg->username && cfg->username[0]) {
|
||||
struct passwd *pwd;
|
||||
if((pwd = getpwnam(cfg->username)) == NULL)
|
||||
if(getpwnam(cfg->username) == NULL)
|
||||
fatal_exit("user '%s' does not exist.", cfg->username);
|
||||
endpwent();
|
||||
}
|
||||
|
||||
if(!(zs = local_zones_create()))
|
||||
fatal_exit("out of memory");
|
||||
if(!local_zones_apply_cfg(zs, cfg))
|
||||
fatal_exit("failed local-zone, local-data configuration");
|
||||
local_zones_print(zs); /* @@@ DEBUG */
|
||||
local_zones_delete(zs);
|
||||
localzonechecks(cfg);
|
||||
}
|
||||
|
||||
/** check config file */
|
||||
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* checkconf/unbound-host.c - replacement for host that supports validation.
|
||||
*
|
||||
* 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 performs functionality like 'host', and also supports validation.
|
||||
* It uses the libunbound library.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "libunbound/unbound.h"
|
||||
#include <ldns/ldns.h>
|
||||
|
||||
/** verbosity for unbound-host app */
|
||||
static int verb = 0;
|
||||
|
||||
/** Give unbound-host usage, and exit (1). */
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
printf("Usage: unbound-host [-vdh] [-c class] [-t type] hostname\n");
|
||||
printf(" [-y key] [-f keyfile] [-F namedkeyfile]\n");
|
||||
printf(" Queries the DNS for information.\n");
|
||||
printf(" The hostname is looked up for IP4, IP6 and mail.\n");
|
||||
printf(" If an ip-address is given a reverse lookup is done.\n");
|
||||
printf(" Use the -v option to see DNSSEC security information.\n");
|
||||
printf(" -t type what type to look for.\n");
|
||||
printf(" -c class what class to look for, if not class IN.\n");
|
||||
printf(" -y 'keystring' specify trust anchor, DS or DNSKEY, like\n");
|
||||
printf(" -y 'example.com DS 31560 5 1 1CFED8478...'\n");
|
||||
printf(" -f keyfile read trust anchors from file, with lines as -y.\n");
|
||||
printf(" -F keyfile read named.conf-style trust anchors.\n");
|
||||
printf(" -v be more verbose, shows nodata and security.\n");
|
||||
printf(" -d debug, traces the action, -d -d shows more.\n");
|
||||
printf(" -h show this usage help.\n");
|
||||
printf("Version %s\n", PACKAGE_VERSION);
|
||||
printf("BSD licensed, see LICENSE in source package for details.\n");
|
||||
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** determine if str is ip4 and put into reverse lookup format */
|
||||
static int
|
||||
isip4(const char* nm, char** res)
|
||||
{
|
||||
struct in_addr addr;
|
||||
/* ddd.ddd.ddd.ddd.in-addr.arpa. is less than 32 */
|
||||
char buf[32];
|
||||
if(inet_pton(AF_INET, nm, &addr) <= 0) {
|
||||
return 0;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "%u.%u.%u.%u.in-addr.arpa",
|
||||
(unsigned)((uint8_t*)&addr)[3], (unsigned)((uint8_t*)&addr)[2],
|
||||
(unsigned)((uint8_t*)&addr)[1], (unsigned)((uint8_t*)&addr)[0]);
|
||||
*res = strdup(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** determine if str is ip6 and put into reverse lookup format */
|
||||
static int
|
||||
isip6(const char* nm, char** res)
|
||||
{
|
||||
struct in6_addr addr;
|
||||
/* [nibble.]{32}.ip6.arpa. is less than 128 */
|
||||
char* hex = "0123456789abcdef";
|
||||
char buf[128];
|
||||
char *p;
|
||||
int i;
|
||||
if(inet_pton(AF_INET6, nm, &addr) <= 0) {
|
||||
return 0;
|
||||
}
|
||||
p = buf;
|
||||
for(i=15; i>=0; i--) {
|
||||
uint8_t b = ((uint8_t*)&addr)[i];
|
||||
*p++ = hex[ (b&0x0f) ];
|
||||
*p++ = '.';
|
||||
*p++ = hex[ (b&0xf0) >> 4 ];
|
||||
*p++ = '.';
|
||||
}
|
||||
snprintf(buf+16*4, sizeof(buf)-16*4, "ip6.arpa");
|
||||
*res = strdup(buf);
|
||||
if(!*res) {
|
||||
fprintf(stderr, "error: out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** massage input name */
|
||||
static char*
|
||||
massage_qname(const char* nm, int* reverse)
|
||||
{
|
||||
/* recognise IP4 and IP6, create reverse addresses if needed */
|
||||
char* res;
|
||||
if(isip4(nm, &res)) {
|
||||
*reverse = 1;
|
||||
} else if(isip6(nm, &res)) {
|
||||
*reverse = 1;
|
||||
} else {
|
||||
res = strdup(nm);
|
||||
}
|
||||
if(!res) {
|
||||
fprintf(stderr, "error: out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** massage input type */
|
||||
static int
|
||||
massage_type(const char* t, int reverse, int* multi)
|
||||
{
|
||||
if(t) {
|
||||
int r = ldns_get_rr_type_by_name(t);
|
||||
if(r == 0 && strcasecmp(t, "TYPE0") != 0 &&
|
||||
strcmp(t, "") != 0) {
|
||||
fprintf(stderr, "error unknown type %s\n", t);
|
||||
exit(1);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
if(!t && reverse)
|
||||
return LDNS_RR_TYPE_PTR;
|
||||
*multi = 1;
|
||||
return LDNS_RR_TYPE_A;
|
||||
}
|
||||
|
||||
/** massage input class */
|
||||
static int
|
||||
massage_class(const char* c)
|
||||
{
|
||||
if(c) {
|
||||
int r = ldns_get_rr_class_by_name(c);
|
||||
if(r == 0 && strcasecmp(c, "CLASS0") != 0 &&
|
||||
strcmp(c, "") != 0) {
|
||||
fprintf(stderr, "error unknown class %s\n", c);
|
||||
exit(1);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
return LDNS_RR_CLASS_IN;
|
||||
}
|
||||
|
||||
/** nice security status string */
|
||||
static const char*
|
||||
secure_str(struct ub_result* result)
|
||||
{
|
||||
if(result->secure) return "(secure)";
|
||||
if(result->bogus) return "(BOGUS (security failure))";
|
||||
return "(insecure)";
|
||||
}
|
||||
|
||||
/** nice string for type */
|
||||
static void
|
||||
pretty_type(char* s, size_t len, int t)
|
||||
{
|
||||
const ldns_rr_descriptor *d = ldns_rr_descript((uint16_t)t);
|
||||
if(d) {
|
||||
snprintf(s, len, "%s", d->_name);
|
||||
} else {
|
||||
snprintf(s, len, "TYPE%d", t);
|
||||
}
|
||||
}
|
||||
|
||||
/** nice string for class */
|
||||
static void
|
||||
pretty_class(char* s, size_t len, int c)
|
||||
{
|
||||
ldns_lookup_table *cl = ldns_lookup_by_id(ldns_rr_classes, c);
|
||||
if(cl) {
|
||||
snprintf(s, len, "%s", cl->name);
|
||||
} else {
|
||||
snprintf(s, len, "CLASS%d", c);
|
||||
}
|
||||
}
|
||||
|
||||
/** nice string for rcode */
|
||||
static void
|
||||
pretty_rcode(char* s, size_t len, int r)
|
||||
{
|
||||
ldns_lookup_table *rcode = ldns_lookup_by_id(ldns_rcodes, r);
|
||||
if(rcode) {
|
||||
snprintf(s, len, "%s", rcode->name);
|
||||
} else {
|
||||
snprintf(s, len, "RCODE%d", r);
|
||||
}
|
||||
}
|
||||
|
||||
/** convert and print rdata */
|
||||
static void
|
||||
print_rd(int t, char* data, size_t len)
|
||||
{
|
||||
size_t i, pos = 0;
|
||||
uint8_t* rd = (uint8_t*)malloc(len+2);
|
||||
ldns_rr* rr = ldns_rr_new();
|
||||
ldns_status status;
|
||||
if(!rd || !rr) {
|
||||
fprintf(stderr, "out of memory");
|
||||
exit(1);
|
||||
}
|
||||
ldns_rr_set_type(rr, t);
|
||||
ldns_write_uint16(rd, len);
|
||||
memmove(rd+2, data, len);
|
||||
ldns_rr_set_owner(rr, NULL);
|
||||
status = ldns_wire2rdf(rr, rd, len+2, &pos);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
|
||||
free(rd);
|
||||
printf("error_printing_data");
|
||||
}
|
||||
for(i=0; i<ldns_rr_rd_count(rr); i++) {
|
||||
printf(" ");
|
||||
ldns_rdf_print(stdout, ldns_rr_rdf(rr, i));
|
||||
}
|
||||
ldns_rr_free(rr);
|
||||
free(rd);
|
||||
}
|
||||
|
||||
/** pretty line of RR data for results */
|
||||
static void
|
||||
pretty_rdata(char* q, char* cstr, char* tstr, int t, const char* sec,
|
||||
char* data, size_t len)
|
||||
{
|
||||
printf("%s", q);
|
||||
if(strcmp(cstr, "IN") != 0)
|
||||
printf(" in class %s", cstr);
|
||||
if(t == LDNS_RR_TYPE_A)
|
||||
printf(" has address");
|
||||
else if(t == LDNS_RR_TYPE_AAAA)
|
||||
printf(" has IPv6 address");
|
||||
else if(t == LDNS_RR_TYPE_MX)
|
||||
printf(" mail is handled by");
|
||||
else if(t == LDNS_RR_TYPE_PTR)
|
||||
printf(" domain name pointer");
|
||||
else printf(" has %s record", tstr);
|
||||
print_rd(t, data, len);
|
||||
if(verb > 0)
|
||||
printf(" %s", sec);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/** pretty line of output for results */
|
||||
static void
|
||||
pretty_output(char* q, int t, int c, struct ub_result* result, int docname)
|
||||
{
|
||||
int i;
|
||||
const char *secstatus = secure_str(result);
|
||||
char tstr[16];
|
||||
char cstr[16];
|
||||
char rcodestr[16];
|
||||
pretty_type(tstr, 16, t);
|
||||
pretty_class(cstr, 16, c);
|
||||
pretty_rcode(rcodestr, 16, result->rcode);
|
||||
|
||||
if(!result->havedata && result->rcode) {
|
||||
printf("Host %s not found: %d(%s).",
|
||||
q, result->rcode, rcodestr);
|
||||
if(verb > 0)
|
||||
printf(" %s", secstatus);
|
||||
printf("\n");
|
||||
return;
|
||||
}
|
||||
if(docname && result->canonname &&
|
||||
result->canonname != result->qname) {
|
||||
printf("%s is an alias for %s", result->qname,
|
||||
result->canonname);
|
||||
if(verb > 0)
|
||||
printf(" %s", secstatus);
|
||||
printf("\n");
|
||||
}
|
||||
/* remove trailing . from long canonnames for nicer output */
|
||||
if(result->canonname && strlen(result->canonname) > 1 &&
|
||||
result->canonname[strlen(result->canonname)-1] == '.')
|
||||
result->canonname[strlen(result->canonname)-1] = 0;
|
||||
if(!result->havedata) {
|
||||
if(verb > 0) {
|
||||
printf("%s", result->canonname?result->canonname:q);
|
||||
if(strcmp(cstr, "IN") != 0)
|
||||
printf(" in class %s", cstr);
|
||||
if(t == LDNS_RR_TYPE_A)
|
||||
printf(" has no address");
|
||||
else if(t == LDNS_RR_TYPE_AAAA)
|
||||
printf(" has no IPv6 address");
|
||||
else if(t == LDNS_RR_TYPE_PTR)
|
||||
printf(" has no domain name ptr");
|
||||
else if(t == LDNS_RR_TYPE_MX)
|
||||
printf(" has no mail handler record");
|
||||
else printf(" has no %s record", tstr);
|
||||
printf(" %s\n", secstatus);
|
||||
}
|
||||
/* else: emptiness to indicate no data */
|
||||
return;
|
||||
}
|
||||
i=0;
|
||||
while(result->data[i])
|
||||
{
|
||||
pretty_rdata(
|
||||
result->canonname?result->canonname:q,
|
||||
cstr, tstr, t, secstatus, result->data[i],
|
||||
(size_t)result->len[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/** perform a lookup and printout return if domain existed */
|
||||
static int
|
||||
dnslook(struct ub_ctx* ctx, char* q, int t, int c, int docname)
|
||||
{
|
||||
int ret;
|
||||
struct ub_result* result;
|
||||
|
||||
ret = ub_resolve(ctx, q, t, c, &result);
|
||||
if(ret != 0) {
|
||||
fprintf(stderr, "resolve error: %s\n", ub_strerror(ret));
|
||||
exit(1);
|
||||
}
|
||||
pretty_output(q, t, c, result, docname);
|
||||
ret = result->nxdomain;
|
||||
ub_resolve_free(result);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** perform host lookup */
|
||||
static void
|
||||
lookup(struct ub_ctx* ctx, const char* nm, const char* qt, const char* qc)
|
||||
{
|
||||
/* massage input into a query name, type and class */
|
||||
int multi = 0; /* no type, so do A, AAAA, MX */
|
||||
int reverse = 0; /* we are doing a reverse lookup */
|
||||
char* realq = massage_qname(nm, &reverse);
|
||||
int t = massage_type(qt, reverse, &multi);
|
||||
int c = massage_class(qc);
|
||||
|
||||
/* perform the query */
|
||||
if(multi) {
|
||||
if(!dnslook(ctx, realq, LDNS_RR_TYPE_A, c, 1)) {
|
||||
/* domain exists, lookup more */
|
||||
(void)dnslook(ctx, realq, LDNS_RR_TYPE_AAAA, c, 0);
|
||||
(void)dnslook(ctx, realq, LDNS_RR_TYPE_MX, c, 0);
|
||||
}
|
||||
} else {
|
||||
(void)dnslook(ctx, realq, t, c, 1);
|
||||
}
|
||||
ub_ctx_delete(ctx);
|
||||
free(realq);
|
||||
}
|
||||
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern int optind;
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern char* optarg;
|
||||
|
||||
/** Main routine for checkconf */
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int c;
|
||||
char* qclass = NULL;
|
||||
char* qtype = NULL;
|
||||
struct ub_ctx* ctx = NULL;
|
||||
int debuglevel = 0;
|
||||
|
||||
ctx = ub_ctx_create();
|
||||
if(!ctx) {
|
||||
fprintf(stderr, "error: out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* parse the options */
|
||||
while( (c=getopt(argc, argv, "F:c:df:ht:vy:")) != -1) {
|
||||
switch(c) {
|
||||
case 'c':
|
||||
qclass = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
debuglevel++;
|
||||
if(debuglevel < 2)
|
||||
debuglevel = 2; /* at least VERB_DETAIL */
|
||||
ub_ctx_debuglevel(ctx, debuglevel);
|
||||
break;
|
||||
case 't':
|
||||
qtype = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
verb++;
|
||||
break;
|
||||
case 'y':
|
||||
ub_ctx_add_ta(ctx, optarg);
|
||||
break;
|
||||
case 'f':
|
||||
ub_ctx_add_ta_file(ctx, optarg);
|
||||
break;
|
||||
case 'F':
|
||||
ub_ctx_trustedkeys(ctx, optarg);
|
||||
break;
|
||||
case '?':
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if(argc != 1)
|
||||
usage();
|
||||
|
||||
lookup(ctx, argv[0], qtype, qclass);
|
||||
return 0;
|
||||
}
|
||||
@@ -114,3 +114,49 @@ acl_list_cmp(const void* ATTR_UNUSED(k1), const void* ATTR_UNUSED(k2))
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libworker_send_packet(ldns_buffer* ATTR_UNUSED(pkt),
|
||||
struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
socklen_t ATTR_UNUSED(addrlen), int ATTR_UNUSED(timeout),
|
||||
struct module_qstate* ATTR_UNUSED(q), int ATTR_UNUSED(use_tcp))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct outbound_entry* libworker_send_query(uint8_t* ATTR_UNUSED(qname),
|
||||
size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
|
||||
uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
|
||||
int ATTR_UNUSED(dnssec), struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
socklen_t ATTR_UNUSED(addrlen), struct module_qstate* ATTR_UNUSED(q))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libworker_handle_reply(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libworker_handle_service_reply(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int context_query_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void worker_stat_timer_cb(void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
@@ -0,0 +1,472 @@
|
||||
/*
|
||||
* testcode/asynclook.c - debug program perform async libunbound queries.
|
||||
*
|
||||
* Copyright (c) 2008, 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 program shows the results from several background lookups,
|
||||
* while printing time in the foreground.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "libunbound/unbound.h"
|
||||
#include "util/locks.h"
|
||||
#include "util/log.h"
|
||||
|
||||
/**
|
||||
* result list for the lookups
|
||||
*/
|
||||
struct lookinfo {
|
||||
/** name to look up */
|
||||
char* name;
|
||||
/** tracking number that can be used to cancel the query */
|
||||
int async_id;
|
||||
/** error code from libunbound */
|
||||
int err;
|
||||
/** result from lookup */
|
||||
struct ub_result* result;
|
||||
};
|
||||
|
||||
/** global variable to see how many queries we have left */
|
||||
static int num_wait = 0;
|
||||
|
||||
/** usage information for asynclook */
|
||||
void usage(char* argv[])
|
||||
{
|
||||
printf("usage: %s [options] name ...\n", argv[0]);
|
||||
printf("names are looked up at the same time, asynchronously.\n");
|
||||
printf(" -b : use blocking requests\n");
|
||||
printf(" -c : cancel the requests\n");
|
||||
printf(" -d : enable debug output\n");
|
||||
printf(" -f addr : use addr, forward to that server\n");
|
||||
printf(" -h : this help message\n");
|
||||
printf(" -H fname : read hosts from fname\n");
|
||||
printf(" -r fname : read resolv.conf from fname\n");
|
||||
printf(" -t : use a resolver thread instead of forking a process\n");
|
||||
printf(" -x : perform extended threaded test\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** print result from lookup nicely */
|
||||
static void
|
||||
print_result(struct lookinfo* info)
|
||||
{
|
||||
char buf[100];
|
||||
if(info->err) /* error (from libunbound) */
|
||||
printf("%s: error %s\n", info->name,
|
||||
ub_strerror(info->err));
|
||||
else if(!info->result)
|
||||
printf("%s: cancelled\n", info->name);
|
||||
else if(info->result->havedata)
|
||||
printf("%s: %s\n", info->name,
|
||||
inet_ntop(AF_INET, info->result->data[0],
|
||||
buf, (socklen_t)sizeof(buf)));
|
||||
else {
|
||||
/* there is no data, why that? */
|
||||
if(info->result->rcode == 0 /*noerror*/ ||
|
||||
info->result->nxdomain)
|
||||
printf("%s: no data %s\n", info->name,
|
||||
info->result->nxdomain?"(no such host)":
|
||||
"(no IP4 address)");
|
||||
else /* some error (from the server) */
|
||||
printf("%s: DNS error %d\n", info->name,
|
||||
info->result->rcode);
|
||||
}
|
||||
}
|
||||
|
||||
/** this is a function of type ub_callback_t */
|
||||
static void
|
||||
lookup_is_done(void* mydata, int err, struct ub_result* result)
|
||||
{
|
||||
/* cast mydata back to the correct type */
|
||||
struct lookinfo* info = (struct lookinfo*)mydata;
|
||||
fprintf(stderr, "name %s resolved\n", info->name);
|
||||
info->err = err;
|
||||
info->result = result;
|
||||
/* one less to wait for */
|
||||
num_wait--;
|
||||
}
|
||||
|
||||
/** check error, if bad, exit with error message */
|
||||
static void
|
||||
checkerr(const char* desc, int err)
|
||||
{
|
||||
if(err != 0) {
|
||||
printf("%s error: %s\n", desc, ub_strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
|
||||
/** only one process can communicate with async worker */
|
||||
#define NUMTHR 1
|
||||
#else /* have threads */
|
||||
/** number of threads to make in extended test */
|
||||
#define NUMTHR 10
|
||||
#endif
|
||||
|
||||
/** struct for extended thread info */
|
||||
struct ext_thr_info {
|
||||
/** thread num for debug */
|
||||
int thread_num;
|
||||
/** thread id */
|
||||
ub_thread_t tid;
|
||||
/** context */
|
||||
struct ub_ctx* ctx;
|
||||
/** size of array to query */
|
||||
int argc;
|
||||
/** array of names to query */
|
||||
char** argv;
|
||||
/** number of queries to do */
|
||||
int numq;
|
||||
};
|
||||
|
||||
/** if true, we are testing against 'localhost' and extra checking is done */
|
||||
static int q_is_localhost = 0;
|
||||
|
||||
/** check result structure for the 'correct' answer */
|
||||
static void
|
||||
ext_check_result(const char* desc, int err, struct ub_result* result)
|
||||
{
|
||||
checkerr(desc, err);
|
||||
if(result == NULL) {
|
||||
printf("%s: error result is NULL.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(q_is_localhost) {
|
||||
if(strcmp(result->qname, "localhost") != 0) {
|
||||
printf("%s: error result has wrong qname.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->qtype != LDNS_RR_TYPE_A) {
|
||||
printf("%s: error result has wrong qtype.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->qclass != LDNS_RR_CLASS_IN) {
|
||||
printf("%s: error result has wrong qclass.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->data == NULL) {
|
||||
printf("%s: error result->data is NULL.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->len == NULL) {
|
||||
printf("%s: error result->len is NULL.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->rcode != 0) {
|
||||
printf("%s: error result->rcode is set.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->havedata == 0) {
|
||||
printf("%s: error result->havedata is unset.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->nxdomain != 0) {
|
||||
printf("%s: error result->nxdomain is set.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->secure || result->bogus) {
|
||||
printf("%s: error result->secure or bogus is set.\n",
|
||||
desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->data[0] == NULL) {
|
||||
printf("%s: error result->data[0] is NULL.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->len[0] != 4) {
|
||||
printf("%s: error result->len[0] is wrong.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->len[1] != 0 || result->data[1] != NULL) {
|
||||
printf("%s: error result->data[1] or len[1] is "
|
||||
"wrong.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** extended bg result callback, this function is ub_callback_t */
|
||||
static void
|
||||
ext_callback(void* mydata, int err, struct ub_result* result)
|
||||
{
|
||||
int* my_id = (int*)mydata;
|
||||
int doprint = 0;
|
||||
if(my_id) {
|
||||
/* I have an id, make sure we are not cancelled */
|
||||
if(*my_id == 0) {
|
||||
printf("error: query returned, but was cancelled\n");
|
||||
abort();
|
||||
exit(1);
|
||||
}
|
||||
if(doprint)
|
||||
printf("cb %d: ", *my_id);
|
||||
}
|
||||
ext_check_result("ext_callback", err, result);
|
||||
log_assert(result);
|
||||
if(doprint) {
|
||||
struct lookinfo pi;
|
||||
pi.name = result?result->qname:"noname";
|
||||
pi.result = result;
|
||||
pi.err = 0;
|
||||
print_result(&pi);
|
||||
}
|
||||
ub_resolve_free(result);
|
||||
}
|
||||
|
||||
/** extended thread worker */
|
||||
static void*
|
||||
ext_thread(void* arg)
|
||||
{
|
||||
struct ext_thr_info* inf = (struct ext_thr_info*)arg;
|
||||
int i, r;
|
||||
struct ub_result* result;
|
||||
int* async_ids = NULL;
|
||||
log_thread_set(&inf->thread_num);
|
||||
if(inf->thread_num > NUMTHR*2/3) {
|
||||
async_ids = (int*)calloc((size_t)inf->numq, sizeof(int));
|
||||
if(!async_ids) {
|
||||
printf("out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
for(i=0; i<inf->numq; i++) {
|
||||
if(async_ids) {
|
||||
r = ub_resolve_async(inf->ctx,
|
||||
inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
|
||||
LDNS_RR_CLASS_IN, &async_ids[i], ext_callback,
|
||||
&async_ids[i]);
|
||||
checkerr("ub_resolve_async", r);
|
||||
if(i > 100) {
|
||||
r = ub_cancel(inf->ctx, async_ids[i-100]);
|
||||
checkerr("ub_cancel", r);
|
||||
async_ids[i-100]=0;
|
||||
}
|
||||
} else if(inf->thread_num > NUMTHR/2) {
|
||||
/* async */
|
||||
r = ub_resolve_async(inf->ctx,
|
||||
inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
|
||||
LDNS_RR_CLASS_IN, NULL, ext_callback, NULL);
|
||||
checkerr("ub_resolve_async", r);
|
||||
} else {
|
||||
/* blocking */
|
||||
r = ub_resolve(inf->ctx, inf->argv[i%inf->argc],
|
||||
LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN, &result);
|
||||
ext_check_result("ub_resolve", r, result);
|
||||
}
|
||||
}
|
||||
if(inf->thread_num > NUMTHR/2) {
|
||||
r = ub_wait(inf->ctx);
|
||||
checkerr("ub_ctx_wait", r);
|
||||
}
|
||||
free(async_ids);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** perform extended threaded test */
|
||||
static int
|
||||
ext_test(struct ub_ctx* ctx, int argc, char** argv)
|
||||
{
|
||||
struct ext_thr_info inf[NUMTHR];
|
||||
int i;
|
||||
if(argc == 1 && strcmp(argv[0], "localhost") == 0)
|
||||
q_is_localhost = 1;
|
||||
printf("extended test start (%d threads)\n", NUMTHR);
|
||||
for(i=0; i<NUMTHR; i++) {
|
||||
/* 0 = this, 1 = library bg worker */
|
||||
inf[i].thread_num = i+2;
|
||||
inf[i].ctx = ctx;
|
||||
inf[i].argc = argc;
|
||||
inf[i].argv = argv;
|
||||
inf[i].numq = 1000;
|
||||
ub_thread_create(&inf[i].tid, ext_thread, &inf[i]);
|
||||
}
|
||||
/* the work happens here */
|
||||
for(i=0; i<NUMTHR; i++) {
|
||||
ub_thread_join(inf[i].tid);
|
||||
}
|
||||
printf("extended test end\n");
|
||||
ub_ctx_delete(ctx);
|
||||
checklock_stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern int optind;
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern char* optarg;
|
||||
|
||||
/** main program for asynclook */
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int c;
|
||||
struct ub_ctx* ctx;
|
||||
struct lookinfo* lookups;
|
||||
int i, r, cancel=0, blocking=0, ext=0;
|
||||
|
||||
/* lock debug start (if any) */
|
||||
checklock_start();
|
||||
|
||||
/* create context */
|
||||
ctx = ub_ctx_create();
|
||||
if(!ctx) {
|
||||
printf("could not create context, %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* command line options */
|
||||
if(argc == 1) {
|
||||
usage(argv);
|
||||
}
|
||||
while( (c=getopt(argc, argv, "bcdf:hH:r:tx")) != -1) {
|
||||
switch(c) {
|
||||
case 'd':
|
||||
r = ub_ctx_debuglevel(ctx, 3);
|
||||
checkerr("ub_ctx_debuglevel", r);
|
||||
break;
|
||||
case 't':
|
||||
r = ub_ctx_async(ctx, 1);
|
||||
checkerr("ub_ctx_async", r);
|
||||
break;
|
||||
case 'c':
|
||||
cancel = 1;
|
||||
break;
|
||||
case 'b':
|
||||
blocking = 1;
|
||||
break;
|
||||
case 'r':
|
||||
r = ub_ctx_resolvconf(ctx, optarg);
|
||||
if(r != 0) {
|
||||
printf("ub_ctx_resolvconf "
|
||||
"error: %s : %s\n",
|
||||
ub_strerror(r),
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
r = ub_ctx_hosts(ctx, optarg);
|
||||
if(r != 0) {
|
||||
printf("ub_ctx_hosts "
|
||||
"error: %s : %s\n",
|
||||
ub_strerror(r),
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
r = ub_ctx_set_fwd(ctx, optarg);
|
||||
checkerr("ub_ctx_set_fwd", r);
|
||||
break;
|
||||
case 'x':
|
||||
ext = 1;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
default:
|
||||
usage(argv);
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if(ext)
|
||||
return ext_test(ctx, argc, argv);
|
||||
|
||||
/* allocate array for results. */
|
||||
lookups = (struct lookinfo*)calloc((size_t)argc,
|
||||
sizeof(struct lookinfo));
|
||||
if(!lookups) {
|
||||
printf("out of memory\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* perform asyncronous calls */
|
||||
num_wait = argc;
|
||||
for(i=0; i<argc; i++) {
|
||||
lookups[i].name = argv[i];
|
||||
if(blocking) {
|
||||
fprintf(stderr, "lookup %s\n", argv[i]);
|
||||
r = ub_resolve(ctx, argv[i], LDNS_RR_TYPE_A,
|
||||
LDNS_RR_CLASS_IN, &lookups[i].result);
|
||||
checkerr("ub_resolve", r);
|
||||
} else {
|
||||
fprintf(stderr, "start async lookup %s\n", argv[i]);
|
||||
r = ub_resolve_async(ctx, argv[i], LDNS_RR_TYPE_A,
|
||||
LDNS_RR_CLASS_IN, &lookups[i], &lookup_is_done,
|
||||
&lookups[i].async_id);
|
||||
checkerr("ub_resolve_async", r);
|
||||
}
|
||||
}
|
||||
if(blocking)
|
||||
num_wait = 0;
|
||||
else if(cancel) {
|
||||
for(i=0; i<argc; i++) {
|
||||
fprintf(stderr, "cancel %s\n", argv[i]);
|
||||
r = ub_cancel(ctx, lookups[i].async_id);
|
||||
checkerr("ub_cancel", r);
|
||||
}
|
||||
num_wait = 0;
|
||||
}
|
||||
|
||||
/* wait while the hostnames are looked up. Do something useful here */
|
||||
if(num_wait > 0)
|
||||
for(i=0; i<1000; i++) {
|
||||
usleep(100000);
|
||||
fprintf(stderr, "%g seconds passed\n", 0.1*(double)i);
|
||||
r = ub_process(ctx);
|
||||
checkerr("ub_process", r);
|
||||
if(num_wait == 0)
|
||||
break;
|
||||
}
|
||||
if(i>=999) {
|
||||
printf("timed out\n");
|
||||
return 0;
|
||||
}
|
||||
printf("lookup complete\n");
|
||||
|
||||
/* print lookup results */
|
||||
for(i=0; i<argc; i++) {
|
||||
print_result(&lookups[i]);
|
||||
ub_resolve_free(lookups[i].result);
|
||||
}
|
||||
|
||||
ub_ctx_delete(ctx);
|
||||
free(lookups);
|
||||
checklock_stop();
|
||||
return 0;
|
||||
}
|
||||
@@ -658,7 +658,9 @@ static void* checklock_main(void* arg)
|
||||
/* Hack to get same numbers as in log file */
|
||||
thr->num = *(int*)(thr->arg);
|
||||
log_assert(thr->num < THRDEBUG_MAX_THREADS);
|
||||
log_assert(thread_infos[thr->num] == NULL);
|
||||
/* as an aside, due to this, won't work for libunbound bg thread */
|
||||
if(thread_infos[thr->num] != NULL)
|
||||
log_warn("thread warning, thr->num %d not NULL", thr->num);
|
||||
thread_infos[thr->num] = thr;
|
||||
LOCKRET(pthread_setspecific(thr_debug_key, thr));
|
||||
if(check_locking_order)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
NEED_SPLINT='00-lint.tpkg'
|
||||
NEED_DOXYGEN='01-doc.tpkg'
|
||||
NEED_LDNS_TESTNS='fwd_no_edns.tpkg fwd_tcp_tc.tpkg fwd_tcp.tpkg fwd_three_service.tpkg fwd_three.tpkg fwd_ttlexpire.tpkg fwd_udp.tpkg fwd_tcp_tc6.tpkg fwd_compress_c00c.tpkg'
|
||||
NEED_LDNS_TESTNS='fwd_no_edns.tpkg fwd_tcp_tc.tpkg fwd_tcp.tpkg fwd_three_service.tpkg fwd_three.tpkg fwd_ttlexpire.tpkg fwd_udp.tpkg fwd_tcp_tc6.tpkg fwd_compress_c00c.tpkg fwd_ancil.tpkg stat_timer.tpkg 05-asynclook.tpkg stream_tcp.tpkg'
|
||||
NEED_XXD='fwd_compress_c00c.tpkg'
|
||||
NEED_NC='fwd_compress_c00c.tpkg'
|
||||
|
||||
|
||||
+35
-10
@@ -662,15 +662,6 @@ comm_point_send_reply(struct comm_reply* repinfo)
|
||||
log_pkt("reply pkt: ", ans->pkt);
|
||||
}
|
||||
|
||||
void
|
||||
comm_point_send_reply_iov(struct comm_reply* repinfo, struct iovec* iov,
|
||||
size_t iovlen)
|
||||
{
|
||||
/* skip tcp len at [0]. */
|
||||
write_iov_buffer(repinfo->c->buffer, iov+1, iovlen-1);
|
||||
comm_point_send_reply(repinfo);
|
||||
}
|
||||
|
||||
void
|
||||
comm_point_drop_reply(struct comm_reply* repinfo)
|
||||
{
|
||||
@@ -863,7 +854,7 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
pend->addrlen = addrlen;
|
||||
pend->callback = callback;
|
||||
pend->cb_arg = callback_arg;
|
||||
pend->timeout = UDP_QUERY_TIMEOUT;
|
||||
pend->timeout = UDP_AUTH_QUERY_TIMEOUT;
|
||||
pend->transport = transport_udp; /* pretend UDP */
|
||||
pend->pkt = NULL;
|
||||
pend->runtime = runtime;
|
||||
@@ -1005,6 +996,12 @@ void comm_point_udp_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(event),
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void comm_point_udp_ancil_callback(int ATTR_UNUSED(fd),
|
||||
short ATTR_UNUSED(event), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void comm_point_tcp_accept_callback(int ATTR_UNUSED(fd),
|
||||
short ATTR_UNUSED(event), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
@@ -1035,6 +1032,12 @@ void comm_point_local_handle_callback(int ATTR_UNUSED(fd),
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void comm_point_raw_handle_callback(int ATTR_UNUSED(fd),
|
||||
short ATTR_UNUSED(event), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
int serviced_udp_callback(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
@@ -1063,4 +1066,26 @@ int serviced_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* no statistics timers in testbound */
|
||||
struct comm_timer* comm_timer_create(struct comm_base* ATTR_UNUSED(base),
|
||||
void (*cb)(void*), void* ATTR_UNUSED(cb_arg))
|
||||
{
|
||||
(void)cb;
|
||||
return malloc(1);
|
||||
}
|
||||
|
||||
void comm_timer_disable(struct comm_timer* ATTR_UNUSED(timer))
|
||||
{
|
||||
}
|
||||
|
||||
void comm_timer_set(struct comm_timer* ATTR_UNUSED(timer),
|
||||
struct timeval* ATTR_UNUSED(tv))
|
||||
{
|
||||
}
|
||||
|
||||
void comm_timer_delete(struct comm_timer* timer)
|
||||
{
|
||||
free(timer);
|
||||
}
|
||||
|
||||
/*********** End of Dummy routines ***********/
|
||||
|
||||
@@ -504,7 +504,7 @@ read_entry(FILE* in, const char* name, int *lineno, uint32_t* default_ttl,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** reads the canned reply file and returns a list of structs */
|
||||
/* reads the canned reply file and returns a list of structs */
|
||||
struct entry*
|
||||
read_datafile(const char* name)
|
||||
{
|
||||
@@ -645,7 +645,7 @@ match_all(ldns_pkt* q, ldns_pkt* p, bool mttl)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** finds entry in list, or returns NULL */
|
||||
/* finds entry in list, or returns NULL */
|
||||
struct entry*
|
||||
find_match(struct entry* entries, ldns_pkt* query_pkt,
|
||||
enum transport_type transport)
|
||||
@@ -781,9 +781,14 @@ handle_query(uint8_t* inbuf, ssize_t inlen, struct entry* entries, int* count,
|
||||
answer_pkt = NULL;
|
||||
} else {
|
||||
verbose(3, "Could not parse hex data (%s), sending hex data directly.\n", ldns_get_errorstr_by_id(status));
|
||||
/* still try to adjust ID */
|
||||
answer_size = ldns_buffer_capacity(p->reply_from_hex);
|
||||
outbuf = LDNS_XMALLOC(uint8_t, answer_size);
|
||||
memcpy(outbuf, ldns_buffer_export(p->reply_from_hex), answer_size);
|
||||
if(entry->copy_id) {
|
||||
ldns_write_uint16(outbuf,
|
||||
ldns_pkt_id(query_pkt));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
answer_pkt = ldns_pkt_clone(p->reply);
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
; with the SECTION keywords (only SECTION QUERY
|
||||
; is used to match queries). If the data cannot
|
||||
; be parsed, ADJUST rules for the answer packet
|
||||
; are ignored
|
||||
; are ignored. Only copy_id is done.
|
||||
HEX_ANSWER_END
|
||||
ENTRY_END
|
||||
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ skip_whites(const char** p)
|
||||
}
|
||||
}
|
||||
|
||||
/** takes a hex string and puts into buffer */
|
||||
/* takes a hex string and puts into buffer */
|
||||
void hex_to_buf(ldns_buffer* pkt, const char* hex)
|
||||
{
|
||||
const char* p = hex;
|
||||
|
||||
+5
-1
@@ -41,7 +41,11 @@
|
||||
#ifndef TESTCODE_READHEX_H
|
||||
#define TESTCODE_READHEX_H
|
||||
|
||||
/** helper to convert hex string to packet buffer */
|
||||
/**
|
||||
* Helper to convert hex string to packet buffer.
|
||||
* @param pkt: buffer to put result in.
|
||||
* @param hex: string of hex data. Spaces and ';...' comments are skipped.
|
||||
*/
|
||||
void hex_to_buf(ldns_buffer* pkt, const char* hex);
|
||||
|
||||
#endif /* TESTCODE_READHEX_H */
|
||||
|
||||
+3
-1
@@ -155,7 +155,9 @@ struct replay_moment {
|
||||
repevt_back_query,
|
||||
/** an error happens to outbound query */
|
||||
repevt_error
|
||||
} evt_type;
|
||||
}
|
||||
/** variable with what is to happen this moment */
|
||||
evt_type;
|
||||
|
||||
/** The sent packet must match this. Incoming events, the data. */
|
||||
struct entry* match;
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* testcode/streamtcp.c - debug program perform multiple DNS queries on tcp.
|
||||
*
|
||||
* Copyright (c) 2008, 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 program performs multiple DNS queries on a TCP stream.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <signal.h>
|
||||
#include "util/locks.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/dname.h"
|
||||
|
||||
/** usage information for streamtcp */
|
||||
void usage(char* argv[])
|
||||
{
|
||||
printf("usage: %s [options] name type class ...\n", argv[0]);
|
||||
printf(" sends the name-type-class queries over TCP.\n");
|
||||
printf("-f server what ipaddr@portnr to send the queries to\n");
|
||||
printf("-h this help text\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** open TCP socket to svr */
|
||||
static int
|
||||
open_svr(char* svr)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
int fd = -1;
|
||||
/* svr can be ip@port */
|
||||
if(!extstrtoaddr(svr, &addr, &addrlen)) {
|
||||
printf("fatal: bad server specs '%s'\n", svr);
|
||||
exit(1);
|
||||
}
|
||||
fd = socket(addr_is_ip6(&addr, addrlen)?PF_INET6:PF_INET,
|
||||
SOCK_STREAM, 0);
|
||||
if(fd == -1) {
|
||||
perror("socket() error");
|
||||
exit(1);
|
||||
}
|
||||
if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) {
|
||||
perror("connect() error");
|
||||
exit(1);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/** write a query over the TCP fd */
|
||||
static void
|
||||
write_q(int fd, ldns_buffer* buf, int id,
|
||||
char* strname, char* strtype, char* strclass)
|
||||
{
|
||||
struct query_info qinfo;
|
||||
ldns_rdf* rdf;
|
||||
int labs;
|
||||
uint16_t len;
|
||||
/* qname */
|
||||
rdf = ldns_dname_new_frm_str(strname);
|
||||
if(!rdf) {
|
||||
printf("cannot parse query name: '%s'\n", strname);
|
||||
exit(1);
|
||||
}
|
||||
qinfo.qname = memdup(ldns_rdf_data(rdf), ldns_rdf_size(rdf));
|
||||
labs = dname_count_size_labels(qinfo.qname, &qinfo.qname_len);
|
||||
ldns_rdf_deep_free(rdf);
|
||||
if(!qinfo.qname) fatal_exit("out of memory");
|
||||
|
||||
/* qtype and qclass */
|
||||
qinfo.qtype = ldns_get_rr_type_by_name(strtype);
|
||||
qinfo.qclass = ldns_get_rr_class_by_name(strclass);
|
||||
|
||||
/* make query */
|
||||
qinfo_query_encode(buf, &qinfo);
|
||||
ldns_buffer_write_u16_at(buf, 0, (uint16_t)id);
|
||||
ldns_buffer_write_u16_at(buf, 2, BIT_RD);
|
||||
|
||||
/* send it */
|
||||
len = (uint16_t)ldns_buffer_limit(buf);
|
||||
len = htons(len);
|
||||
if(write(fd, &len, sizeof(len)) < (ssize_t)sizeof(len)) {
|
||||
perror("write() len failed");
|
||||
exit(1);
|
||||
}
|
||||
if(write(fd, ldns_buffer_begin(buf), ldns_buffer_limit(buf)) <
|
||||
(ssize_t)ldns_buffer_limit(buf)) {
|
||||
perror("write() data failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
free(qinfo.qname);
|
||||
}
|
||||
|
||||
/** receive DNS datagram over TCP and print it */
|
||||
static void
|
||||
recv_one(int fd, ldns_buffer* buf)
|
||||
{
|
||||
uint16_t len;
|
||||
ldns_pkt* pkt;
|
||||
ldns_status status;
|
||||
if(read(fd, &len, sizeof(len)) < (ssize_t)sizeof(len)) {
|
||||
perror("read() len failed");
|
||||
exit(1);
|
||||
}
|
||||
len = ntohs(len);
|
||||
ldns_buffer_clear(buf);
|
||||
ldns_buffer_set_limit(buf, len);
|
||||
if(read(fd, ldns_buffer_begin(buf), len) < (ssize_t)len) {
|
||||
perror("read() data failed");
|
||||
exit(1);
|
||||
}
|
||||
printf("\nnext received packet\n");
|
||||
log_buf(0, "data", buf);
|
||||
|
||||
status = ldns_wire2pkt(&pkt, ldns_buffer_begin(buf), len);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
printf("could not parse incoming packet: %s\n",
|
||||
ldns_get_errorstr_by_id(status));
|
||||
log_buf(0, "data was", buf);
|
||||
exit(1);
|
||||
}
|
||||
ldns_pkt_print(stdout, pkt);
|
||||
ldns_pkt_free(pkt);
|
||||
}
|
||||
|
||||
/** send the TCP queries and print answers */
|
||||
static void
|
||||
send_em(char* svr, int num, char** qs)
|
||||
{
|
||||
ldns_buffer* buf = ldns_buffer_new(65553);
|
||||
int fd = open_svr(svr);
|
||||
int i;
|
||||
if(!buf) fatal_exit("out of memory");
|
||||
for(i=0; i<num; i+=3) {
|
||||
printf("\nNext query is %s %s %s\n", qs[i], qs[i+1], qs[i+2]);
|
||||
write_q(fd, buf, i, qs[i], qs[i+1], qs[i+2]);
|
||||
/* print at least one result */
|
||||
recv_one(fd, buf);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
ldns_buffer_free(buf);
|
||||
printf("orderly exit\n");
|
||||
}
|
||||
|
||||
/** SIGPIPE handler */
|
||||
static RETSIGTYPE sigh(int sig)
|
||||
{
|
||||
if(sig == SIGPIPE) {
|
||||
printf("got SIGPIPE, remote connection gone\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("Got unhandled signal %d\n", sig);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern int optind;
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern char* optarg;
|
||||
|
||||
/** main program for streamtcp */
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int c;
|
||||
char* svr = "127.0.0.1";
|
||||
|
||||
/* lock debug start (if any) */
|
||||
log_init(0, 0, 0);
|
||||
checklock_start();
|
||||
|
||||
if(signal(SIGPIPE, &sigh) == SIG_ERR) {
|
||||
perror("could not install signal handler");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* command line options */
|
||||
if(argc == 1) {
|
||||
usage(argv);
|
||||
}
|
||||
while( (c=getopt(argc, argv, "f:h")) != -1) {
|
||||
switch(c) {
|
||||
case 'f':
|
||||
svr = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
default:
|
||||
usage(argv);
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if(argc % 3 != 0) {
|
||||
printf("queries must be multiples of name,type,class\n");
|
||||
return 1;
|
||||
}
|
||||
send_em(svr, argc, argv);
|
||||
checklock_stop();
|
||||
return 0;
|
||||
}
|
||||
+43
-4
@@ -240,6 +240,44 @@ net_test()
|
||||
}
|
||||
}
|
||||
|
||||
#include "util/config_file.h"
|
||||
/** test config_file: cfg_parse_memsize */
|
||||
static void
|
||||
config_memsize_test()
|
||||
{
|
||||
size_t v = 0;
|
||||
if(0) {
|
||||
/* these emit errors */
|
||||
unit_assert( cfg_parse_memsize("", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("bla", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("nop", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("n0b", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("gb", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("b", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("kb", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("kk kb", &v) == 0);
|
||||
}
|
||||
unit_assert( cfg_parse_memsize("0", &v) && v==0);
|
||||
unit_assert( cfg_parse_memsize("1", &v) && v==1);
|
||||
unit_assert( cfg_parse_memsize("10", &v) && v==10);
|
||||
unit_assert( cfg_parse_memsize("10b", &v) && v==10);
|
||||
unit_assert( cfg_parse_memsize("5b", &v) && v==5);
|
||||
unit_assert( cfg_parse_memsize("1024", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1k", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1K", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1Kb", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1kb", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1 kb", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("10 kb", &v) && v==10240);
|
||||
unit_assert( cfg_parse_memsize("2k", &v) && v==2048);
|
||||
unit_assert( cfg_parse_memsize("2m", &v) && v==2048*1024);
|
||||
unit_assert( cfg_parse_memsize("3M", &v) && v==3072*1024);
|
||||
unit_assert( cfg_parse_memsize("40m", &v) && v==40960*1024);
|
||||
unit_assert( cfg_parse_memsize("1G", &v) && v==1024*1024*1024);
|
||||
unit_assert( cfg_parse_memsize("1 Gb", &v) && v==1024*1024*1024);
|
||||
unit_assert( cfg_parse_memsize("0 Gb", &v) && v==0*1024*1024);
|
||||
}
|
||||
|
||||
#include "util/rtt.h"
|
||||
/** test RTT code */
|
||||
static void
|
||||
@@ -325,12 +363,12 @@ infra_test()
|
||||
static void
|
||||
rnd_test()
|
||||
{
|
||||
struct ub_randstate r;
|
||||
struct ub_randstate* r;
|
||||
int num = 100, i;
|
||||
long int a[100];
|
||||
unit_assert( ub_initstate((unsigned)time(NULL), &r, 256) );
|
||||
unit_assert( (r = ub_initstate((unsigned)time(NULL), NULL)) );
|
||||
for(i=0; i<num; i++) {
|
||||
a[i] = ub_random(&r);
|
||||
a[i] = ub_random(r);
|
||||
unit_assert(a[i] >= 0);
|
||||
unit_assert((size_t)a[i] <= (size_t)RAND_MAX);
|
||||
if(i > 5)
|
||||
@@ -338,7 +376,7 @@ rnd_test()
|
||||
a[i] != a[i-3] || a[i] != a[i-4] ||
|
||||
a[i] != a[i-5] || a[i] != a[i-6]);
|
||||
}
|
||||
ub_randfree(&r);
|
||||
ub_randfree(r);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,6 +398,7 @@ main(int argc, char* argv[])
|
||||
rnd_test();
|
||||
verify_test();
|
||||
net_test();
|
||||
config_memsize_test();
|
||||
dname_test();
|
||||
anchors_test();
|
||||
rtt_test();
|
||||
|
||||
@@ -45,7 +45,11 @@
|
||||
/** number of tests done */
|
||||
extern int testcount;
|
||||
/** test bool x, exits on failure, increases testcount. */
|
||||
#ifdef DEBUG_UNBOUND
|
||||
#define unit_assert(x) do {testcount++; log_assert(x);} while(0)
|
||||
#else
|
||||
#define unit_assert(x) do {testcount++; if(!(x)) { fprintf(stderr, "assertion failure %s:%d\n", __FILE__, __LINE__); exit(1);}} while(0)
|
||||
#endif
|
||||
|
||||
/** unit test lruhashtable implementation */
|
||||
void lruhash_test();
|
||||
|
||||
@@ -150,7 +150,7 @@ verifytest_rrset(struct module_env* env, struct val_env* ve,
|
||||
{
|
||||
enum sec_status sec;
|
||||
if(vsig) {
|
||||
log_nametypeclass(VERB_DETAIL, "verify of rrset",
|
||||
log_nametypeclass(VERB_QUERY, "verify of rrset",
|
||||
rrset->rk.dname, ntohs(rrset->rk.type),
|
||||
ntohs(rrset->rk.rrset_class));
|
||||
}
|
||||
@@ -237,7 +237,7 @@ dstest_entry(struct entry* e, struct alloc_cache* alloc,
|
||||
printf("result(no)= %s\n", ret?"yes":"no");
|
||||
}
|
||||
unit_assert(!ret);
|
||||
verbose(VERB_DETAIL, "DS fail: OK; matched unit test");
|
||||
verbose(VERB_QUERY, "DS fail: OK; matched unit test");
|
||||
} else {
|
||||
fatal_exit("Bad qname in DS unit test, yes or no");
|
||||
}
|
||||
@@ -274,7 +274,7 @@ verifytest_file(const char* fname, const char* at_date)
|
||||
ve.date_override = cfg_convert_timeval(at_date);
|
||||
unit_assert(region && buf);
|
||||
dnskey = extract_keys(list, &alloc, region, buf);
|
||||
if(vsig) log_nametypeclass(VERB_DETAIL, "test dnskey",
|
||||
if(vsig) log_nametypeclass(VERB_QUERY, "test dnskey",
|
||||
dnskey->rk.dname, ntohs(dnskey->rk.type),
|
||||
ntohs(dnskey->rk.rrset_class));
|
||||
/* ready to go! */
|
||||
|
||||
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
BIN
Binary file not shown.
+245
-2
@@ -45,6 +45,7 @@
|
||||
#include "util/configyyrename.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/configparser.h"
|
||||
#include "util/net_help.h"
|
||||
/** global config during parsing */
|
||||
struct config_parser_state* cfg_parser = 0;
|
||||
/** lex in file */
|
||||
@@ -69,6 +70,7 @@ config_create()
|
||||
return NULL;
|
||||
/* the defaults if no config is present */
|
||||
cfg->verbosity = 1;
|
||||
cfg->stat_interval = 0;
|
||||
cfg->num_threads = 1;
|
||||
cfg->port = UNBOUND_DNS_PORT;
|
||||
cfg->do_ip4 = 1;
|
||||
@@ -104,6 +106,7 @@ config_create()
|
||||
cfg->donotquery_localhost = 1;
|
||||
cfg->root_hints = NULL;
|
||||
cfg->do_daemonize = 1;
|
||||
cfg->if_automatic = 0;
|
||||
cfg->num_ifs = 0;
|
||||
cfg->ifs = NULL;
|
||||
cfg->num_out_ifs = 0;
|
||||
@@ -130,6 +133,8 @@ config_create()
|
||||
cfg->local_zones = NULL;
|
||||
cfg->local_zones_nodefault = NULL;
|
||||
cfg->local_data = NULL;
|
||||
cfg->donotquery_localhost = 0; /* allow, so that you can ask a
|
||||
forward nameserver running on localhost */
|
||||
if(!(cfg->module_conf = strdup("validator iterator"))) goto error_exit;
|
||||
if(!(cfg->val_nsec3_key_iterations =
|
||||
strdup("1024 150 2048 500 4096 2500"))) goto error_exit;
|
||||
@@ -139,6 +144,188 @@ error_exit:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct config_file* config_create_forlib()
|
||||
{
|
||||
struct config_file* cfg = config_create();
|
||||
if(!cfg) return NULL;
|
||||
/* modifications for library use, less verbose, less memory */
|
||||
free(cfg->chrootdir);
|
||||
cfg->chrootdir = NULL;
|
||||
cfg->verbosity = 0;
|
||||
cfg->outgoing_num_tcp = 2;
|
||||
cfg->msg_cache_size = 1024*1024;
|
||||
cfg->msg_cache_slabs = 1;
|
||||
cfg->rrset_cache_size = 1024*1024;
|
||||
cfg->rrset_cache_slabs = 1;
|
||||
cfg->infra_cache_slabs = 1;
|
||||
cfg->use_syslog = 0;
|
||||
cfg->key_cache_size = 1024*1024;
|
||||
cfg->key_cache_slabs = 1;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
/** check that the value passed is >= 0 */
|
||||
#define IS_NUMBER_OR_ZERO \
|
||||
if(atoi(val) == 0 && strcmp(val, "0") != 0) return 0
|
||||
/** check that the value passed is > 0 */
|
||||
#define IS_NONZERO_NUMBER \
|
||||
if(atoi(val) == 0) return 0
|
||||
/** check that the value passed is not 0 and a power of 2 */
|
||||
#define IS_POW2_NUMBER \
|
||||
if(atoi(val) == 0 || !is_pow2((size_t)atoi(val))) return 0
|
||||
/** check that the value passed is yes or no */
|
||||
#define IS_YES_OR_NO \
|
||||
if(strcmp(val, "yes") != 0 && strcmp(val, "no") != 0) return 0
|
||||
|
||||
int config_set_option(struct config_file* cfg, const char* opt,
|
||||
const char* val)
|
||||
{
|
||||
if(strcmp(opt, "verbosity:") == 0) {
|
||||
IS_NUMBER_OR_ZERO;
|
||||
cfg->verbosity = atoi(val);
|
||||
} else if(strcmp(opt, "statistics-interval:") == 0) {
|
||||
if(strcmp(val, "0") == 0 || strcmp(val, "") == 0)
|
||||
cfg->stat_interval = 0;
|
||||
else if(atoi(val) == 0)
|
||||
return 0;
|
||||
else cfg->stat_interval = atoi(val);
|
||||
} else if(strcmp(opt, "num_threads:") == 0) {
|
||||
/* not supported, library must have 1 thread in bgworker */
|
||||
return 0;
|
||||
} else if(strcmp(opt, "do-ip4:") == 0) {
|
||||
IS_YES_OR_NO;
|
||||
cfg->do_ip4 = (strcmp(val, "yes") == 0);
|
||||
} else if(strcmp(opt, "do-ip6:") == 0) {
|
||||
IS_YES_OR_NO;
|
||||
cfg->do_ip6 = (strcmp(val, "yes") == 0);
|
||||
} else if(strcmp(opt, "do-udp:") == 0) {
|
||||
IS_YES_OR_NO;
|
||||
cfg->do_udp = (strcmp(val, "yes") == 0);
|
||||
} else if(strcmp(opt, "do-tcp:") == 0) {
|
||||
IS_YES_OR_NO;
|
||||
cfg->do_tcp = (strcmp(val, "yes") == 0);
|
||||
} else if(strcmp(opt, "outgoing-port:") == 0) {
|
||||
IS_NUMBER_OR_ZERO;
|
||||
cfg->outgoing_base_port = atoi(val);
|
||||
} else if(strcmp(opt, "outgoing-range:") == 0) {
|
||||
IS_NONZERO_NUMBER;
|
||||
cfg->outgoing_num_ports = atoi(val);
|
||||
} else if(strcmp(opt, "outgoing-num-tcp:") == 0) {
|
||||
IS_NUMBER_OR_ZERO;
|
||||
cfg->outgoing_num_tcp = (size_t)atoi(val);
|
||||
} else if(strcmp(opt, "incoming-num-tcp:") == 0) {
|
||||
IS_NUMBER_OR_ZERO;
|
||||
cfg->incoming_num_tcp = (size_t)atoi(val);
|
||||
} else if(strcmp(opt, "msg-buffer-size:") == 0) {
|
||||
IS_NONZERO_NUMBER;
|
||||
cfg->msg_buffer_size = (size_t)atoi(val);
|
||||
} else if(strcmp(opt, "msg-cache-size:") == 0) {
|
||||
return cfg_parse_memsize(val, &cfg->msg_cache_size);
|
||||
} else if(strcmp(opt, "msg-cache-slabs:") == 0) {
|
||||
IS_POW2_NUMBER;
|
||||
cfg->msg_cache_slabs = (size_t)atoi(val);
|
||||
} else if(strcmp(opt, "num-queries-per-thread:") == 0) {
|
||||
IS_NONZERO_NUMBER;
|
||||
cfg->num_queries_per_thread = (size_t)atoi(val);
|
||||
} else if(strcmp(opt, "rrset-cache-size:") == 0) {
|
||||
return cfg_parse_memsize(val, &cfg->rrset_cache_size);
|
||||
} else if(strcmp(opt, "rrset-cache-slabs:") == 0) {
|
||||
IS_POW2_NUMBER;
|
||||
cfg->rrset_cache_slabs = (size_t)atoi(val);
|
||||
} else if(strcmp(opt, "cache-max-ttl:") == 0) {
|
||||
IS_NUMBER_OR_ZERO;
|
||||
cfg->max_ttl = atoi(val);
|
||||
} else if(strcmp(opt, "infra-host-ttl:") == 0) {
|
||||
IS_NUMBER_OR_ZERO;
|
||||
cfg->host_ttl = atoi(val);
|
||||
} else if(strcmp(opt, "infra-lame-ttl:") == 0) {
|
||||
IS_NUMBER_OR_ZERO;
|
||||
cfg->lame_ttl = atoi(val);
|
||||
} else if(strcmp(opt, "infra-cache-slabs:") == 0) {
|
||||
IS_POW2_NUMBER;
|
||||
cfg->infra_cache_slabs = (size_t)atoi(val);
|
||||
} else if(strcmp(opt, "infra-cache-numhosts:") == 0) {
|
||||
IS_NONZERO_NUMBER;
|
||||
cfg->infra_cache_numhosts = (size_t)atoi(val);
|
||||
} else if(strcmp(opt, "infra-cache-lame-size:") == 0) {
|
||||
return cfg_parse_memsize(val, &cfg->infra_cache_lame_size);
|
||||
} else if(strcmp(opt, "logfile:") == 0) {
|
||||
cfg->use_syslog = 0;
|
||||
free(cfg->logfile);
|
||||
return (cfg->logfile = strdup(val)) != NULL;
|
||||
} else if(strcmp(opt, "use-syslog:") == 0) {
|
||||
IS_YES_OR_NO;
|
||||
cfg->use_syslog = (strcmp(val, "yes") == 0);
|
||||
} else if(strcmp(opt, "root-hints:") == 0) {
|
||||
return cfg_strlist_insert(&cfg->root_hints, strdup(val));
|
||||
} else if(strcmp(opt, "target-fetch-policy:") == 0) {
|
||||
free(cfg->target_fetch_policy);
|
||||
return (cfg->target_fetch_policy = strdup(val)) != NULL;
|
||||
} else if(strcmp(opt, "harden-glue:") == 0) {
|
||||
IS_YES_OR_NO;
|
||||
cfg->harden_glue = (strcmp(val, "yes") == 0);
|
||||
} else if(strcmp(opt, "harden-short-bufsize:") == 0) {
|
||||
IS_YES_OR_NO;
|
||||
cfg->harden_short_bufsize = (strcmp(val, "yes") == 0);
|
||||
} else if(strcmp(opt, "harden-large-queries:") == 0) {
|
||||
IS_YES_OR_NO;
|
||||
cfg->harden_large_queries = (strcmp(val, "yes") == 0);
|
||||
} else if(strcmp(opt, "harden-dnssec-stripped:") == 0) {
|
||||
IS_YES_OR_NO;
|
||||
cfg->harden_dnssec_stripped = (strcmp(val, "yes") == 0);
|
||||
} else if(strcmp(opt, "do-not-query-localhost:") == 0) {
|
||||
IS_YES_OR_NO;
|
||||
cfg->donotquery_localhost = (strcmp(val, "yes") == 0);
|
||||
} else if(strcmp(opt, "do-not-query-address:") == 0) {
|
||||
return cfg_strlist_insert(&cfg->donotqueryaddrs, strdup(val));
|
||||
} else if(strcmp(opt, "trust-anchor-file:") == 0) {
|
||||
return cfg_strlist_insert(&cfg->trust_anchor_file_list,
|
||||
strdup(val));
|
||||
} else if(strcmp(opt, "trust-anchor:") == 0) {
|
||||
return cfg_strlist_insert(&cfg->trust_anchor_list,
|
||||
strdup(val));
|
||||
} else if(strcmp(opt, "trusted-keys-file:") == 0) {
|
||||
return cfg_strlist_insert(&cfg->trusted_keys_file_list,
|
||||
strdup(val));
|
||||
} else if(strcmp(opt, "val-override-date:") == 0) {
|
||||
if(strcmp(val, "") == 0 || strcmp(val, "0") == 0) {
|
||||
cfg->val_date_override = 0;
|
||||
} else if(strlen(val) == 14) {
|
||||
cfg->val_date_override = cfg_convert_timeval(val);
|
||||
return cfg->val_date_override != 0;
|
||||
} else {
|
||||
if(atoi(val) == 0) return 0;
|
||||
cfg->val_date_override = (uint32_t)atoi(val);
|
||||
}
|
||||
} else if(strcmp(opt, "val-bogus-ttl:") == 0) {
|
||||
IS_NUMBER_OR_ZERO;
|
||||
cfg->bogus_ttl = atoi(val);
|
||||
} else if(strcmp(opt, "val-clean-additional:") == 0) {
|
||||
IS_YES_OR_NO;
|
||||
cfg->val_clean_additional = (strcmp(val, "yes") == 0);
|
||||
} else if(strcmp(opt, "val-permissive-mode:") == 0) {
|
||||
IS_YES_OR_NO;
|
||||
cfg->val_permissive_mode = (strcmp(val, "yes") == 0);
|
||||
} else if(strcmp(opt, "val-nsec3-keysize-iterations:") == 0) {
|
||||
free(cfg->val_nsec3_key_iterations);
|
||||
return (cfg->val_nsec3_key_iterations = strdup(val)) != NULL;
|
||||
} else if(strcmp(opt, "key-cache-size:") == 0) {
|
||||
return cfg_parse_memsize(val, &cfg->key_cache_size);
|
||||
} else if(strcmp(opt, "key-cache-slabs:") == 0) {
|
||||
IS_POW2_NUMBER;
|
||||
cfg->key_cache_slabs = (size_t)atoi(val);
|
||||
} else if(strcmp(opt, "local-data:") == 0) {
|
||||
return cfg_strlist_insert(&cfg->local_data, strdup(val));
|
||||
} else if(strcmp(opt, "module-config:") == 0) {
|
||||
free(cfg->module_conf);
|
||||
return (cfg->module_conf = strdup(val)) != NULL;
|
||||
} else {
|
||||
/* unknown or unsupported (from the library interface) */
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** initialize the global cfg_parser object */
|
||||
static void
|
||||
create_cfg_parser(struct config_file* cfg, char* filename)
|
||||
@@ -200,8 +387,7 @@ config_deldblstrlist(struct config_str2list* p)
|
||||
}
|
||||
}
|
||||
|
||||
/** delete config stublist */
|
||||
static void
|
||||
void
|
||||
config_delstubs(struct config_stub* p)
|
||||
{
|
||||
struct config_stub* np;
|
||||
@@ -323,6 +509,8 @@ cfg_convert_timeval(const char* str)
|
||||
uint32_t t;
|
||||
struct tm tm;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
if(strlen(str) < 14)
|
||||
return 0;
|
||||
if(sscanf(str, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon,
|
||||
&tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6)
|
||||
return 0;
|
||||
@@ -365,6 +553,60 @@ cfg_count_numbers(const char* s)
|
||||
return num;
|
||||
}
|
||||
|
||||
/** all digit number */
|
||||
static int isalldigit(const char* str, size_t l)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<l; i++)
|
||||
if(!isdigit(str[i]))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
cfg_parse_memsize(const char* str, size_t* res)
|
||||
{
|
||||
size_t len = (size_t)strlen(str);
|
||||
size_t mult = 1;
|
||||
if(!str || len == 0) {
|
||||
log_err("not a size: '%s'", str);
|
||||
return 0;
|
||||
}
|
||||
if(isalldigit(str, len)) {
|
||||
*res = (size_t)atol(str);
|
||||
return 1;
|
||||
}
|
||||
/* check appended num */
|
||||
while(len>0 && str[len-1]==' ')
|
||||
len--;
|
||||
if(len > 1 && str[len-1] == 'b')
|
||||
len--;
|
||||
else if(len > 1 && str[len-1] == 'B')
|
||||
len--;
|
||||
|
||||
if(len > 1 && tolower(str[len-1]) == 'g')
|
||||
mult = 1024*1024*1024;
|
||||
else if(len > 1 && tolower(str[len-1]) == 'm')
|
||||
mult = 1024*1024;
|
||||
else if(len > 1 && tolower(str[len-1]) == 'k')
|
||||
mult = 1024;
|
||||
else if(len > 0 && isdigit(str[len-1]))
|
||||
mult = 1;
|
||||
else {
|
||||
log_err("unknown size specifier: '%s'", str);
|
||||
return 0;
|
||||
}
|
||||
while(len>1 && str[len-2]==' ')
|
||||
len--;
|
||||
|
||||
if(!isalldigit(str, len-1)) {
|
||||
log_err("unknown size specifier: '%s'", str);
|
||||
return 0;
|
||||
}
|
||||
*res = ((size_t)atol(str)) * mult;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** the MAX_TTL global */
|
||||
extern uint32_t MAX_TTL;
|
||||
|
||||
@@ -373,3 +615,4 @@ config_apply(struct config_file* config)
|
||||
{
|
||||
MAX_TTL = (uint32_t)config->max_ttl;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ struct config_str2list;
|
||||
struct config_file {
|
||||
/** verbosity level as specified in the config file */
|
||||
int verbosity;
|
||||
/** statistics interval (in seconds) */
|
||||
int stat_interval;
|
||||
|
||||
/** number of threads to create */
|
||||
int num_threads;
|
||||
@@ -102,6 +104,10 @@ struct config_file {
|
||||
/** the target fetch policy for the iterator */
|
||||
char* target_fetch_policy;
|
||||
|
||||
/** automatic interface for incoming messages. Uses ipv6 remapping,
|
||||
* and recvmsg/sendmsg ancillary data to detect interfaces, boolean */
|
||||
int if_automatic;
|
||||
|
||||
/** number of interfaces to open. If 0 default all interfaces. */
|
||||
int num_ifs;
|
||||
/** interface description strings (IP addresses) */
|
||||
@@ -239,6 +245,12 @@ struct config_str2list {
|
||||
*/
|
||||
struct config_file* config_create();
|
||||
|
||||
/**
|
||||
* Create config file structure for library use. Filled with default values.
|
||||
* @return: the new structure or NULL on memory error.
|
||||
*/
|
||||
struct config_file* config_create_forlib();
|
||||
|
||||
/**
|
||||
* Read the config file from the specified filename.
|
||||
* @param config: where options are stored into, must be freshly created.
|
||||
@@ -259,6 +271,17 @@ void config_delete(struct config_file* config);
|
||||
*/
|
||||
void config_apply(struct config_file* config);
|
||||
|
||||
/**
|
||||
* Set the given keyword to the given value.
|
||||
* @param config: where to store config
|
||||
* @param option: option name, including the ':' character.
|
||||
* @param value: value, this string is copied if needed, or parsed.
|
||||
* The caller owns the value string.
|
||||
* @return 0 on error (malloc or syntax error).
|
||||
*/
|
||||
int config_set_option(struct config_file* config, const char* option,
|
||||
const char* value);
|
||||
|
||||
/**
|
||||
* Insert string into strlist.
|
||||
* @param head: pointer to strlist head variable.
|
||||
@@ -288,6 +311,12 @@ void config_delstrlist(struct config_strlist* list);
|
||||
*/
|
||||
void config_deldblstrlist(struct config_str2list* list);
|
||||
|
||||
/**
|
||||
* Delete items in config stub list.
|
||||
* @param list: list.
|
||||
*/
|
||||
void config_delstubs(struct config_stub* list);
|
||||
|
||||
/**
|
||||
* Convert 14digit to time value
|
||||
* @param str: string of 14 digits
|
||||
@@ -307,6 +336,17 @@ uint32_t cfg_convert_timeval(const char* str);
|
||||
*/
|
||||
int cfg_count_numbers(const char* str);
|
||||
|
||||
/**
|
||||
* Convert a 'nice' memory or file size into a bytecount
|
||||
* From '100k' to 102400. and so on. Understands kKmMgG.
|
||||
* k=1024, m=1024*1024, g=1024*1024*1024.
|
||||
* @param str: string
|
||||
* @param res: result is stored here, size in bytes.
|
||||
* @return: true if parsed correctly, or 0 on a parse error (and an error
|
||||
* is logged).
|
||||
*/
|
||||
int cfg_parse_memsize(const char* str, size_t* res);
|
||||
|
||||
/**
|
||||
* Used during options parsing
|
||||
*/
|
||||
|
||||
+651
-621
File diff suppressed because it is too large
Load Diff
@@ -113,6 +113,7 @@ do-udp{COLON} { YDOUT; return VAR_DO_UDP;}
|
||||
do-tcp{COLON} { YDOUT; return VAR_DO_TCP;}
|
||||
interface{COLON} { YDOUT; return VAR_INTERFACE;}
|
||||
outgoing-interface{COLON} { YDOUT; return VAR_OUTGOING_INTERFACE;}
|
||||
interface-automatic{COLON} { YDOUT; return VAR_INTERFACE_AUTOMATIC;}
|
||||
chroot{COLON} { YDOUT; return VAR_CHROOT;}
|
||||
username{COLON} { YDOUT; return VAR_USERNAME;}
|
||||
directory{COLON} { YDOUT; return VAR_DIRECTORY;}
|
||||
@@ -164,6 +165,7 @@ val-nsec3-keysize-iterations{COLON} { YDOUT; return VAR_VAL_NSEC3_KEYSIZE_ITERAT
|
||||
use-syslog{COLON} { YDOUT; return VAR_USE_SYSLOG;}
|
||||
local-zone{COLON} { YDOUT; return VAR_LOCAL_ZONE;}
|
||||
local-data{COLON} { YDOUT; return VAR_LOCAL_DATA;}
|
||||
statistics-interval{COLON} { YDOUT; return VAR_STATISTICS_INTERVAL;}
|
||||
{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;}
|
||||
|
||||
/* Quoted strings. Strip leading and ending quotes */
|
||||
|
||||
+308
-278
File diff suppressed because it is too large
Load Diff
+6
-2
@@ -111,7 +111,9 @@
|
||||
VAR_HARDEN_DNNSEC_STRIPPED = 327,
|
||||
VAR_ACCESS_CONTROL = 328,
|
||||
VAR_LOCAL_ZONE = 329,
|
||||
VAR_LOCAL_DATA = 330
|
||||
VAR_LOCAL_DATA = 330,
|
||||
VAR_INTERFACE_AUTOMATIC = 331,
|
||||
VAR_STATISTICS_INTERVAL = 332
|
||||
};
|
||||
#endif
|
||||
/* Tokens. */
|
||||
@@ -188,6 +190,8 @@
|
||||
#define VAR_ACCESS_CONTROL 328
|
||||
#define VAR_LOCAL_ZONE 329
|
||||
#define VAR_LOCAL_DATA 330
|
||||
#define VAR_INTERFACE_AUTOMATIC 331
|
||||
#define VAR_STATISTICS_INTERVAL 332
|
||||
|
||||
|
||||
|
||||
@@ -199,7 +203,7 @@ typedef union YYSTYPE
|
||||
char* str;
|
||||
}
|
||||
/* Line 1489 of yacc.c. */
|
||||
#line 203 "util/configparser.h"
|
||||
#line 207 "util/configparser.h"
|
||||
YYSTYPE;
|
||||
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
|
||||
+33
-14
@@ -63,7 +63,7 @@ extern struct config_parser_state* cfg_parser;
|
||||
%}
|
||||
%union {
|
||||
char* str;
|
||||
}
|
||||
};
|
||||
|
||||
%token SPACE LETTER NEWLINE COMMENT COLON ANY ZONESTR
|
||||
%token <str> STRING
|
||||
@@ -87,7 +87,8 @@ extern struct config_parser_state* cfg_parser;
|
||||
%token VAR_VAL_NSEC3_KEYSIZE_ITERATIONS VAR_USE_SYSLOG
|
||||
%token VAR_OUTGOING_INTERFACE VAR_ROOT_HINTS VAR_DO_NOT_QUERY_LOCALHOST
|
||||
%token VAR_CACHE_MAX_TTL VAR_HARDEN_DNNSEC_STRIPPED VAR_ACCESS_CONTROL
|
||||
%token VAR_LOCAL_ZONE VAR_LOCAL_DATA
|
||||
%token VAR_LOCAL_ZONE VAR_LOCAL_DATA VAR_INTERFACE_AUTOMATIC
|
||||
%token VAR_STATISTICS_INTERVAL
|
||||
|
||||
%%
|
||||
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
|
||||
@@ -126,7 +127,8 @@ content_server: server_num_threads | server_verbosity | server_port |
|
||||
server_use_syslog | server_outgoing_interface | server_root_hints |
|
||||
server_do_not_query_localhost | server_cache_max_ttl |
|
||||
server_harden_dnssec_stripped | server_access_control |
|
||||
server_local_zone | server_local_data
|
||||
server_local_zone | server_local_data | server_interface_automatic |
|
||||
server_statistics_interval
|
||||
;
|
||||
stubstart: VAR_STUB_ZONE
|
||||
{
|
||||
@@ -178,6 +180,17 @@ server_verbosity: VAR_VERBOSITY STRING
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_statistics_interval: VAR_STATISTICS_INTERVAL STRING
|
||||
{
|
||||
OUTYY(("P(server_statistics_interval:%s)\n", $2));
|
||||
if(strcmp($2, "") == 0 || strcmp($2, "0") == 0)
|
||||
cfg_parser->cfg->stat_interval = 0;
|
||||
else if(atoi($2) == 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->stat_interval = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_port: VAR_PORT STRING
|
||||
{
|
||||
OUTYY(("P(server_port:%s)\n", $2));
|
||||
@@ -251,6 +264,15 @@ server_incoming_num_tcp: VAR_INCOMING_NUM_TCP STRING
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_interface_automatic: VAR_INTERFACE_AUTOMATIC STRING
|
||||
{
|
||||
OUTYY(("P(server_interface_automatic:%s)\n", $2));
|
||||
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
|
||||
yyerror("expected yes or no.");
|
||||
else cfg_parser->cfg->if_automatic = (strcmp($2, "yes")==0);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_do_ip4: VAR_DO_IP4 STRING
|
||||
{
|
||||
OUTYY(("P(server_do_ip4:%s)\n", $2));
|
||||
@@ -413,9 +435,8 @@ server_msg_buffer_size: VAR_MSG_BUFFER_SIZE STRING
|
||||
server_msg_cache_size: VAR_MSG_CACHE_SIZE STRING
|
||||
{
|
||||
OUTYY(("P(server_msg_cache_size:%s)\n", $2));
|
||||
if(atoi($2) == 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->msg_cache_size = atoi($2);
|
||||
if(!cfg_parse_memsize($2, &cfg_parser->cfg->msg_cache_size))
|
||||
yyerror("memory size expected");
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
@@ -444,9 +465,8 @@ server_num_queries_per_thread: VAR_NUM_QUERIES_PER_THREAD STRING
|
||||
server_rrset_cache_size: VAR_RRSET_CACHE_SIZE STRING
|
||||
{
|
||||
OUTYY(("P(server_rrset_cache_size:%s)\n", $2));
|
||||
if(atoi($2) == 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->rrset_cache_size = atoi($2);
|
||||
if(!cfg_parse_memsize($2, &cfg_parser->cfg->rrset_cache_size))
|
||||
yyerror("memory size expected");
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
@@ -493,9 +513,9 @@ server_infra_cache_numhosts: VAR_INFRA_CACHE_NUMHOSTS STRING
|
||||
server_infra_cache_lame_size: VAR_INFRA_CACHE_LAME_SIZE STRING
|
||||
{
|
||||
OUTYY(("P(server_infra_cache_lame_size:%s)\n", $2));
|
||||
if(atoi($2) == 0)
|
||||
if(!cfg_parse_memsize($2, &cfg_parser->cfg->
|
||||
infra_cache_lame_size))
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->infra_cache_lame_size = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
@@ -662,9 +682,8 @@ server_val_nsec3_keysize_iterations: VAR_VAL_NSEC3_KEYSIZE_ITERATIONS STRING
|
||||
server_key_cache_size: VAR_KEY_CACHE_SIZE STRING
|
||||
{
|
||||
OUTYY(("P(server_key_cache_size:%s)\n", $2));
|
||||
if(atoi($2) == 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->key_cache_size = atoi($2);
|
||||
if(!cfg_parse_memsize($2, &cfg_parser->cfg->key_cache_size))
|
||||
yyerror("memory size expected");
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
|
||||
+1
-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)
|
||||
{
|
||||
|
||||
@@ -424,7 +424,7 @@ find_rrset(struct msg_parse* msg, ldns_buffer* pkt, uint8_t* dname,
|
||||
ldns_pkt_section section, struct regional* region)
|
||||
{
|
||||
uint16_t covtype;
|
||||
if(rrset_prev) {
|
||||
if(*rrset_prev) {
|
||||
/* check if equal to previous item */
|
||||
if(type == *prev_type && dclass == *prev_dclass &&
|
||||
dnamelen == *prev_dnamelen &&
|
||||
@@ -700,9 +700,9 @@ add_rr_to_rrset(struct rrset_parse* rrset, ldns_buffer* pkt,
|
||||
* also the last part is more likely to be incomplete.
|
||||
* RFC 2181: must put RRset only once in response. */
|
||||
/*
|
||||
verbose(VERB_DETAIL, "Packet contains rrset data in "
|
||||
verbose(VERB_QUERY, "Packet contains rrset data in "
|
||||
"multiple sections, dropped last part.");
|
||||
log_buf(VERB_DETAIL, "packet was", pkt);
|
||||
log_buf(VERB_QUERY, "packet was", pkt);
|
||||
*/
|
||||
/* forwards */
|
||||
if(!skip_ttl_rdata(pkt))
|
||||
|
||||
+25
-5
@@ -279,8 +279,10 @@ parse_create_rrset(ldns_buffer* pkt, struct rrset_parse* pset,
|
||||
if(!*data)
|
||||
return 0;
|
||||
/* copy & decompress */
|
||||
if(!parse_rr_copy(pkt, pset, *data))
|
||||
if(!parse_rr_copy(pkt, pset, *data)) {
|
||||
if(!region) free(*data);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -541,12 +543,9 @@ msgreply_sizefunc(void* k, void* d)
|
||||
}
|
||||
|
||||
void
|
||||
query_entry_delete(void *k, void* ATTR_UNUSED(arg), int is_locked)
|
||||
query_entry_delete(void *k, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct msgreply_entry* q = (struct msgreply_entry*)k;
|
||||
if(is_locked) {
|
||||
lock_rw_unlock(&q->entry.lock);
|
||||
}
|
||||
lock_rw_destroy(&q->entry.lock);
|
||||
query_info_clear(&q->key);
|
||||
free(q);
|
||||
@@ -649,6 +648,27 @@ reply_info_copy(struct reply_info* rep, struct alloc_cache* alloc,
|
||||
return cp;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
reply_find_final_cname_target(struct query_info* qinfo, struct reply_info* rep)
|
||||
{
|
||||
uint8_t* sname = qinfo->qname;
|
||||
size_t snamelen = qinfo->qname_len;
|
||||
size_t i;
|
||||
for(i=0; i<rep->an_numrrsets; i++) {
|
||||
struct ub_packed_rrset_key* s = rep->rrsets[i];
|
||||
/* follow CNAME chain (if any) */
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
|
||||
ntohs(s->rk.rrset_class) == qinfo->qclass &&
|
||||
snamelen == s->rk.dname_len &&
|
||||
query_dname_compare(sname, s->rk.dname) == 0) {
|
||||
get_cname_target(s, &sname, &snamelen);
|
||||
}
|
||||
}
|
||||
if(sname != qinfo->qname)
|
||||
return sname;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ub_packed_rrset_key*
|
||||
reply_find_answer_rrset(struct query_info* qinfo, struct reply_info* rep)
|
||||
{
|
||||
|
||||
+10
-1
@@ -268,7 +268,7 @@ void query_info_clear(struct query_info* m);
|
||||
size_t msgreply_sizefunc(void* k, void* d);
|
||||
|
||||
/** delete msgreply_entry key structure */
|
||||
void query_entry_delete(void *q, void* arg, int is_locked);
|
||||
void query_entry_delete(void *q, void* arg);
|
||||
|
||||
/** delete reply_info data structure */
|
||||
void reply_info_delete(void* d, void* arg);
|
||||
@@ -315,6 +315,15 @@ int parse_copy_decompress_rrset(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct rrset_parse *pset, struct regional* region,
|
||||
struct ub_packed_rrset_key* pk);
|
||||
|
||||
/**
|
||||
* Find final cname target in reply, the one matching qinfo. Follows CNAMEs.
|
||||
* @param qinfo: what to start with.
|
||||
* @param rep: looks in answer section of this message.
|
||||
* @return: pointer dname, or NULL if not found.
|
||||
*/
|
||||
uint8_t* reply_find_final_cname_target(struct query_info* qinfo,
|
||||
struct reply_info* rep);
|
||||
|
||||
/**
|
||||
* Find answer rrset in reply, the one matching qinfo. Follows CNAMEs, so the
|
||||
* result may have a different owner name.
|
||||
|
||||
@@ -121,14 +121,11 @@ ub_rrset_compare(void* k1, void* k2)
|
||||
}
|
||||
|
||||
void
|
||||
ub_rrset_key_delete(void* key, void* userdata, int is_locked)
|
||||
ub_rrset_key_delete(void* key, void* userdata)
|
||||
{
|
||||
struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)key;
|
||||
struct alloc_cache* a = (struct alloc_cache*)userdata;
|
||||
k->id = 0;
|
||||
if(is_locked) {
|
||||
lock_rw_unlock(&k->entry.lock);
|
||||
}
|
||||
free(k->rk.dname);
|
||||
k->rk.dname = NULL;
|
||||
alloc_special_release(a, k);
|
||||
|
||||
@@ -300,13 +300,12 @@ int rrsetdata_equal(struct packed_rrset_data* d1, struct packed_rrset_data* d2);
|
||||
|
||||
/**
|
||||
* Old key to be deleted. RRset keys are recycled via alloc.
|
||||
* The id is set to 0. So that other threads, after acquiring a lock always
|
||||
* get the correct value, in this case the 0 deleted-special value.
|
||||
* @param key: struct ub_packed_rrset_key*.
|
||||
* @param userdata: alloc structure to use for recycling.
|
||||
* @param is_locked: if the key is locked, the id is set to 0 while it is
|
||||
* still locked. So that other threads, after acquiring a lock always
|
||||
* get the correct value, in this case the 0 deleted-special value.
|
||||
*/
|
||||
void ub_rrset_key_delete(void* key, void* userdata, int is_locked);
|
||||
void ub_rrset_key_delete(void* key, void* userdata);
|
||||
|
||||
/**
|
||||
* Old data to be deleted.
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#include "services/mesh.h"
|
||||
#include "services/localzone.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "iterator/iterator.h"
|
||||
#include "iterator/iter_donotq.h"
|
||||
#include "iterator/iter_fwd.h"
|
||||
@@ -66,6 +67,8 @@
|
||||
#include "util/locks.h"
|
||||
#include "testcode/checklocks.h"
|
||||
#include "daemon/acl_list.h"
|
||||
#include "libunbound/libworker.h"
|
||||
#include "libunbound/context.h"
|
||||
|
||||
int
|
||||
fptr_whitelist_comm_point(comm_point_callback_t *fptr)
|
||||
@@ -82,6 +85,7 @@ fptr_whitelist_comm_timer(void (*fptr)(void*))
|
||||
{
|
||||
if(fptr == &pending_udp_timer_cb) return 1;
|
||||
else if(fptr == &outnet_tcptimer) return 1;
|
||||
else if(fptr == &worker_stat_timer_cb) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -96,11 +100,13 @@ int
|
||||
fptr_whitelist_event(void (*fptr)(int, short, void *))
|
||||
{
|
||||
if(fptr == &comm_point_udp_callback) return 1;
|
||||
else if(fptr == &comm_point_udp_ancil_callback) return 1;
|
||||
else if(fptr == &comm_point_tcp_accept_callback) return 1;
|
||||
else if(fptr == &comm_point_tcp_handle_callback) return 1;
|
||||
else if(fptr == &comm_timer_callback) return 1;
|
||||
else if(fptr == &comm_signal_callback) return 1;
|
||||
else if(fptr == &comm_point_local_handle_callback) return 1;
|
||||
else if(fptr == &comm_point_raw_handle_callback) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -109,6 +115,7 @@ fptr_whitelist_pending_udp(comm_point_callback_t *fptr)
|
||||
{
|
||||
if(fptr == &serviced_udp_callback) return 1;
|
||||
else if(fptr == &worker_handle_reply) return 1;
|
||||
else if(fptr == &libworker_handle_reply) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -117,6 +124,7 @@ fptr_whitelist_pending_tcp(comm_point_callback_t *fptr)
|
||||
{
|
||||
if(fptr == &serviced_tcp_callback) return 1;
|
||||
else if(fptr == &worker_handle_reply) return 1;
|
||||
else if(fptr == &libworker_handle_reply) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -124,6 +132,7 @@ int
|
||||
fptr_whitelist_serviced_query(comm_point_callback_t *fptr)
|
||||
{
|
||||
if(fptr == &worker_handle_service_reply) return 1;
|
||||
else if(fptr == &libworker_handle_service_reply) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -146,6 +155,7 @@ fptr_whitelist_rbtree_cmp(int (*fptr) (const void *, const void *))
|
||||
else if(fptr == &mini_ev_cmp) return 1;
|
||||
else if(fptr == &anchor_cmp) return 1;
|
||||
else if(fptr == &canonical_tree_compare) return 1;
|
||||
else if(fptr == &context_query_cmp) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -197,12 +207,21 @@ fptr_whitelist_hash_deldatafunc(lruhash_deldatafunc_t fptr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fptr_whitelist_hash_markdelfunc(lruhash_markdelfunc_t fptr)
|
||||
{
|
||||
if(fptr == NULL) return 1;
|
||||
else if(fptr == &rrset_markdel) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fptr_whitelist_modenv_send_packet(int (*fptr)(ldns_buffer* pkt,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
struct module_qstate* q, int use_tcp))
|
||||
{
|
||||
if(fptr == &worker_send_packet) return 1;
|
||||
else if(fptr == &libworker_send_packet) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -213,6 +232,7 @@ fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)(
|
||||
socklen_t addrlen, struct module_qstate* q))
|
||||
{
|
||||
if(fptr == &worker_send_query) return 1;
|
||||
else if(fptr == &libworker_send_query) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -155,6 +155,14 @@ int fptr_whitelist_hash_delkeyfunc(lruhash_delkeyfunc_t fptr);
|
||||
*/
|
||||
int fptr_whitelist_hash_deldatafunc(lruhash_deldatafunc_t fptr);
|
||||
|
||||
/**
|
||||
* Check function pointer whitelist for lruhash markdel callback values.
|
||||
*
|
||||
* @param fptr: function pointer to check.
|
||||
* @return false if not in whitelist.
|
||||
*/
|
||||
int fptr_whitelist_hash_markdelfunc(lruhash_markdelfunc_t fptr);
|
||||
|
||||
/**
|
||||
* Check function pointer whitelist for module_env send_packet callback values.
|
||||
*
|
||||
|
||||
+8
-2
@@ -83,7 +83,7 @@ log_init(const char* filename, int use_syslog, const char* chrootdir)
|
||||
|| log_to_syslog
|
||||
#endif
|
||||
)
|
||||
verbose(VERB_DETAIL, "switching log to %s",
|
||||
verbose(VERB_QUERY, "switching log to %s",
|
||||
use_syslog?"syslog":(filename&&filename[0]?filename:"stderr"));
|
||||
if(logfile && logfile != stderr)
|
||||
fclose(logfile);
|
||||
@@ -104,7 +104,7 @@ log_init(const char* filename, int use_syslog, const char* chrootdir)
|
||||
}
|
||||
/* open the file for logging */
|
||||
if(chrootdir && chrootdir[0] && strncmp(filename, chrootdir,
|
||||
strlen(chrootdir)) == 0)
|
||||
strlen(chrootdir)) == 0)
|
||||
filename += strlen(chrootdir);
|
||||
f = fopen(filename, "a");
|
||||
if(!f) {
|
||||
@@ -115,6 +115,11 @@ log_init(const char* filename, int use_syslog, const char* chrootdir)
|
||||
logfile = f;
|
||||
}
|
||||
|
||||
void log_file(FILE *f)
|
||||
{
|
||||
logfile = f;
|
||||
}
|
||||
|
||||
void log_thread_set(int* num)
|
||||
{
|
||||
ub_thread_key_set(logkey, num);
|
||||
@@ -140,6 +145,7 @@ log_vmsg(int pri, const char* type,
|
||||
return;
|
||||
}
|
||||
#endif /* HAVE_SYSLOG_H */
|
||||
if(!logfile) return;
|
||||
fprintf(logfile, "[%d] %s[%d:%x] %s: %s\n", (int)time(NULL),
|
||||
ident, (int)getpid(), tid?*tid:0, type, message);
|
||||
fflush(logfile);
|
||||
|
||||
+12
-5
@@ -55,9 +55,11 @@ enum verbosity_value {
|
||||
NO_VERBOSE = 0,
|
||||
/** 1 - operational information */
|
||||
VERB_OPS,
|
||||
/** 2 - query level information */
|
||||
/** 2 - detailed information */
|
||||
VERB_DETAIL,
|
||||
/** 3 - algorithm level information */
|
||||
/** 3 - query level information */
|
||||
VERB_QUERY,
|
||||
/** 4 - algorithm level information */
|
||||
VERB_ALGO
|
||||
};
|
||||
|
||||
@@ -82,6 +84,13 @@ void verbose(enum verbosity_value level,
|
||||
*/
|
||||
void log_init(const char* filename, int use_syslog, const char* chrootdir);
|
||||
|
||||
/**
|
||||
* Set logging to go to the specified file *.
|
||||
* This setting does not affect the use_syslog setting.
|
||||
* @param f: to that file, or pass NULL to disable logging.
|
||||
*/
|
||||
void log_file(FILE *f);
|
||||
|
||||
/**
|
||||
* Init a thread (will print this number for the thread log entries).
|
||||
* Must be called from the thread itself. If not called 0 is printed.
|
||||
@@ -151,12 +160,10 @@ void fatal_exit(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
|
||||
*/
|
||||
void log_vmsg(int pri, const char* type, const char* format, va_list args);
|
||||
|
||||
/** always assert for now. */
|
||||
#define UNBOUND_ASSERT 1
|
||||
/**
|
||||
* an assertion that is thrown to the logfile.
|
||||
*/
|
||||
#ifdef UNBOUND_ASSERT
|
||||
#ifdef UNBOUND_DEBUG
|
||||
# define log_assert(x) \
|
||||
do { if(!(x)) \
|
||||
fatal_exit("%s:%d: %s: assertion %s failed", \
|
||||
|
||||
@@ -204,6 +204,8 @@ int event_base_dispatch(struct event_base* base)
|
||||
return -1;
|
||||
/* see if timeouts need handling */
|
||||
handle_timeouts(base, &now, &wait);
|
||||
if(base->need_to_exit)
|
||||
return 0;
|
||||
/* do select */
|
||||
if(handle_select(base, &wait) < 0) {
|
||||
if(base->need_to_exit)
|
||||
|
||||
+8
-10
@@ -46,7 +46,7 @@
|
||||
/** max length of an IP address (the address portion) that we allow */
|
||||
#define MAX_ADDR_STRLEN 128 /* characters */
|
||||
|
||||
/** returns true is string addr is an ip6 specced address */
|
||||
/* returns true is string addr is an ip6 specced address */
|
||||
int
|
||||
str_is_ip6(const char* str)
|
||||
{
|
||||
@@ -169,8 +169,10 @@ log_addr(enum verbosity_value v, const char* str,
|
||||
}
|
||||
dest[sizeof(dest)-1] = 0;
|
||||
port = ntohs(((struct sockaddr_in*)addr)->sin_port);
|
||||
log_info("%s %s %s %d (len %d)", str, family, dest, (int)port,
|
||||
(int)addrlen);
|
||||
if(verbosity >= 4)
|
||||
log_info("%s %s %s port %d (len %d)", str, family, dest,
|
||||
(int)port, (int)addrlen);
|
||||
else log_info("%s %s port %d", str, dest, (int)port);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -182,14 +184,12 @@ extstrtoaddr(const char* str, struct sockaddr_storage* addr,
|
||||
if((s=strchr(str, '@'))) {
|
||||
char buf[MAX_ADDR_STRLEN];
|
||||
if(s-str >= MAX_ADDR_STRLEN) {
|
||||
log_err("address too long: '%s'", str);
|
||||
return 0;
|
||||
}
|
||||
strncpy(buf, str, MAX_ADDR_STRLEN);
|
||||
buf[s-str] = 0;
|
||||
port = atoi(s+1);
|
||||
if(port == 0 && strcmp(s+1,"0")!=0) {
|
||||
log_err("bad port spec in address: '%s", str);
|
||||
return 0;
|
||||
}
|
||||
return ipstrtoaddr(buf, port, addr, addrlen);
|
||||
@@ -212,7 +212,6 @@ ipstrtoaddr(const char* ip, int port, struct sockaddr_storage* addr,
|
||||
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 */
|
||||
@@ -222,7 +221,6 @@ ipstrtoaddr(const char* ip, int port, struct sockaddr_storage* addr,
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -240,7 +238,7 @@ int netblockstrtoaddr(const char* str, int port, struct sockaddr_storage* addr,
|
||||
return 0;
|
||||
}
|
||||
*net = atoi(s+1);
|
||||
if(net == 0 && strcmp(s+1, "0") != 0) {
|
||||
if(*net == 0 && strcmp(s+1, "0") != 0) {
|
||||
log_err("cannot parse netblock: '%s'", str);
|
||||
return 0;
|
||||
}
|
||||
@@ -320,9 +318,9 @@ void log_name_addr(enum verbosity_value v, const char* str, uint8_t* zone,
|
||||
port = ntohs(((struct sockaddr_in*)addr)->sin_port);
|
||||
dname_str(zone, namebuf);
|
||||
if(af != AF_INET && af != AF_INET6)
|
||||
verbose(VERB_DETAIL, "%s <%s> %s%s#%d (addrlen %d)",
|
||||
verbose(v, "%s <%s> %s%s#%d (addrlen %d)",
|
||||
str, namebuf, family, dest, (int)port, (int)addrlen);
|
||||
else verbose(VERB_DETAIL, "%s <%s> %s%s#%d",
|
||||
else verbose(v, "%s <%s> %s%s#%d",
|
||||
str, namebuf, family, dest, (int)port);
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -70,9 +70,9 @@
|
||||
#define FLAGS_SET_RCODE(f, r) (f = (((f) & 0xfff0) | (r)))
|
||||
|
||||
/** timeout in seconds for UDP queries to auth servers. */
|
||||
#define UDP_QUERY_TIMEOUT 4
|
||||
#define UDP_AUTH_QUERY_TIMEOUT 4
|
||||
/** timeout in seconds for TCP queries to auth servers. */
|
||||
#define TCP_QUERY_TIMEOUT 30
|
||||
#define TCP_AUTH_QUERY_TIMEOUT 30
|
||||
/** Advertised version of EDNS capabilities */
|
||||
#define EDNS_ADVERTISED_VERSION 0
|
||||
/** Advertised size of EDNS capabilities */
|
||||
@@ -245,7 +245,7 @@ int addr_is_ip6(struct sockaddr_storage* addr, socklen_t len);
|
||||
* Make sure the sockaddr ends in zeroes. For tree insertion and subsequent
|
||||
* comparison.
|
||||
* @param addr: the ip4 or ip6 addr.
|
||||
* @Param len: lentgh of addr.
|
||||
* @param len: length of addr.
|
||||
* @param net: number of bits to leave untouched, the rest of the netblock
|
||||
* address is zeroed.
|
||||
*/
|
||||
|
||||
+368
-81
@@ -41,6 +41,7 @@
|
||||
|
||||
#include "util/netevent.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
|
||||
/* -------- Start of local definitions -------- */
|
||||
@@ -128,10 +129,14 @@ comm_base_delete(struct comm_base* b)
|
||||
{
|
||||
if(!b)
|
||||
return;
|
||||
#ifdef HAVE_EVENT_BASE_FREE
|
||||
/* only libevent 1.2+ has it */
|
||||
#ifdef USE_MINI_EVENT
|
||||
event_base_free(b->eb->base);
|
||||
#endif /* HAVE_EVENT_BASE_FREE */
|
||||
#elif defined(HAVE_EVENT_BASE_FREE) && defined(HAVE_EVENT_BASE_ONCE)
|
||||
/* only libevent 1.2+ has it, but in 1.2 it is broken -
|
||||
assertion fails on signal handling ev that is not deleted
|
||||
in libevent 1.3c (event_base_once appears) this is fixed. */
|
||||
event_base_free(b->eb->base);
|
||||
#endif /* HAVE_EVENT_BASE_FREE and HAVE_EVENT_BASE_ONCE */
|
||||
b->eb->base = NULL;
|
||||
free(b->eb);
|
||||
free(b);
|
||||
@@ -155,7 +160,7 @@ void comm_base_exit(struct comm_base* b)
|
||||
}
|
||||
}
|
||||
|
||||
/** send a UDP reply */
|
||||
/* send a UDP reply */
|
||||
int
|
||||
comm_point_send_udp_msg(struct comm_point *c, ldns_buffer* packet,
|
||||
struct sockaddr* addr, socklen_t addrlen)
|
||||
@@ -168,7 +173,9 @@ comm_point_send_udp_msg(struct comm_point *c, ldns_buffer* packet,
|
||||
ldns_buffer_remaining(packet), 0,
|
||||
addr, addrlen);
|
||||
if(sent == -1) {
|
||||
log_err("sendto failed: %s", strerror(errno));
|
||||
verbose(VERB_OPS, "sendto failed: %s", strerror(errno));
|
||||
log_addr(VERB_OPS, "remote address is",
|
||||
(struct sockaddr_storage*)addr, addrlen);
|
||||
return 0;
|
||||
} else if((size_t)sent != ldns_buffer_remaining(packet)) {
|
||||
log_err("sent %d in place of %d bytes",
|
||||
@@ -178,6 +185,223 @@ comm_point_send_udp_msg(struct comm_point *c, ldns_buffer* packet,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** if no CMSG_LEN (Solaris 9) define something reasonable for one element */
|
||||
#ifndef CMSG_LEN
|
||||
#define CMSG_LEN(x) (sizeof(struct cmsghdr)+(x))
|
||||
#endif
|
||||
|
||||
/** print debug ancillary info */
|
||||
void p_ancil(const char* str, struct comm_reply* r)
|
||||
{
|
||||
#if defined(AF_INET6) && defined(IPV6_PKTINFO)
|
||||
if(r->srctype != 4 && r->srctype != 6) {
|
||||
log_info("%s: unknown srctype %d", str, r->srctype);
|
||||
return;
|
||||
}
|
||||
if(r->srctype == 6) {
|
||||
char buf[1024];
|
||||
if(inet_ntop(AF_INET6, &r->pktinfo.v6info.ipi6_addr,
|
||||
buf, (socklen_t)sizeof(buf)) == 0) {
|
||||
strncpy(buf, "(inet_ntop error)", sizeof(buf));
|
||||
}
|
||||
buf[sizeof(buf)-1]=0;
|
||||
log_info("%s: %s %d", str, buf, r->pktinfo.v6info.ipi6_ifindex);
|
||||
} else if(r->srctype == 4) {
|
||||
#ifdef IP_RECVDSTADDR
|
||||
char buf1[1024];
|
||||
if(inet_ntop(AF_INET, &r->pktinfo.v4addr,
|
||||
buf1, (socklen_t)sizeof(buf1)) == 0) {
|
||||
strncpy(buf1, "(inet_ntop error)", sizeof(buf1));
|
||||
}
|
||||
buf1[sizeof(buf1)-1]=0;
|
||||
log_info("%s: %s", str, buf1);
|
||||
#elif defined(IP_PKTINFO)
|
||||
char buf1[1024], buf2[1024];
|
||||
if(inet_ntop(AF_INET, &r->pktinfo.v4info.ipi_addr,
|
||||
buf1, (socklen_t)sizeof(buf1)) == 0) {
|
||||
strncpy(buf1, "(inet_ntop error)", sizeof(buf1));
|
||||
}
|
||||
buf1[sizeof(buf1)-1]=0;
|
||||
if(inet_ntop(AF_INET, &r->pktinfo.v4info.ipi_spec_dst,
|
||||
buf2, (socklen_t)sizeof(buf2)) == 0) {
|
||||
strncpy(buf2, "(inet_ntop error)", sizeof(buf2));
|
||||
}
|
||||
buf2[sizeof(buf2)-1]=0;
|
||||
log_info("%s: %d %s %s", str, r->pktinfo.v4info.ipi_ifindex,
|
||||
buf1, buf2);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** send a UDP reply over specified interface*/
|
||||
int
|
||||
comm_point_send_udp_msg_if(struct comm_point *c, ldns_buffer* packet,
|
||||
struct sockaddr* addr, socklen_t addrlen, struct comm_reply* r)
|
||||
{
|
||||
#if defined(AF_INET6) && defined(IPV6_PKTINFO)
|
||||
ssize_t sent;
|
||||
struct msghdr msg;
|
||||
struct iovec iov[1];
|
||||
char control[256];
|
||||
#ifndef S_SPLINT_S
|
||||
struct cmsghdr *cmsg;
|
||||
#endif /* S_SPLINT_S */
|
||||
|
||||
log_assert(c->fd != -1);
|
||||
log_assert(ldns_buffer_remaining(packet) > 0);
|
||||
log_assert(addr && addrlen > 0);
|
||||
|
||||
msg.msg_name = addr;
|
||||
msg.msg_namelen = addrlen;
|
||||
iov[0].iov_base = ldns_buffer_begin(packet);
|
||||
iov[0].iov_len = ldns_buffer_remaining(packet);
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = control;
|
||||
#ifndef S_SPLINT_S
|
||||
msg.msg_controllen = sizeof(control);
|
||||
#endif /* S_SPLINT_S */
|
||||
msg.msg_flags = 0;
|
||||
|
||||
#ifndef S_SPLINT_S
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if(r->srctype == 4) {
|
||||
#ifdef IP_RECVDSTADDR
|
||||
cmsg->cmsg_level = IPPROTO_IP;
|
||||
cmsg->cmsg_type = IP_RECVDSTADDR;
|
||||
memmove(CMSG_DATA(cmsg), &r->pktinfo.v4addr,
|
||||
sizeof(struct in_addr));
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
|
||||
#elif defined(IP_PKTINFO)
|
||||
cmsg->cmsg_level = IPPROTO_IP;
|
||||
cmsg->cmsg_type = IP_PKTINFO;
|
||||
memmove(CMSG_DATA(cmsg), &r->pktinfo.v4info,
|
||||
sizeof(struct in_pktinfo));
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
|
||||
#endif
|
||||
} else if(r->srctype == 6) {
|
||||
cmsg->cmsg_level = IPPROTO_IPV6;
|
||||
cmsg->cmsg_type = IPV6_PKTINFO;
|
||||
memmove(CMSG_DATA(cmsg), &r->pktinfo.v6info,
|
||||
sizeof(struct in6_pktinfo));
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
|
||||
} else {
|
||||
/* try to pass all 0 to use default route */
|
||||
cmsg->cmsg_level = IPPROTO_IPV6;
|
||||
cmsg->cmsg_type = IPV6_PKTINFO;
|
||||
memset(CMSG_DATA(cmsg), 0, sizeof(struct in6_pktinfo));
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
|
||||
}
|
||||
msg.msg_controllen = cmsg->cmsg_len;
|
||||
#endif /* S_SPLINT_S */
|
||||
|
||||
if(verbosity >= VERB_ALGO)
|
||||
p_ancil("send_udp over interface", r);
|
||||
sent = sendmsg(c->fd, &msg, 0);
|
||||
if(sent == -1) {
|
||||
verbose(VERB_OPS, "sendmsg failed: %s", strerror(errno));
|
||||
log_addr(VERB_OPS, "remote address is",
|
||||
(struct sockaddr_storage*)addr, addrlen);
|
||||
return 0;
|
||||
} else if((size_t)sent != ldns_buffer_remaining(packet)) {
|
||||
log_err("sent %d in place of %d bytes",
|
||||
(int)sent, (int)ldns_buffer_remaining(packet));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
#else
|
||||
log_err("sendmsg: IPV6_PKTINFO not supported");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
comm_point_udp_ancil_callback(int fd, short event, void* arg)
|
||||
{
|
||||
#if defined(AF_INET6) && defined(IPV6_PKTINFO)
|
||||
struct comm_reply rep;
|
||||
struct msghdr msg;
|
||||
struct iovec iov[1];
|
||||
ssize_t recv;
|
||||
char ancil[256];
|
||||
#ifndef S_SPLINT_S
|
||||
struct cmsghdr* cmsg;
|
||||
#endif /* S_SPLINT_S */
|
||||
|
||||
rep.c = (struct comm_point*)arg;
|
||||
log_assert(rep.c->type == comm_udp);
|
||||
|
||||
if(!(event&EV_READ))
|
||||
return;
|
||||
log_assert(rep.c && rep.c->buffer && rep.c->fd == fd);
|
||||
ldns_buffer_clear(rep.c->buffer);
|
||||
rep.addrlen = (socklen_t)sizeof(rep.addr);
|
||||
log_assert(fd != -1);
|
||||
log_assert(ldns_buffer_remaining(rep.c->buffer) > 0);
|
||||
msg.msg_name = &rep.addr;
|
||||
msg.msg_namelen = (socklen_t)sizeof(rep.addr);
|
||||
iov[0].iov_base = ldns_buffer_begin(rep.c->buffer);
|
||||
iov[0].iov_len = ldns_buffer_remaining(rep.c->buffer);
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = ancil;
|
||||
#ifndef S_SPLINT_S
|
||||
msg.msg_controllen = sizeof(ancil);
|
||||
#endif /* S_SPLINT_S */
|
||||
msg.msg_flags = 0;
|
||||
recv = recvmsg(fd, &msg, 0);
|
||||
if(recv == -1) {
|
||||
if(errno != EAGAIN && errno != EINTR) {
|
||||
log_err("recvmsg failed: %s", strerror(errno));
|
||||
}
|
||||
return;
|
||||
}
|
||||
rep.addrlen = msg.msg_namelen;
|
||||
ldns_buffer_skip(rep.c->buffer, recv);
|
||||
ldns_buffer_flip(rep.c->buffer);
|
||||
rep.srctype = 0;
|
||||
#ifndef S_SPLINT_S
|
||||
for(cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
|
||||
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if( cmsg->cmsg_level == IPPROTO_IPV6 &&
|
||||
cmsg->cmsg_type == IPV6_PKTINFO) {
|
||||
rep.srctype = 6;
|
||||
memmove(&rep.pktinfo.v6info, CMSG_DATA(cmsg),
|
||||
sizeof(struct in6_pktinfo));
|
||||
break;
|
||||
#ifdef IP_RECVDSTADDR
|
||||
} else if( cmsg->cmsg_level == IPPROTO_IP &&
|
||||
cmsg->cmsg_type == IP_RECVDSTADDR) {
|
||||
rep.srctype = 4;
|
||||
memmove(&rep.pktinfo.v4addr, CMSG_DATA(cmsg),
|
||||
sizeof(struct in_addr));
|
||||
break;
|
||||
#elif defined(IP_PKTINFO)
|
||||
} else if( cmsg->cmsg_level == IPPROTO_IP &&
|
||||
cmsg->cmsg_type == IP_PKTINFO) {
|
||||
rep.srctype = 4;
|
||||
memmove(&rep.pktinfo.v4info, CMSG_DATA(cmsg),
|
||||
sizeof(struct in_pktinfo));
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if(verbosity >= VERB_ALGO)
|
||||
p_ancil("receive_udp on interface", &rep);
|
||||
#endif /* S_SPLINT_S */
|
||||
log_assert(fptr_whitelist_comm_point(rep.c->callback));
|
||||
if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) {
|
||||
/* send back immediate reply */
|
||||
(void)comm_point_send_udp_msg_if(rep.c, rep.c->buffer,
|
||||
(struct sockaddr*)&rep.addr, rep.addrlen, &rep);
|
||||
}
|
||||
#else
|
||||
fatal_exit("recvmsg: No support for IPV6_PKTINFO. "
|
||||
"Please disable interface-automatic");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
comm_point_udp_callback(int fd, short event, void* arg)
|
||||
{
|
||||
@@ -205,6 +429,7 @@ comm_point_udp_callback(int fd, short event, void* arg)
|
||||
}
|
||||
ldns_buffer_skip(rep.c->buffer, recv);
|
||||
ldns_buffer_flip(rep.c->buffer);
|
||||
rep.srctype = 0;
|
||||
log_assert(fptr_whitelist_comm_point(rep.c->callback));
|
||||
if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) {
|
||||
/* send back immediate reply */
|
||||
@@ -257,6 +482,8 @@ comm_point_tcp_accept_callback(int fd, short event, void* arg)
|
||||
)
|
||||
return;
|
||||
log_err("accept failed: %s", strerror(errno));
|
||||
log_addr(0, "remote address is", &c_hdl->repinfo.addr,
|
||||
c_hdl->repinfo.addrlen);
|
||||
return;
|
||||
}
|
||||
/* grab the tcp handler buffers */
|
||||
@@ -294,11 +521,9 @@ tcp_callback_writer(struct comm_point* c)
|
||||
if(c->tcp_do_toggle_rw)
|
||||
c->tcp_is_reading = 1;
|
||||
c->tcp_byte_count = 0;
|
||||
/* switch from listening(write) to listening(read) */
|
||||
comm_point_stop_listening(c);
|
||||
if(c->tcp_parent) /* for listening socket */
|
||||
reclaim_tcp_handler(c);
|
||||
else /* its outgoing socket, start listening for reading */
|
||||
comm_point_start_listening(c, -1, -1);
|
||||
comm_point_start_listening(c, -1, -1);
|
||||
}
|
||||
|
||||
/** do the callback when reading is done */
|
||||
@@ -345,6 +570,8 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
|
||||
if(errno == ECONNRESET && verbosity < 2)
|
||||
return 0; /* silence reset by peer */
|
||||
log_err("read (in tcp s): %s", strerror(errno));
|
||||
log_addr(0, "remote address is", &c->repinfo.addr,
|
||||
c->repinfo.addrlen);
|
||||
return 0;
|
||||
}
|
||||
c->tcp_byte_count += r;
|
||||
@@ -352,14 +579,14 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
|
||||
return 1;
|
||||
if(ldns_buffer_read_u16_at(c->buffer, 0) >
|
||||
ldns_buffer_capacity(c->buffer)) {
|
||||
verbose(VERB_DETAIL, "tcp: dropped larger than buffer");
|
||||
verbose(VERB_QUERY, "tcp: dropped larger than buffer");
|
||||
return 0;
|
||||
}
|
||||
ldns_buffer_set_limit(c->buffer,
|
||||
ldns_buffer_read_u16_at(c->buffer, 0));
|
||||
if(!short_ok &&
|
||||
ldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
|
||||
verbose(VERB_DETAIL, "tcp: dropped bogus too short.");
|
||||
verbose(VERB_QUERY, "tcp: dropped bogus too short.");
|
||||
return 0;
|
||||
}
|
||||
verbose(VERB_ALGO, "Reading tcp query of length %d",
|
||||
@@ -375,6 +602,8 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
|
||||
if(errno == EINTR || errno == EAGAIN)
|
||||
return 1;
|
||||
log_err("read (in tcp r): %s", strerror(errno));
|
||||
log_addr(0, "remote address is", &c->repinfo.addr,
|
||||
c->repinfo.addrlen);
|
||||
return 0;
|
||||
}
|
||||
ldns_buffer_skip(c->buffer, r);
|
||||
@@ -408,10 +637,18 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
|
||||
}
|
||||
if(error == EINPROGRESS || error == EWOULDBLOCK)
|
||||
return 1; /* try again later */
|
||||
if(error == ECONNREFUSED && verbosity < 2)
|
||||
return 0; /* silence 'connection refused' */
|
||||
if(error != 0) {
|
||||
#ifdef ECONNREFUSED
|
||||
else if(error == ECONNREFUSED && verbosity < 2)
|
||||
return 0; /* silence 'connection refused' */
|
||||
#endif
|
||||
#ifdef EHOSTUNREACH
|
||||
else if(error == EHOSTUNREACH && verbosity < 2)
|
||||
return 0; /* silence 'no route to host' */
|
||||
#endif
|
||||
else if(error != 0) {
|
||||
log_err("tcp connect: %s", strerror(error));
|
||||
log_addr(0, "remote address is", &c->repinfo.addr,
|
||||
c->repinfo.addrlen);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -430,6 +667,8 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
|
||||
if(errno == EINTR || errno == EAGAIN)
|
||||
return 1;
|
||||
log_err("tcp writev: %s", strerror(errno));
|
||||
log_addr(0, "remote address is", &c->repinfo.addr,
|
||||
c->repinfo.addrlen);
|
||||
return 0;
|
||||
}
|
||||
c->tcp_byte_count += r;
|
||||
@@ -449,6 +688,8 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
|
||||
if(errno == EINTR || errno == EAGAIN)
|
||||
return 1;
|
||||
log_err("tcp write: %s", strerror(errno));
|
||||
log_addr(0, "remote address is", &c->repinfo.addr,
|
||||
c->repinfo.addrlen);
|
||||
return 0;
|
||||
}
|
||||
ldns_buffer_skip(c->buffer, r);
|
||||
@@ -491,7 +732,7 @@ comm_point_tcp_handle_callback(int fd, short event, void* arg)
|
||||
return;
|
||||
}
|
||||
if(event&EV_TIMEOUT) {
|
||||
verbose(VERB_DETAIL, "tcp took too long, dropped");
|
||||
verbose(VERB_QUERY, "tcp took too long, dropped");
|
||||
reclaim_tcp_handler(c);
|
||||
if(!c->tcp_do_close) {
|
||||
log_assert(fptr_whitelist_comm_point(c->callback));
|
||||
@@ -519,6 +760,15 @@ void comm_point_local_handle_callback(int fd, short event, void* arg)
|
||||
log_err("Ignored event %d for localhdl.", event);
|
||||
}
|
||||
|
||||
void comm_point_raw_handle_callback(int ATTR_UNUSED(fd),
|
||||
short ATTR_UNUSED(event), void* arg)
|
||||
{
|
||||
struct comm_point* c = (struct comm_point*)arg;
|
||||
log_assert(c->type == comm_raw);
|
||||
|
||||
(void)(*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, NULL);
|
||||
}
|
||||
|
||||
struct comm_point*
|
||||
comm_point_create_udp(struct comm_base *base, int fd, ldns_buffer* buffer,
|
||||
comm_point_callback_t* callback, void* callback_arg)
|
||||
@@ -562,6 +812,50 @@ comm_point_create_udp(struct comm_base *base, int fd, ldns_buffer* buffer,
|
||||
return c;
|
||||
}
|
||||
|
||||
struct comm_point*
|
||||
comm_point_create_udp_ancil(struct comm_base *base, int fd,
|
||||
ldns_buffer* buffer,
|
||||
comm_point_callback_t* callback, void* callback_arg)
|
||||
{
|
||||
struct comm_point* c = (struct comm_point*)calloc(1,
|
||||
sizeof(struct comm_point));
|
||||
short evbits;
|
||||
if(!c)
|
||||
return NULL;
|
||||
c->ev = (struct internal_event*)calloc(1,
|
||||
sizeof(struct internal_event));
|
||||
if(!c->ev) {
|
||||
free(c);
|
||||
return NULL;
|
||||
}
|
||||
c->fd = fd;
|
||||
c->buffer = buffer;
|
||||
c->timeout = NULL;
|
||||
c->tcp_is_reading = 0;
|
||||
c->tcp_byte_count = 0;
|
||||
c->tcp_parent = NULL;
|
||||
c->max_tcp_count = 0;
|
||||
c->tcp_handlers = NULL;
|
||||
c->tcp_free = NULL;
|
||||
c->type = comm_udp;
|
||||
c->tcp_do_close = 0;
|
||||
c->do_not_close = 0;
|
||||
c->tcp_do_toggle_rw = 0;
|
||||
c->tcp_check_nb_connect = 0;
|
||||
c->callback = callback;
|
||||
c->cb_arg = callback_arg;
|
||||
evbits = EV_READ | EV_PERSIST;
|
||||
/* libevent stuff */
|
||||
event_set(&c->ev->ev, c->fd, evbits, comm_point_udp_ancil_callback, c);
|
||||
if(event_base_set(base->eb->base, &c->ev->ev) != 0 ||
|
||||
event_add(&c->ev->ev, c->timeout) != 0 ) {
|
||||
log_err("could not add udp event");
|
||||
comm_point_delete(c);
|
||||
return NULL;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static struct comm_point*
|
||||
comm_point_create_tcp_handler(struct comm_base *base,
|
||||
struct comm_point* parent, size_t bufsize,
|
||||
@@ -780,7 +1074,55 @@ comm_point_create_local(struct comm_base *base, int fd, size_t bufsize,
|
||||
if(event_base_set(base->eb->base, &c->ev->ev) != 0 ||
|
||||
event_add(&c->ev->ev, c->timeout) != 0 )
|
||||
{
|
||||
log_err("could not add tcphdl event");
|
||||
log_err("could not add localhdl event");
|
||||
free(c->ev);
|
||||
free(c);
|
||||
return NULL;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
struct comm_point*
|
||||
comm_point_create_raw(struct comm_base* base, int fd, int writing,
|
||||
comm_point_callback_t* callback, void* callback_arg)
|
||||
{
|
||||
struct comm_point* c = (struct comm_point*)calloc(1,
|
||||
sizeof(struct comm_point));
|
||||
short evbits;
|
||||
if(!c)
|
||||
return NULL;
|
||||
c->ev = (struct internal_event*)calloc(1,
|
||||
sizeof(struct internal_event));
|
||||
if(!c->ev) {
|
||||
free(c);
|
||||
return NULL;
|
||||
}
|
||||
c->fd = fd;
|
||||
c->buffer = NULL;
|
||||
c->timeout = NULL;
|
||||
c->tcp_is_reading = 0;
|
||||
c->tcp_byte_count = 0;
|
||||
c->tcp_parent = NULL;
|
||||
c->max_tcp_count = 0;
|
||||
c->tcp_handlers = NULL;
|
||||
c->tcp_free = NULL;
|
||||
c->type = comm_raw;
|
||||
c->tcp_do_close = 0;
|
||||
c->do_not_close = 1;
|
||||
c->tcp_do_toggle_rw = 0;
|
||||
c->tcp_check_nb_connect = 0;
|
||||
c->callback = callback;
|
||||
c->cb_arg = callback_arg;
|
||||
/* libevent stuff */
|
||||
if(writing)
|
||||
evbits = EV_PERSIST | EV_WRITE;
|
||||
else evbits = EV_PERSIST | EV_READ;
|
||||
event_set(&c->ev->ev, c->fd, evbits, comm_point_raw_handle_callback,
|
||||
c);
|
||||
if(event_base_set(base->eb->base, &c->ev->ev) != 0 ||
|
||||
event_add(&c->ev->ev, c->timeout) != 0 )
|
||||
{
|
||||
log_err("could not add rawhdl event");
|
||||
free(c->ev);
|
||||
free(c);
|
||||
return NULL;
|
||||
@@ -834,76 +1176,18 @@ comm_point_send_reply(struct comm_reply *repinfo)
|
||||
{
|
||||
log_assert(repinfo && repinfo->c);
|
||||
if(repinfo->c->type == comm_udp) {
|
||||
comm_point_send_udp_msg(repinfo->c, repinfo->c->buffer,
|
||||
if(repinfo->srctype)
|
||||
comm_point_send_udp_msg_if(repinfo->c,
|
||||
repinfo->c->buffer, (struct sockaddr*)&repinfo->addr,
|
||||
repinfo->addrlen, repinfo);
|
||||
else
|
||||
comm_point_send_udp_msg(repinfo->c, repinfo->c->buffer,
|
||||
(struct sockaddr*)&repinfo->addr, repinfo->addrlen);
|
||||
} else {
|
||||
comm_point_start_listening(repinfo->c, -1, TCP_QUERY_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
comm_point_send_reply_iov(struct comm_reply* repinfo, struct iovec* iov,
|
||||
size_t iovlen)
|
||||
{
|
||||
log_assert(repinfo && repinfo->c);
|
||||
log_assert(repinfo->c->fd != -1);
|
||||
log_assert(repinfo->addrlen > 0);
|
||||
if(repinfo->c->type == comm_udp) {
|
||||
struct msghdr hdr;
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
hdr.msg_name = &repinfo->addr;
|
||||
hdr.msg_namelen = repinfo->addrlen;
|
||||
hdr.msg_iov = iov + 1;
|
||||
hdr.msg_iovlen = (TYPE_MSGIOVLEN)(iovlen - 1);
|
||||
/* note that number of characters sent is not checked. */
|
||||
if(sendmsg(repinfo->c->fd, &hdr, 0) == -1)
|
||||
log_err("sendmsg: %s", strerror(errno));
|
||||
} else {
|
||||
/* try if it can be sent in writev right now */
|
||||
size_t i;
|
||||
uint16_t len = 0;
|
||||
ssize_t done;
|
||||
for(i=1; i<iovlen; i++)
|
||||
len += iov[i].iov_len;
|
||||
len = htons(len);
|
||||
iov[0].iov_base = (void*)&len;
|
||||
iov[0].iov_len = sizeof(uint16_t);
|
||||
if((done=writev(repinfo->c->fd, iov, (int)iovlen)) == -1) {
|
||||
#ifdef S_SPLINT_S
|
||||
/* don't complain about returning stack references */
|
||||
iov[0].iov_base = NULL;
|
||||
#endif
|
||||
if(errno != EINTR && errno != EAGAIN) {
|
||||
log_err("writev: %s", strerror(errno));
|
||||
comm_point_drop_reply(repinfo);
|
||||
return;
|
||||
}
|
||||
done = 0;
|
||||
}
|
||||
#ifdef S_SPLINT_S
|
||||
/* don't complain about returning stack references */
|
||||
iov[0].iov_base = NULL;
|
||||
#endif
|
||||
if((size_t)done == ntohs(len) + sizeof(uint16_t)) {
|
||||
/* done in one call */
|
||||
comm_point_drop_reply(repinfo);
|
||||
} else {
|
||||
/* sending remaining bytes */
|
||||
ldns_buffer_clear(repinfo->c->buffer);
|
||||
repinfo->c->tcp_byte_count = (size_t)done;
|
||||
for(i=1; i<iovlen; i++)
|
||||
ldns_buffer_write(repinfo->c->buffer,
|
||||
iov[i].iov_base, iov[i].iov_len);
|
||||
ldns_buffer_flip(repinfo->c->buffer);
|
||||
if((size_t)done >= sizeof(uint16_t))
|
||||
ldns_buffer_set_position(repinfo->c->buffer,
|
||||
(size_t)done - sizeof(uint16_t));
|
||||
comm_point_start_listening(repinfo->c, -1,
|
||||
TCP_QUERY_TIMEOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
comm_point_drop_reply(struct comm_reply* repinfo)
|
||||
{
|
||||
@@ -1024,7 +1308,10 @@ comm_timer_set(struct comm_timer* timer, struct timeval* tv)
|
||||
log_assert(tv);
|
||||
if(timer->ev_timer->enabled)
|
||||
comm_timer_disable(timer);
|
||||
evtimer_add(&timer->ev_timer->ev, tv);
|
||||
event_set(&timer->ev_timer->ev, -1, EV_PERSIST|EV_TIMEOUT,
|
||||
comm_timer_callback, timer);
|
||||
if(evtimer_add(&timer->ev_timer->ev, tv) != 0)
|
||||
log_err("comm_timer_set: evtimer_add failed.");
|
||||
timer->ev_timer->enabled = 1;
|
||||
}
|
||||
|
||||
|
||||
+69
-14
@@ -98,6 +98,21 @@ struct comm_reply {
|
||||
struct sockaddr_storage addr;
|
||||
/** length of address */
|
||||
socklen_t addrlen;
|
||||
/** return type 0 (none), 4(IP4), 6(IP6) */
|
||||
int srctype;
|
||||
/** the return source interface data */
|
||||
union {
|
||||
#ifdef IPV6_PKTINFO
|
||||
struct in6_pktinfo v6info;
|
||||
#endif
|
||||
#ifdef IP_RECVDSTADDR
|
||||
struct in_addr v4addr;
|
||||
#elif defined(IP_PKTINFO)
|
||||
struct in_pktinfo v4info;
|
||||
#endif
|
||||
}
|
||||
/** variable with return source data */
|
||||
pktinfo;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -151,8 +166,12 @@ struct comm_point {
|
||||
/** TCP handler socket - handle byteperbyte readwrite. */
|
||||
comm_tcp,
|
||||
/** AF_UNIX socket - for internal commands. */
|
||||
comm_local
|
||||
} type;
|
||||
comm_local,
|
||||
/** raw - not DNS format - for pipe readers and writers */
|
||||
comm_raw
|
||||
}
|
||||
/** variable with type of socket, UDP,TCP-accept,TCP,pipe */
|
||||
type;
|
||||
|
||||
/* ---------- Behaviour ----------- */
|
||||
/** if set the connection is NOT closed on delete. */
|
||||
@@ -272,6 +291,22 @@ struct comm_point* comm_point_create_udp(struct comm_base* base,
|
||||
int fd, ldns_buffer* buffer,
|
||||
comm_point_callback_t* callback, void* callback_arg);
|
||||
|
||||
/**
|
||||
* Create an UDP with ancillary data comm point. Calls malloc.
|
||||
* Uses recvmsg instead of recv to get udp message.
|
||||
* setups the structure with the parameters you provide.
|
||||
* @param base: in which base to alloc the commpoint.
|
||||
* @param fd : file descriptor of open UDP socket.
|
||||
* @param buffer: shared buffer by UDP sockets from this thread.
|
||||
* @param callback: callback function pointer.
|
||||
* @param callback_arg: will be passed to your callback function.
|
||||
* @return: returns the allocated communication point. NULL on error.
|
||||
* Sets timeout to NULL. Turns off TCP options.
|
||||
*/
|
||||
struct comm_point* comm_point_create_udp_ancil(struct comm_base* base,
|
||||
int fd, ldns_buffer* buffer,
|
||||
comm_point_callback_t* callback, void* callback_arg);
|
||||
|
||||
/**
|
||||
* Create a TCP listener comm point. Calls malloc.
|
||||
* Setups the structure with the parameters you provide.
|
||||
@@ -317,6 +352,19 @@ struct comm_point* comm_point_create_local(struct comm_base* base,
|
||||
int fd, size_t bufsize,
|
||||
comm_point_callback_t* callback, void* callback_arg);
|
||||
|
||||
/**
|
||||
* Create commpoint to listen to a local domain pipe descriptor.
|
||||
* @param base: in which base to alloc the commpoint.
|
||||
* @param fd: file descriptor.
|
||||
* @param writing: true if you want to listen to writes, false for reads.
|
||||
* @param callback: callback function pointer for the handler.
|
||||
* @param callback_arg: will be passed to your callback function.
|
||||
* @return: the commpoint or NULL on error.
|
||||
*/
|
||||
struct comm_point* comm_point_create_raw(struct comm_base* base,
|
||||
int fd, int writing,
|
||||
comm_point_callback_t* callback, void* callback_arg);
|
||||
|
||||
/**
|
||||
* Close a comm point fd.
|
||||
* @param c: comm point to close.
|
||||
@@ -343,18 +391,6 @@ void comm_point_set_cb_arg(struct comm_point* c, void* arg);
|
||||
*/
|
||||
void comm_point_send_reply(struct comm_reply* repinfo);
|
||||
|
||||
/**
|
||||
* Send reply. Message is not put into commpoint buffer, but in iovec.
|
||||
* If it cannot be sent immediately (TCP) the message is copied to the buffer.
|
||||
* @param repinfo: reply info copied from commpoint callback call.
|
||||
* @param iov: iovector, array of base, len parts to send out.
|
||||
* caller must keep entry 0 free for use by tcp handler. Start at entry 1.
|
||||
* @param iovlen: number of iov items to concatenate and send out.
|
||||
* this includes the entry 0, which is not filled in by caller.
|
||||
*/
|
||||
void comm_point_send_reply_iov(struct comm_reply* repinfo, struct iovec* iov,
|
||||
size_t iovlen);
|
||||
|
||||
/**
|
||||
* Drop reply. Cleans up.
|
||||
* @param repinfo: The reply info copied from a commpoint callback call.
|
||||
@@ -476,6 +512,16 @@ void comm_signal_delete(struct comm_signal* comsig);
|
||||
*/
|
||||
void comm_point_udp_callback(int fd, short event, void* arg);
|
||||
|
||||
/**
|
||||
* This routine is published for checks and tests, and is only used internally.
|
||||
* handle libevent callback for udp ancillary data comm point.
|
||||
* @param fd: file descriptor.
|
||||
* @param event: event bits from libevent:
|
||||
* EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
|
||||
* @param arg: the comm_point structure.
|
||||
*/
|
||||
void comm_point_udp_ancil_callback(int fd, short event, void* arg);
|
||||
|
||||
/**
|
||||
* This routine is published for checks and tests, and is only used internally.
|
||||
* handle libevent callback for tcp accept comm point
|
||||
@@ -526,5 +572,14 @@ void comm_signal_callback(int fd, short event, void* arg);
|
||||
*/
|
||||
void comm_point_local_handle_callback(int fd, short event, void* arg);
|
||||
|
||||
/**
|
||||
* This routine is published for checks and tests, and is only used internally.
|
||||
* libevent callback for raw fd access.
|
||||
* @param fd: file descriptor.
|
||||
* @param event: event bits from libevent:
|
||||
* EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
|
||||
* @param arg: the comm_point structure.
|
||||
*/
|
||||
void comm_point_raw_handle_callback(int fd, short event, void* arg);
|
||||
|
||||
#endif /* NET_EVENT_H */
|
||||
|
||||
+43
-26
@@ -53,7 +53,7 @@
|
||||
* Struct with per-thread random state.
|
||||
* Keeps SSL types away from the header file.
|
||||
*/
|
||||
struct ub_hiddenstate {
|
||||
struct ub_randstate {
|
||||
/** key used for arc4random generation */
|
||||
RC4_KEY rc4;
|
||||
/** keeps track of key usage */
|
||||
@@ -68,16 +68,22 @@ struct ub_hiddenstate {
|
||||
|
||||
/** reseed random generator */
|
||||
static void
|
||||
ub_arc4random_stir(struct ub_hiddenstate* s)
|
||||
ub_arc4random_stir(struct ub_randstate* s, struct ub_randstate* from)
|
||||
{
|
||||
unsigned char rand_buf[SEED_SIZE];
|
||||
int i;
|
||||
|
||||
memset(&s->rc4, 0, sizeof(s->rc4));
|
||||
if (RAND_bytes(rand_buf, (int)sizeof(rand_buf)) <= 0)
|
||||
fatal_exit("Couldn't obtain random bytes (error %ld)",
|
||||
ERR_get_error());
|
||||
RC4_set_key(&s->rc4, (int)sizeof(rand_buf), rand_buf);
|
||||
memset(rand_buf, 0xc, sizeof(rand_buf));
|
||||
if (from) {
|
||||
for(i=0; i<SEED_SIZE; i++)
|
||||
rand_buf[i] = (unsigned char)ub_random(from);
|
||||
} else {
|
||||
if (RAND_bytes(rand_buf, (int)sizeof(rand_buf)) <= 0)
|
||||
fatal_exit("Couldn't obtain random bytes (error %ld)",
|
||||
ERR_get_error());
|
||||
}
|
||||
RC4_set_key(&s->rc4, SEED_SIZE, rand_buf);
|
||||
|
||||
/*
|
||||
* Discard early keystream, as per recommendations in:
|
||||
@@ -91,44 +97,55 @@ ub_arc4random_stir(struct ub_hiddenstate* s)
|
||||
s->rc4_ready = REKEY_BYTES;
|
||||
}
|
||||
|
||||
int
|
||||
ub_initstate(unsigned int ATTR_UNUSED(seed), struct ub_randstate* state,
|
||||
unsigned long ATTR_UNUSED(n))
|
||||
struct ub_randstate*
|
||||
ub_initstate(unsigned int seed, struct ub_randstate* from)
|
||||
{
|
||||
state->s = calloc(1, sizeof(*state->s));
|
||||
if(!state->s) {
|
||||
struct ub_randstate* s = (struct ub_randstate*)calloc(1, sizeof(*s));
|
||||
if(!s) {
|
||||
log_err("malloc failure in random init");
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* RAND_ is threadsafe, by the way */
|
||||
if(!RAND_status()) {
|
||||
log_err("Random generator has no entropy (error %ld)",
|
||||
ERR_get_error());
|
||||
return 0;
|
||||
/* try to seed it */
|
||||
unsigned char buf[256];
|
||||
unsigned int v = seed;
|
||||
size_t i;
|
||||
for(i=0; i<256/sizeof(seed); i++) {
|
||||
memmove(buf+i*sizeof(seed), &v, sizeof(seed));
|
||||
v = v*seed + (unsigned int)i;
|
||||
}
|
||||
RAND_seed(buf, 256);
|
||||
if(!RAND_status()) {
|
||||
log_err("Random generator has no entropy (error %ld)",
|
||||
ERR_get_error());
|
||||
return NULL;
|
||||
}
|
||||
verbose(VERB_OPS, "openssl has no entropy, seeding with time");
|
||||
}
|
||||
ub_arc4random_stir(state->s);
|
||||
return 1;
|
||||
ub_arc4random_stir(s, from);
|
||||
return s;
|
||||
}
|
||||
|
||||
long int
|
||||
ub_random(struct ub_randstate* state)
|
||||
ub_random(struct ub_randstate* s)
|
||||
{
|
||||
unsigned int r = 0;
|
||||
if (state->s->rc4_ready <= 0) {
|
||||
ub_arc4random_stir(state->s);
|
||||
if (s->rc4_ready <= 0) {
|
||||
ub_arc4random_stir(s, NULL);
|
||||
}
|
||||
|
||||
RC4(&state->s->rc4, sizeof(r),
|
||||
RC4(&s->rc4, sizeof(r),
|
||||
(unsigned char *)&r, (unsigned char *)&r);
|
||||
state->s->rc4_ready -= sizeof(r);
|
||||
s->rc4_ready -= sizeof(r);
|
||||
return (long int)((r) % (((unsigned)RAND_MAX + 1)));
|
||||
}
|
||||
|
||||
void
|
||||
ub_randfree(struct ub_randstate* state)
|
||||
ub_randfree(struct ub_randstate* s)
|
||||
{
|
||||
if(state)
|
||||
free(state->s);
|
||||
RAND_cleanup();
|
||||
if(s)
|
||||
free(s);
|
||||
/* user app must do RAND_cleanup(); */
|
||||
}
|
||||
|
||||
+7
-12
@@ -42,27 +42,22 @@
|
||||
* initialisation routine.
|
||||
*/
|
||||
|
||||
struct ub_hiddenstate;
|
||||
|
||||
/**
|
||||
* random state structure.
|
||||
*/
|
||||
struct ub_randstate {
|
||||
/** state hidden type. */
|
||||
struct ub_hiddenstate* s;
|
||||
};
|
||||
struct ub_randstate;
|
||||
|
||||
/**
|
||||
* Initialize a random generator state for use
|
||||
* @param seed: seed value to create state contents.
|
||||
* (ignored for arc4random).
|
||||
* @param state: struct allocated by caller.
|
||||
* @param n: size of state->state. 8, 32, 64, 128, or 256 bytes.
|
||||
* (ignored for arc4random).
|
||||
* @return false alloc failure.
|
||||
* @param from: if not NULL, the seed is taken from this random structure.
|
||||
* can be used to seed random states via a parent-random-state that
|
||||
* is itself seeded with entropy.
|
||||
* @return new state or NULL alloc failure.
|
||||
*/
|
||||
int ub_initstate(unsigned int seed, struct ub_randstate* state,
|
||||
unsigned long n);
|
||||
struct ub_randstate* ub_initstate(unsigned int seed,
|
||||
struct ub_randstate* from);
|
||||
|
||||
/**
|
||||
* Generate next random number from the state passed along.
|
||||
|
||||
+26
-6
@@ -106,7 +106,7 @@ bin_delete(struct lruhash* table, struct lruhash_bin* bin)
|
||||
while(p) {
|
||||
np = p->overflow_next;
|
||||
d = p->data;
|
||||
(*table->delkeyfunc)(p->key, table->cb_arg, 0);
|
||||
(*table->delkeyfunc)(p->key, table->cb_arg);
|
||||
(*table->deldatafunc)(d, table->cb_arg);
|
||||
p = np;
|
||||
}
|
||||
@@ -144,6 +144,7 @@ bin_split(struct lruhash* table, struct lruhash_bin* newa,
|
||||
}
|
||||
lock_quick_unlock(&newa[i].lock);
|
||||
lock_quick_unlock(&newa[newbit|i].lock);
|
||||
lock_quick_unlock(&table->array[i].lock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +206,9 @@ reclaim_space(struct lruhash* table, struct lruhash_entry** list)
|
||||
*list = d;
|
||||
lock_rw_wrlock(&d->lock);
|
||||
table->space_used -= table->sizefunc(d->key, d->data);
|
||||
if(table->markdelfunc)
|
||||
(*table->markdelfunc)(d->key);
|
||||
lock_rw_unlock(&d->lock);
|
||||
lock_quick_unlock(&bin->lock);
|
||||
}
|
||||
}
|
||||
@@ -245,7 +249,6 @@ table_grow(struct lruhash* table)
|
||||
/* delete the old bins */
|
||||
lock_unprotect(&table->lock, table->array);
|
||||
for(i=0; i<table->size; i++) {
|
||||
lock_quick_unlock(&table->array[i].lock);
|
||||
lock_quick_destroy(&table->array[i].lock);
|
||||
}
|
||||
free(table->array);
|
||||
@@ -303,6 +306,7 @@ lruhash_insert(struct lruhash* table, hashvalue_t hash,
|
||||
log_assert(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
|
||||
log_assert(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
|
||||
log_assert(fptr_whitelist_hash_compfunc(table->compfunc));
|
||||
log_assert(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
|
||||
need_size = table->sizefunc(entry->key, data);
|
||||
if(cb_arg == NULL) cb_arg = table->cb_arg;
|
||||
|
||||
@@ -323,7 +327,7 @@ lruhash_insert(struct lruhash* table, hashvalue_t hash,
|
||||
/* if so: update data - needs a writelock */
|
||||
table->space_used += need_size -
|
||||
(*table->sizefunc)(found->key, found->data);
|
||||
(*table->delkeyfunc)(entry->key, cb_arg, 0);
|
||||
(*table->delkeyfunc)(entry->key, cb_arg);
|
||||
lru_touch(table, found);
|
||||
lock_rw_wrlock(&found->lock);
|
||||
(*table->deldatafunc)(found->data, cb_arg);
|
||||
@@ -341,7 +345,7 @@ lruhash_insert(struct lruhash* table, hashvalue_t hash,
|
||||
while(reclaimlist) {
|
||||
struct lruhash_entry* n = reclaimlist->overflow_next;
|
||||
void* d = reclaimlist->data;
|
||||
(*table->delkeyfunc)(reclaimlist->key, cb_arg, 1);
|
||||
(*table->delkeyfunc)(reclaimlist->key, cb_arg);
|
||||
(*table->deldatafunc)(d, cb_arg);
|
||||
reclaimlist = n;
|
||||
}
|
||||
@@ -379,6 +383,7 @@ lruhash_remove(struct lruhash* table, hashvalue_t hash, void* key)
|
||||
log_assert(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
|
||||
log_assert(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
|
||||
log_assert(fptr_whitelist_hash_compfunc(table->compfunc));
|
||||
log_assert(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
|
||||
|
||||
lock_quick_lock(&table->lock);
|
||||
bin = &table->array[hash & table->size_mask];
|
||||
@@ -395,10 +400,13 @@ lruhash_remove(struct lruhash* table, hashvalue_t hash, void* key)
|
||||
table->space_used -= (*table->sizefunc)(entry->key, entry->data);
|
||||
lock_quick_unlock(&table->lock);
|
||||
lock_rw_wrlock(&entry->lock);
|
||||
if(table->markdelfunc)
|
||||
(*table->markdelfunc)(entry->key);
|
||||
lock_rw_unlock(&entry->lock);
|
||||
lock_quick_unlock(&bin->lock);
|
||||
/* finish removal */
|
||||
d = entry->data;
|
||||
(*table->delkeyfunc)(entry->key, table->cb_arg, 1);
|
||||
(*table->delkeyfunc)(entry->key, table->cb_arg);
|
||||
(*table->deldatafunc)(d, table->cb_arg);
|
||||
}
|
||||
|
||||
@@ -414,7 +422,10 @@ bin_clear(struct lruhash* table, struct lruhash_bin* bin)
|
||||
lock_rw_wrlock(&p->lock);
|
||||
np = p->overflow_next;
|
||||
d = p->data;
|
||||
(*table->delkeyfunc)(p->key, table->cb_arg, 1);
|
||||
if(table->markdelfunc)
|
||||
(*table->markdelfunc)(p->key);
|
||||
lock_rw_unlock(&p->lock);
|
||||
(*table->delkeyfunc)(p->key, table->cb_arg);
|
||||
(*table->deldatafunc)(d, table->cb_arg);
|
||||
p = np;
|
||||
}
|
||||
@@ -430,6 +441,7 @@ lruhash_clear(struct lruhash* table)
|
||||
return;
|
||||
log_assert(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
|
||||
log_assert(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
|
||||
log_assert(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
|
||||
|
||||
lock_quick_lock(&table->lock);
|
||||
for(i=0; i<table->size; i++) {
|
||||
@@ -491,3 +503,11 @@ lruhash_get_mem(struct lruhash* table)
|
||||
s += lock_get_mem(&table->lock);
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_t md)
|
||||
{
|
||||
lock_quick_lock(&table->lock);
|
||||
table->markdelfunc = md;
|
||||
lock_quick_unlock(&table->lock);
|
||||
}
|
||||
|
||||
+14
-7
@@ -130,17 +130,17 @@ typedef size_t (*lruhash_sizefunc_t)(void*, void*);
|
||||
typedef int (*lruhash_compfunc_t)(void*, void*);
|
||||
|
||||
/** old keys are deleted.
|
||||
* If is_locked is set, then the routine must unlock the item before deletion.
|
||||
* If is_locked is not set, then this item is not locked. This allows the
|
||||
* routine to perform operations within the critical region of the lock
|
||||
* of the key. The critical region has been locked before the delete happened.
|
||||
* The RRset type has to revoke its ID number inside the critical region.
|
||||
* This function is called: func(key, userarg, is_locked) */
|
||||
typedef void (*lruhash_delkeyfunc_t)(void*, void*, int);
|
||||
* The RRset type has to revoke its ID number, markdel() is used first.
|
||||
* This function is called: func(key, userarg) */
|
||||
typedef void (*lruhash_delkeyfunc_t)(void*, void*);
|
||||
|
||||
/** old data is deleted. This function is called: func(data, userarg). */
|
||||
typedef void (*lruhash_deldatafunc_t)(void*, void*);
|
||||
|
||||
/** mark a key as pending to be deleted (and not to be used by anyone).
|
||||
* called: func(key) */
|
||||
typedef void (*lruhash_markdelfunc_t)(void*);
|
||||
|
||||
/**
|
||||
* Hash table that keeps LRU list of entries.
|
||||
*/
|
||||
@@ -155,6 +155,8 @@ struct lruhash {
|
||||
lruhash_delkeyfunc_t delkeyfunc;
|
||||
/** how to delete data. */
|
||||
lruhash_deldatafunc_t deldatafunc;
|
||||
/** how to mark a key pending deletion */
|
||||
lruhash_markdelfunc_t markdelfunc;
|
||||
/** user argument for user functions */
|
||||
void* cb_arg;
|
||||
|
||||
@@ -294,6 +296,11 @@ struct lruhash_entry* lruhash_lookup(struct lruhash* table, hashvalue_t hash,
|
||||
*/
|
||||
void lru_touch(struct lruhash* table, struct lruhash_entry* entry);
|
||||
|
||||
/**
|
||||
* Set the markdelfunction (or NULL)
|
||||
*/
|
||||
void lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_t md);
|
||||
|
||||
/************************* Internal functions ************************/
|
||||
/*** these are only exposed for unit tests. ***/
|
||||
|
||||
|
||||
@@ -192,9 +192,8 @@ int test_slabhash_compfunc(void* key1, void* key2)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void test_slabhash_delkey(void* key, void* ATTR_UNUSED(arg), int l)
|
||||
void test_slabhash_delkey(void* key, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
if(l) { lock_rw_unlock(&((struct slabhash_testkey*)key)->entry.lock); }
|
||||
delkey((struct slabhash_testkey*)key);
|
||||
}
|
||||
|
||||
@@ -202,3 +201,11 @@ void test_slabhash_deldata(void* data, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
deldata((struct slabhash_testdata*)data);
|
||||
}
|
||||
|
||||
void slabhash_setmarkdel(struct slabhash* sl, lruhash_markdelfunc_t md)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<sl->size; i++) {
|
||||
lruhash_setmarkdel(sl->array[i], md);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user