mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-08-17 21:25:50 +02:00
Compare commits
45
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31cc7670c6 | ||
|
|
39da393425 | ||
|
|
9d89d05bd1 | ||
|
|
1ea78ab032 | ||
|
|
138aa8eebc | ||
|
|
78ab5f41d7 | ||
|
|
a43a042792 | ||
|
|
696d38318c | ||
|
|
1a38497dbe | ||
|
|
06afa3d5d0 | ||
|
|
aa4918c25b | ||
|
|
1c24c78462 | ||
|
|
b150a499db | ||
|
|
3a1187b2f6 | ||
|
|
a3abab528b | ||
|
|
06af702c20 | ||
|
|
8b4c06230c | ||
|
|
9c52b0a7bd | ||
|
|
3f6f9e5a7c | ||
|
|
0e3a023540 | ||
|
|
78d01c6f48 | ||
|
|
82b4130aae | ||
|
|
04e0a37e53 | ||
|
|
af2f95454a | ||
|
|
1dca586a28 | ||
|
|
e4d39152b3 | ||
|
|
e679f51ed9 | ||
|
|
7ccdfe8f7c | ||
|
|
64f8b35c9e | ||
|
|
5f92286f32 | ||
|
|
f0a6f5130f | ||
|
|
a92f1cf9ac | ||
|
|
7e4700ec5f | ||
|
|
c4d87f4f5d | ||
|
|
84053077eb | ||
|
|
d5de0d10d5 | ||
|
|
618ef6acbb | ||
|
|
a36b64a9e7 | ||
|
|
2534d6b120 | ||
|
|
a87e39c8e6 | ||
|
|
4e33e351a3 | ||
|
|
d6a8a0c37b | ||
|
|
ed1f699789 | ||
|
|
095a1dd291 | ||
|
|
f0eeb50fee |
+18
-3
@@ -27,6 +27,8 @@ doxygen=@doxygen@
|
||||
libtool=@libtool@
|
||||
ldnsdir=@ldnsdir@
|
||||
|
||||
YACC=@YACC@
|
||||
LEX=@LEX@
|
||||
CC=@CC@
|
||||
CPPFLAGS=-I. @CPPFLAGS@ @DEFS@
|
||||
CFLAGS=-I. @CFLAGS@
|
||||
@@ -48,14 +50,14 @@ LINTFLAGS+="-DBN_ULONG=unsigned long" -Dkrb5_int32=int "-Dkrb5_ui_4=unsigned int
|
||||
|
||||
INSTALL=$(srcdir)/install-sh
|
||||
|
||||
COMMON_SRC=$(wildcard services/*.c util/*.c)
|
||||
COMMON_SRC=$(wildcard services/*.c util/*.c) util/configparser.c util/configlexer.c testcode/checklocks.c
|
||||
COMMON_OBJ=$(addprefix $(BUILD),$(COMMON_SRC:.c=.o))
|
||||
COMPAT_OBJ=$(addprefix $(BUILD)compat/,$(LIBOBJS))
|
||||
UNITTEST_SRC=testcode/unitmain.c $(COMMON_SRC)
|
||||
UNITTEST_OBJ=$(addprefix $(BUILD),$(UNITTEST_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
DAEMON_SRC=$(wildcard daemon/*.c) $(COMMON_SRC)
|
||||
DAEMON_OBJ=$(addprefix $(BUILD),$(DAEMON_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
TESTBOUND_SRC=testcode/testbound.c testcode/ldns-testpkts.c daemon/worker.c testcode/replay.c testcode/fake_event.c $(filter-out util/netevent.c services/listen_dnsport.c services/outside_network.c, $(COMMON_SRC))
|
||||
TESTBOUND_SRC=testcode/testbound.c testcode/ldns-testpkts.c daemon/worker.c daemon/daemon.c 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)
|
||||
ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) $(TESTBOUND_SRC)
|
||||
ALL_OBJ=$(addprefix $(BUILD),$(ALL_SRC:.c=.o) $(addprefix compat/,$(LIBOBJS))) $(COMPAT_OBJ)
|
||||
@@ -90,6 +92,18 @@ testcode/ldns-testpkts.c: $(ldnsdir)/examples/ldns-testpkts.c \
|
||||
cp $(ldnsdir)/examples/ldns-testpkts.c testcode/ldns-testpkts.c
|
||||
cp $(ldnsdir)/examples/ldns-testpkts.h testcode/ldns-testpkts.h
|
||||
|
||||
util/config_file.c: util/configparser.h
|
||||
util/configlexer.c: $(srcdir)/util/configlexer.lex util/configparser.h
|
||||
$(INFO) Lex $<
|
||||
@if test ! -d util; then $(INSTALL) -d util; fi
|
||||
$Qecho "#include \"util/configyyrename.h\"" > $@
|
||||
$Q$(LEX) -i -t $< >> $@
|
||||
|
||||
util/configparser.c util/configparser.h: $(srcdir)/util/configparser.y
|
||||
$(INFO) Yacc $<
|
||||
@if test ! -d util; then $(INSTALL) -d util; fi
|
||||
$Q$(YACC) -d -o util/configparser.c $<
|
||||
|
||||
clean:
|
||||
rm -f *.o *.d *.lo *~ tags
|
||||
rm -rf autom4te.cache .libs build doc/html
|
||||
@@ -97,10 +111,11 @@ clean:
|
||||
realclean: clean
|
||||
rm -f config.status config.log config.h.in config.h
|
||||
rm -f configure config.sub config.guess ltmain.sh aclocal.m4 libtool
|
||||
rm -f util/configlexer.c util/configparser.c util/configparser.h
|
||||
rm -f Makefile
|
||||
|
||||
lint:
|
||||
$Qfor i in $(sort $(ALL_SRC)); do \
|
||||
$Qfor i in $(filter-out util/configparser.c,$(filter-out util/configlexer.c,$(sort $(ALL_SRC)))); do \
|
||||
echo lint $$i; \
|
||||
$(LINT) $(LINTFLAGS) -I. -I$(srcdir) -I$(ldnsdir)/include $(srcdir)/$$i ; \
|
||||
if [ $$? -ne 0 ] ; then exit 1 ; fi ; \
|
||||
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
##### http://autoconf-archive.cryp.to/acx_pthread.html
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This macro figures out how to build C programs using POSIX threads.
|
||||
# It sets the PTHREAD_LIBS output variable to the threads library and
|
||||
# linker flags, and the PTHREAD_CFLAGS output variable to any special
|
||||
# C compiler flags that are needed. (The user can also force certain
|
||||
# compiler flags/libs to be tested by setting these environment
|
||||
# variables.)
|
||||
#
|
||||
# Also sets PTHREAD_CC to any special C compiler that is needed for
|
||||
# multi-threaded programs (defaults to the value of CC otherwise).
|
||||
# (This is necessary on AIX to use the special cc_r compiler alias.)
|
||||
#
|
||||
# NOTE: You are assumed to not only compile your program with these
|
||||
# flags, but also link it with them as well. e.g. you should link
|
||||
# with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS
|
||||
# $LIBS
|
||||
#
|
||||
# If you are only building threads programs, you may wish to use
|
||||
# these variables in your default LIBS, CFLAGS, and CC:
|
||||
#
|
||||
# LIBS="$PTHREAD_LIBS $LIBS"
|
||||
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
# CC="$PTHREAD_CC"
|
||||
#
|
||||
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute
|
||||
# constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to
|
||||
# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
|
||||
#
|
||||
# ACTION-IF-FOUND is a list of shell commands to run if a threads
|
||||
# library is found, and ACTION-IF-NOT-FOUND is a list of commands to
|
||||
# run it if it is not found. If ACTION-IF-FOUND is not specified, the
|
||||
# default action will define HAVE_PTHREAD.
|
||||
#
|
||||
# Please let the authors know if this macro fails on any platform, or
|
||||
# if you have any other suggestions or comments. This macro was based
|
||||
# on work by SGJ on autoconf scripts for FFTW (http://www.fftw.org/)
|
||||
# (with help from M. Frigo), as well as ac_pthread and hb_pthread
|
||||
# macros posted by Alejandro Forero Cuervo to the autoconf macro
|
||||
# repository. We are also grateful for the helpful feedback of
|
||||
# numerous users.
|
||||
#
|
||||
# LAST MODIFICATION
|
||||
#
|
||||
# 2006-05-29
|
||||
#
|
||||
# COPYLEFT
|
||||
#
|
||||
# Copyright (c) 2006 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
# 02111-1307, USA.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright
|
||||
# owner gives unlimited permission to copy, distribute and modify the
|
||||
# configure scripts that are the output of Autoconf when processing
|
||||
# the Macro. You need not follow the terms of the GNU General Public
|
||||
# License when using or distributing such scripts, even though
|
||||
# portions of the text of the Macro appear in them. The GNU General
|
||||
# Public License (GPL) does govern all other use of the material that
|
||||
# constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the
|
||||
# Autoconf Macro released by the Autoconf Macro Archive. When you
|
||||
# make and distribute a modified version of the Autoconf Macro, you
|
||||
# may extend this special exception to the GPL to apply to your
|
||||
# modified version as well.
|
||||
|
||||
AC_DEFUN([ACX_PTHREAD], [
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
acx_pthread_ok=no
|
||||
|
||||
# We used to check for pthread.h first, but this fails if pthread.h
|
||||
# requires special compiler flags (e.g. on True64 or Sequent).
|
||||
# It gets checked for in the link test anyway.
|
||||
|
||||
# First of all, check if the user has set any of the PTHREAD_LIBS,
|
||||
# etcetera environment variables, and if threads linking works using
|
||||
# them:
|
||||
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
|
||||
AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
|
||||
AC_MSG_RESULT($acx_pthread_ok)
|
||||
if test x"$acx_pthread_ok" = xno; then
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
fi
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
fi
|
||||
|
||||
# We must check for the threads library under a number of different
|
||||
# names; the ordering is very important because some systems
|
||||
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
|
||||
# libraries is broken (non-POSIX).
|
||||
|
||||
# Create a list of thread flags to try. Items starting with a "-" are
|
||||
# C compiler flags, and other items are library names, except for "none"
|
||||
# which indicates that we try without any flags at all, and "pthread-config"
|
||||
# which is a program returning the flags for the Pth emulation library.
|
||||
|
||||
acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
|
||||
|
||||
# The ordering *is* (sometimes) important. Some notes on the
|
||||
# individual items follow:
|
||||
|
||||
# pthreads: AIX (must check this before -lpthread)
|
||||
# none: in case threads are in libc; should be tried before -Kthread and
|
||||
# other compiler flags to prevent continual compiler warnings
|
||||
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
|
||||
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
|
||||
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
|
||||
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
|
||||
# -pthreads: Solaris/gcc
|
||||
# -mthreads: Mingw32/gcc, Lynx/gcc
|
||||
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
|
||||
# doesn't hurt to check since this sometimes defines pthreads too;
|
||||
# also defines -D_REENTRANT)
|
||||
# ... -mt is also the pthreads flag for HP/aCC
|
||||
# pthread: Linux, etcetera
|
||||
# --thread-safe: KAI C++
|
||||
# pthread-config: use pthread-config program (for GNU Pth library)
|
||||
|
||||
case "${host_cpu}-${host_os}" in
|
||||
*solaris*)
|
||||
|
||||
# On Solaris (at least, for some versions), libc contains stubbed
|
||||
# (non-functional) versions of the pthreads routines, so link-based
|
||||
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
|
||||
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
|
||||
# a function called by this macro, so we could check for that, but
|
||||
# who knows whether they'll stub that too in a future libc.) So,
|
||||
# we'll just look for -pthreads and -lpthread first:
|
||||
|
||||
acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
|
||||
;;
|
||||
esac
|
||||
|
||||
if test x"$acx_pthread_ok" = xno; then
|
||||
for flag in $acx_pthread_flags; do
|
||||
|
||||
case $flag in
|
||||
none)
|
||||
AC_MSG_CHECKING([whether pthreads work without any flags])
|
||||
;;
|
||||
|
||||
-*)
|
||||
AC_MSG_CHECKING([whether pthreads work with $flag])
|
||||
PTHREAD_CFLAGS="$flag"
|
||||
;;
|
||||
|
||||
pthread-config)
|
||||
AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
|
||||
if test x"$acx_pthread_config" = xno; then continue; fi
|
||||
PTHREAD_CFLAGS="`pthread-config --cflags`"
|
||||
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
|
||||
;;
|
||||
|
||||
*)
|
||||
AC_MSG_CHECKING([for the pthreads library -l$flag])
|
||||
PTHREAD_LIBS="-l$flag"
|
||||
;;
|
||||
esac
|
||||
|
||||
save_LIBS="$LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# Check for various functions. We must include pthread.h,
|
||||
# since some functions may be macros. (On the Sequent, we
|
||||
# need a special flag -Kthread to make this header compile.)
|
||||
# We check for pthread_join because it is in -lpthread on IRIX
|
||||
# while pthread_create is in libc. We check for pthread_attr_init
|
||||
# due to DEC craziness with -lpthreads. We check for
|
||||
# pthread_cleanup_push because it is one of the few pthread
|
||||
# functions on Solaris that doesn't have a non-functional libc stub.
|
||||
# We try pthread_create on general principles.
|
||||
AC_TRY_LINK([#include <pthread.h>],
|
||||
[pthread_t th; pthread_join(th, 0);
|
||||
pthread_attr_init(0); pthread_cleanup_push(0, 0);
|
||||
pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
|
||||
[acx_pthread_ok=yes])
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
AC_MSG_RESULT($acx_pthread_ok)
|
||||
if test "x$acx_pthread_ok" = xyes; then
|
||||
break;
|
||||
fi
|
||||
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
done
|
||||
fi
|
||||
|
||||
# Various other checks:
|
||||
if test "x$acx_pthread_ok" = xyes; then
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
|
||||
AC_MSG_CHECKING([for joinable pthread attribute])
|
||||
attr_name=unknown
|
||||
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
|
||||
AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
|
||||
[attr_name=$attr; break])
|
||||
done
|
||||
AC_MSG_RESULT($attr_name)
|
||||
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
|
||||
AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
|
||||
[Define to necessary symbol if this constant
|
||||
uses a non-standard name on your system.])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if more special flags are required for pthreads])
|
||||
flag=no
|
||||
case "${host_cpu}-${host_os}" in
|
||||
*-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
|
||||
*solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
|
||||
esac
|
||||
AC_MSG_RESULT(${flag})
|
||||
if test "x$flag" != xno; then
|
||||
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
|
||||
fi
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
# More AIX lossage: must compile with xlc_r or cc_r
|
||||
if test x"$GCC" != xyes; then
|
||||
AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
|
||||
else
|
||||
PTHREAD_CC=$CC
|
||||
fi
|
||||
else
|
||||
PTHREAD_CC="$CC"
|
||||
fi
|
||||
|
||||
AC_SUBST(PTHREAD_LIBS)
|
||||
AC_SUBST(PTHREAD_CFLAGS)
|
||||
AC_SUBST(PTHREAD_CC)
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test x"$acx_pthread_ok" = xyes; then
|
||||
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
|
||||
:
|
||||
else
|
||||
acx_pthread_ok=no
|
||||
$2
|
||||
fi
|
||||
AC_LANG_RESTORE
|
||||
])dnl ACX_PTHREAD
|
||||
+53
-7
@@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
AC_PREREQ(2.57)
|
||||
|
||||
AC_INIT(unbound, 0.0, wouter@nlnetlabs.nl, unbound)
|
||||
AC_INIT(unbound, 0.1, wouter@nlnetlabs.nl, unbound)
|
||||
|
||||
CFLAGS=
|
||||
AC_AIX
|
||||
@@ -263,6 +263,8 @@ AC_CANONICAL_HOST
|
||||
if echo "$host_os" | grep "sunos4" >/dev/null; then
|
||||
lt_cv_sys_max_cmd_len=32750;
|
||||
fi
|
||||
AC_PROG_LEX
|
||||
AC_PROG_YACC
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
# Checks for header files.
|
||||
@@ -348,12 +350,46 @@ AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=pathname],
|
||||
fi
|
||||
AC_CHECK_HEADERS([openssl/ssl.h],,, [AC_INCLUDES_DEFAULT])
|
||||
|
||||
# check for thread library.
|
||||
AC_ARG_WITH(pthreads, AC_HELP_STRING([--with-pthreads],
|
||||
[use pthreads library, or --without--pthreads to disable threading support.]),
|
||||
[ ],[ withval="yes" ])
|
||||
ub_have_pthreads=no
|
||||
if test x_$withval != x_no; then
|
||||
sinclude(acx_pthread.m4)
|
||||
ACX_PTHREAD([
|
||||
AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.])
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
CC="$PTHREAD_CC"
|
||||
ub_have_pthreads=yes
|
||||
AC_CHECK_TYPES(pthread_spinlock_t,,,[#include <pthread.h>])
|
||||
])
|
||||
fi
|
||||
|
||||
# check solaris thread library
|
||||
AC_ARG_WITH(solaris-threads, AC_HELP_STRING([--with-solaris-threads],
|
||||
[use solaris native thread library.]), [ ],[ withval="no" ])
|
||||
if test x_$withval != x_no; then
|
||||
if test x_$ub_have_pthreads != x_no; then
|
||||
AC_WARN([Have pthreads already, ignoring --with-solaris-threads])
|
||||
else
|
||||
AC_SEARCH_LIBS(thr_create, [thread],
|
||||
[
|
||||
AC_DEFINE(HAVE_SOLARIS_THREADS, 1, [Using Solaris threads])
|
||||
|
||||
CHECK_COMPILER_FLAG(mt, [CFLAGS="$CFLAGS -mt"],
|
||||
[CFLAGS="$CFLAGS -D_REENTRANT"])
|
||||
] , [
|
||||
AC_ERROR([no solaris threads found.])
|
||||
])
|
||||
fi
|
||||
fi
|
||||
|
||||
# check for libevent
|
||||
AC_ARG_WITH(libevent, AC_HELP_STRING([--with-libevent=pathname],
|
||||
[set path to libevent (will check /usr/local /usr/lib /usr/pkg /usr/sfw /usr)]),[
|
||||
],[
|
||||
withval="yes"
|
||||
])
|
||||
[set path to libevent (will check /usr/local /usr/lib /usr/pkg /usr/sfw /usr)]),
|
||||
[ ],[ withval="yes" ])
|
||||
if test x_$withval != x_no; then
|
||||
AC_MSG_CHECKING(for libevent)
|
||||
if test x_$withval = x_ -o x_$withval = x_yes; then
|
||||
@@ -385,6 +421,7 @@ if test x_$withval != x_no; then
|
||||
AC_SEARCH_LIBS(event_set, [event])
|
||||
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
|
||||
else
|
||||
AC_DEFINE(USE_MINI_EVENT, 1, [Define if you want to use internal select based events])
|
||||
fi
|
||||
@@ -442,6 +479,8 @@ AH_BOTTOM([
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#if HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
@@ -481,6 +520,10 @@ AH_BOTTOM([
|
||||
#define fseeko fseek
|
||||
#define ftello ftell
|
||||
#endif /* HAVE_FSEEKO */
|
||||
#if defined(HAVE_EVENT_H) && !defined(HAVE_EVENT_BASE_ONCE) && (defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS))
|
||||
/* using version of libevent that is not threadsafe. */
|
||||
# define LIBEVENT_SIGNAL_PROBLEM 1
|
||||
#endif
|
||||
|
||||
#ifndef RAND_MAX
|
||||
#define RAND_MAX 2147483647
|
||||
@@ -499,6 +542,9 @@ AH_BOTTOM([
|
||||
# endif
|
||||
#endif /* CHECKED_INET6 */
|
||||
|
||||
/* maximum nesting of included files */
|
||||
#define MAXINCLUDES 10
|
||||
|
||||
#ifndef HAVE_SNPRINTF
|
||||
#include <stdarg.h>
|
||||
int snprintf (char *str, size_t count, const char *fmt, ...);
|
||||
@@ -526,8 +572,8 @@ struct sockaddr_storage;
|
||||
|
||||
#include "ldns/ldns.h"
|
||||
|
||||
/** default port to listen for queries, passed to getaddrinfo */
|
||||
#define UNBOUND_DNS_PORT "53"
|
||||
/** default port for DNS traffic. */
|
||||
#define UNBOUND_DNS_PORT 53
|
||||
])
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* daemon/daemon.c - collection of workers that handles requests.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* The daemon consists of global settings and a number of workers.
|
||||
*/
|
||||
|
||||
/** buffer size for network connections */
|
||||
#define BUFSZ 65552
|
||||
|
||||
#include "config.h"
|
||||
#include "daemon/daemon.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "services/listen_dnsport.h"
|
||||
#include <signal.h>
|
||||
|
||||
/** How many quit requests happened. */
|
||||
static int sig_record_quit = 0;
|
||||
/** How many reload requests happened. */
|
||||
static int sig_record_reload = 0;
|
||||
|
||||
/** used when no other sighandling happens, so we don't die
|
||||
* when multiple signals in quick succession are sent to us. */
|
||||
static RETSIGTYPE record_sigh(int sig)
|
||||
{
|
||||
#ifdef LIBEVENT_SIGNAL_PROBLEM
|
||||
exit(0);
|
||||
#endif
|
||||
switch(sig)
|
||||
{
|
||||
case SIGTERM:
|
||||
case SIGQUIT:
|
||||
case SIGINT:
|
||||
sig_record_quit++;
|
||||
break;
|
||||
case SIGHUP:
|
||||
sig_record_reload++;
|
||||
break;
|
||||
default:
|
||||
log_err("ignoring signal %d", sig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal handling during the time when netevent is disabled.
|
||||
* Stores signals to replay later.
|
||||
*/
|
||||
static void
|
||||
signal_handling_record()
|
||||
{
|
||||
if( signal(SIGTERM, record_sigh) == SIG_ERR ||
|
||||
signal(SIGQUIT, record_sigh) == SIG_ERR ||
|
||||
signal(SIGINT, record_sigh) == SIG_ERR ||
|
||||
signal(SIGHUP, record_sigh) == SIG_ERR)
|
||||
log_err("install sighandler: %s", strerror(errno));
|
||||
}
|
||||
|
||||
/**
|
||||
* Replay old signals.
|
||||
* @param wrk: worker that handles signals.
|
||||
*/
|
||||
static void
|
||||
signal_handling_playback(struct worker* wrk)
|
||||
{
|
||||
if(sig_record_quit)
|
||||
worker_sighandler(SIGTERM, wrk);
|
||||
if(sig_record_reload)
|
||||
worker_sighandler(SIGHUP, wrk);
|
||||
sig_record_quit = 0;
|
||||
sig_record_reload = 0;
|
||||
}
|
||||
|
||||
struct daemon*
|
||||
daemon_init()
|
||||
{
|
||||
struct daemon* daemon = (struct daemon*)calloc(1,
|
||||
sizeof(struct daemon));
|
||||
if(!daemon)
|
||||
return NULL;
|
||||
signal_handling_record();
|
||||
checklock_start();
|
||||
daemon->need_to_exit = 0;
|
||||
alloc_init(&daemon->superalloc, NULL);
|
||||
return daemon;
|
||||
}
|
||||
|
||||
int
|
||||
daemon_open_shared_ports(struct daemon* daemon)
|
||||
{
|
||||
log_assert(daemon);
|
||||
if(daemon->cfg->port == daemon->listening_port)
|
||||
return 1;
|
||||
listening_ports_free(daemon->ports);
|
||||
if(!(daemon->ports=listening_ports_open(daemon->cfg)))
|
||||
return 0;
|
||||
daemon->listening_port = daemon->cfg->port;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate empty worker structures. With backptr and thread-number,
|
||||
* from 0..numthread initialised. Used as user arguments to new threads.
|
||||
* @param daemon: the daemon with (new) config settings.
|
||||
*/
|
||||
static void
|
||||
daemon_create_workers(struct daemon* daemon)
|
||||
{
|
||||
int i;
|
||||
log_assert(daemon && daemon->cfg);
|
||||
daemon->num = daemon->cfg->num_threads;
|
||||
daemon->workers = (struct worker**)calloc((size_t)daemon->num,
|
||||
sizeof(struct worker*));
|
||||
for(i=0; i<daemon->num; i++) {
|
||||
if(!(daemon->workers[i] = worker_create(daemon, i)))
|
||||
fatal_exit("malloc failure");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all pipes except for the numbered thread.
|
||||
* @param daemon: daemon to close pipes in.
|
||||
* @param thr: thread number 0..num-1 of thread to skip.
|
||||
*/
|
||||
void close_other_pipes(struct daemon* daemon, int thr)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<daemon->num; i++)
|
||||
if(i!=thr) {
|
||||
if(daemon->workers[i]->cmd_send_fd != -1) {
|
||||
close(daemon->workers[i]->cmd_send_fd);
|
||||
daemon->workers[i]->cmd_send_fd = -1;
|
||||
}
|
||||
if(daemon->workers[i]->cmd_recv_fd != -1) {
|
||||
close(daemon->workers[i]->cmd_recv_fd);
|
||||
daemon->workers[i]->cmd_recv_fd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to start one thread.
|
||||
* @param arg: user argument.
|
||||
* @return: void* user return value could be used for thread_join results.
|
||||
*/
|
||||
static void*
|
||||
thread_start(void* arg)
|
||||
{
|
||||
struct worker* worker = (struct worker*)arg;
|
||||
int num = worker->thread_num;
|
||||
log_thread_set(&worker->thread_num);
|
||||
ub_thread_blocksigs();
|
||||
#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
|
||||
/* close pipe ends used by main */
|
||||
close(worker->cmd_send_fd);
|
||||
worker->cmd_send_fd = -1;
|
||||
close_other_pipes(worker->daemon, worker->thread_num);
|
||||
#endif /* no threads */
|
||||
if(!worker_init(worker, worker->daemon->cfg, worker->daemon->ports,
|
||||
BUFSZ, 0))
|
||||
fatal_exit("Could not initialize thread #%d", num);
|
||||
|
||||
worker_work(worker);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fork and init the other threads. Main thread returns for special handling.
|
||||
* @param daemon: the daemon with other threads to fork.
|
||||
*/
|
||||
static void
|
||||
daemon_start_others(struct daemon* daemon)
|
||||
{
|
||||
int i;
|
||||
log_assert(daemon);
|
||||
verbose(VERB_ALGO, "start threads");
|
||||
/* skip i=0, is this thread */
|
||||
for(i=1; i<daemon->num; i++) {
|
||||
ub_thread_create(&daemon->workers[i]->thr_id,
|
||||
thread_start, daemon->workers[i]);
|
||||
#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
|
||||
/* close pipe end of child */
|
||||
close(daemon->workers[i]->cmd_recv_fd);
|
||||
daemon->workers[i]->cmd_recv_fd = -1;
|
||||
#endif /* no threads */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the other threads.
|
||||
* @param daemon: the daemon with other threads.
|
||||
*/
|
||||
static void
|
||||
daemon_stop_others(struct daemon* daemon)
|
||||
{
|
||||
int i;
|
||||
log_assert(daemon);
|
||||
verbose(VERB_ALGO, "stop threads");
|
||||
/* skip i=0, is this thread */
|
||||
/* use i=0 buffer for sending cmds; because we are #0 */
|
||||
for(i=1; i<daemon->num; i++) {
|
||||
worker_send_cmd(daemon->workers[i],
|
||||
daemon->workers[0]->front->udp_buff, worker_cmd_quit);
|
||||
}
|
||||
/** wait for them to quit */
|
||||
for(i=1; i<daemon->num; i++) {
|
||||
/* join it to make sure its dead */
|
||||
verbose(VERB_ALGO, "join %d", i);
|
||||
ub_thread_join(daemon->workers[i]->thr_id);
|
||||
verbose(VERB_ALGO, "join success %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
daemon_fork(struct daemon* daemon)
|
||||
{
|
||||
log_assert(daemon);
|
||||
/* first create all the worker structures, so we can pass
|
||||
* them to the newly created threads.
|
||||
*/
|
||||
daemon_create_workers(daemon);
|
||||
|
||||
/* Now create the threads and init the workers.
|
||||
* By the way, this is thread #0 (the main thread).
|
||||
*/
|
||||
daemon_start_others(daemon);
|
||||
|
||||
/* Special handling for the main thread. This is the thread
|
||||
* that handles signals.
|
||||
*/
|
||||
if(!worker_init(daemon->workers[0], daemon->cfg, daemon->ports,
|
||||
BUFSZ, 1))
|
||||
fatal_exit("Could not initialize main thread");
|
||||
signal_handling_playback(daemon->workers[0]);
|
||||
|
||||
/* Start resolver service on main thread. */
|
||||
log_info("start of service (%s).", PACKAGE_STRING);
|
||||
worker_work(daemon->workers[0]);
|
||||
|
||||
/* we exited! a signal happened! Stop other threads */
|
||||
daemon_stop_others(daemon);
|
||||
|
||||
if(daemon->workers[0]->need_to_restart)
|
||||
daemon->need_to_exit = 0;
|
||||
else daemon->need_to_exit = 1;
|
||||
}
|
||||
|
||||
void
|
||||
daemon_cleanup(struct daemon* daemon)
|
||||
{
|
||||
int i;
|
||||
log_assert(daemon);
|
||||
/* before stopping main worker, handle signals ourselves, so we
|
||||
don't die on multiple reload signals for example. */
|
||||
signal_handling_record();
|
||||
log_thread_set(NULL);
|
||||
for(i=0; i<daemon->num; i++)
|
||||
worker_delete(daemon->workers[i]);
|
||||
free(daemon->workers);
|
||||
daemon->workers = NULL;
|
||||
daemon->num = 0;
|
||||
daemon->cfg = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
daemon_delete(struct daemon* daemon)
|
||||
{
|
||||
if(!daemon)
|
||||
return;
|
||||
listening_ports_free(daemon->ports);
|
||||
alloc_clear(&daemon->superalloc);
|
||||
free(daemon->cwd);
|
||||
free(daemon->pidfile);
|
||||
free(daemon);
|
||||
checklock_stop();
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* daemon/daemon.h - collection of workers that handles requests.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* The daemon consists of global settings and a number of workers.
|
||||
*/
|
||||
|
||||
#ifndef DAEMON_H
|
||||
#define DAEMON_H
|
||||
|
||||
#include "util/locks.h"
|
||||
#include "util/alloc.h"
|
||||
struct config_file;
|
||||
struct worker;
|
||||
struct listen_port;
|
||||
|
||||
/**
|
||||
* Structure holding worker list.
|
||||
* Holds globally visible information.
|
||||
*/
|
||||
struct daemon {
|
||||
/** The config settings */
|
||||
struct config_file* cfg;
|
||||
/** current working directory */
|
||||
char* cwd;
|
||||
/** pidfile that is used */
|
||||
char* pidfile;
|
||||
/** port number that has ports opened. */
|
||||
int listening_port;
|
||||
/** listening ports, opened, to be shared by threads */
|
||||
struct listen_port* ports;
|
||||
/** num threads allocated */
|
||||
int num;
|
||||
/** the worker entries */
|
||||
struct worker** workers;
|
||||
/** do we need to exit unbound (or is it only a reload?) */
|
||||
int need_to_exit;
|
||||
/** master allocation cache */
|
||||
struct alloc_cache superalloc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize daemon structure.
|
||||
* @return: The daemon structure, or NULL on error.
|
||||
*/
|
||||
struct daemon* daemon_init();
|
||||
|
||||
/**
|
||||
* Open shared listening ports (if needed).
|
||||
* The cfg member pointer must have been set for the daemon.
|
||||
* @param daemon: the daemon.
|
||||
* @return: false on error.
|
||||
*/
|
||||
int daemon_open_shared_ports(struct daemon* daemon);
|
||||
|
||||
/**
|
||||
* Fork workers and start service.
|
||||
* When the routine exits, it is no longer forked.
|
||||
* @param daemon: the daemon.
|
||||
*/
|
||||
void daemon_fork(struct daemon* daemon);
|
||||
|
||||
/**
|
||||
* Close off the worker thread information.
|
||||
* Bring the daemon back into state ready for daemon_fork again.
|
||||
* @param daemon: the daemon.
|
||||
*/
|
||||
void daemon_cleanup(struct daemon* daemon);
|
||||
|
||||
/**
|
||||
* Delete workers, close listening ports.
|
||||
* @param daemon: the daemon.
|
||||
*/
|
||||
void daemon_delete(struct daemon* daemon);
|
||||
|
||||
#endif /* DAEMON_H */
|
||||
+221
-48
@@ -42,10 +42,11 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "daemon/worker.h"
|
||||
|
||||
/** buffer size for network connections */
|
||||
#define BUFSZ 65552
|
||||
#include "daemon/daemon.h"
|
||||
#include "util/config_file.h"
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
|
||||
/** print usage. */
|
||||
static void usage()
|
||||
@@ -53,15 +54,215 @@ static void usage()
|
||||
printf("usage: unbound [options]\n");
|
||||
printf(" start unbound daemon DNS resolver.\n");
|
||||
printf("-h this help\n");
|
||||
printf("-p port the port to listen on\n");
|
||||
printf("-c file config file to read, unbound.conf(5).\n");
|
||||
printf("-d do not fork into the background.\n");
|
||||
printf("-v verbose (multiple times increase verbosity)\n");
|
||||
printf("-f ip set forwarder address\n");
|
||||
printf("-z port set forwarder port\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);
|
||||
}
|
||||
|
||||
/** to changedir, logfile */
|
||||
static void
|
||||
apply_dir(struct daemon* daemon, struct config_file* cfg, int cmdline_verbose)
|
||||
{
|
||||
/* apply changes if they have changed */
|
||||
daemon->cfg = cfg;
|
||||
verbosity = cmdline_verbose + cfg->verbosity;
|
||||
if(cfg->directory && cfg->directory[0]) {
|
||||
if(!daemon->cwd || strcmp(daemon->cwd, cfg->directory) != 0) {
|
||||
if(chdir(cfg->directory)) {
|
||||
log_err("Could not chdir to %s: %s",
|
||||
cfg->directory, strerror(errno));
|
||||
}
|
||||
free(daemon->cwd);
|
||||
if(!(daemon->cwd = strdup(cfg->directory)))
|
||||
fatal_exit("malloc failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Read existing pid from pidfile. */
|
||||
static pid_t
|
||||
readpid (const char* file)
|
||||
{
|
||||
int fd;
|
||||
pid_t pid;
|
||||
char pidbuf[32];
|
||||
char* t;
|
||||
ssize_t l;
|
||||
|
||||
if ((fd = open(file, O_RDONLY)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (((l = read(fd, pidbuf, sizeof(pidbuf)))) == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
/* Empty pidfile means no pidfile... */
|
||||
if (l == 0) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid = strtol(pidbuf, &t, 10);
|
||||
|
||||
if (*t && *t != '\n') {
|
||||
return -1;
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
/** write pid to file. */
|
||||
static void
|
||||
writepid (const char* pidfile, pid_t pid)
|
||||
{
|
||||
FILE* f;
|
||||
|
||||
if ((f = fopen(pidfile, "w")) == NULL ) {
|
||||
log_err("cannot open pidfile %s: %s",
|
||||
pidfile, strerror(errno));
|
||||
return;
|
||||
}
|
||||
if(fprintf(f, "%lu\n", (unsigned long)pid) < 0) {
|
||||
log_err("cannot write to pidfile %s: %s",
|
||||
pidfile, strerror(errno));
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/**
|
||||
* check old pid file.
|
||||
* @param cfg: the config settings
|
||||
*/
|
||||
static void
|
||||
checkoldpid(struct config_file* cfg)
|
||||
{
|
||||
pid_t old;
|
||||
if((old = readpid(cfg->pidfile)) == -1) {
|
||||
if(errno != ENOENT) {
|
||||
log_err("Could not read pidfile %s: %s",
|
||||
cfg->pidfile, strerror(errno));
|
||||
}
|
||||
} else {
|
||||
/** see if it is still alive */
|
||||
if(kill(old, 0) == 0 || errno == EPERM)
|
||||
log_warn("unbound is already running as pid %u.",
|
||||
(unsigned)old);
|
||||
else log_warn("did not exit gracefully last time (%u)",
|
||||
(unsigned)old);
|
||||
}
|
||||
}
|
||||
|
||||
/** daemonize, drop user priviliges and chroot if needed */
|
||||
static void
|
||||
do_chroot(struct daemon* daemon, struct config_file* cfg, int debug_mode)
|
||||
{
|
||||
log_assert(cfg);
|
||||
|
||||
/* daemonize last to be able to print error to user */
|
||||
if(cfg->chrootdir && cfg->chrootdir[0])
|
||||
if(chroot(cfg->chrootdir))
|
||||
fatal_exit("unable to chroot: %s", strerror(errno));
|
||||
if(cfg->username && cfg->username[0]) {
|
||||
struct passwd *pwd;
|
||||
if((pwd = getpwnam(cfg->username)) == NULL)
|
||||
fatal_exit("user '%s' does not exist.", cfg->username);
|
||||
if(setgid(pwd->pw_gid) != 0)
|
||||
fatal_exit("unable to set group id: %s", strerror(errno));
|
||||
if(setuid(pwd->pw_uid) != 0)
|
||||
fatal_exit("unable to set user id: %s", strerror(errno));
|
||||
endpwent();
|
||||
}
|
||||
/* check old pid file before forking */
|
||||
if(cfg->pidfile && cfg->pidfile[0]) {
|
||||
checkoldpid(cfg);
|
||||
}
|
||||
|
||||
/* init logfile just before fork */
|
||||
log_init(cfg->logfile);
|
||||
if(!debug_mode && cfg->do_daemonize) {
|
||||
int fd;
|
||||
/* Take off... */
|
||||
switch (fork()) {
|
||||
case 0:
|
||||
break;
|
||||
case -1:
|
||||
unlink(cfg->pidfile);
|
||||
fatal_exit("fork failed: %s", strerror(errno));
|
||||
default:
|
||||
/* exit interactive session */
|
||||
exit(0);
|
||||
}
|
||||
/* detach */
|
||||
if(setsid() == -1)
|
||||
fatal_exit("setsid() failed: %s", strerror(errno));
|
||||
if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
|
||||
(void)dup2(fd, STDIN_FILENO);
|
||||
(void)dup2(fd, STDOUT_FILENO);
|
||||
(void)dup2(fd, STDERR_FILENO);
|
||||
if (fd > 2)
|
||||
(void)close(fd);
|
||||
}
|
||||
}
|
||||
if(cfg->pidfile && cfg->pidfile[0]) {
|
||||
writepid(cfg->pidfile, getpid());
|
||||
daemon->pidfile = strdup(cfg->pidfile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the daemon.
|
||||
* @param cfgfile: the config file name.
|
||||
* @param cmdline_verbose: verbosity resulting from commandline -v.
|
||||
* These increase verbosity as specified in the config file.
|
||||
* @param debug_mode: if set, do not daemonize.
|
||||
*/
|
||||
static void run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode)
|
||||
{
|
||||
struct config_file* cfg = NULL;
|
||||
struct daemon* daemon = NULL;
|
||||
int done_chroot = 0;
|
||||
|
||||
if(!(daemon = daemon_init()))
|
||||
fatal_exit("alloc failure");
|
||||
while(!daemon->need_to_exit) {
|
||||
if(done_chroot)
|
||||
log_info("Restart of %s.", PACKAGE_STRING);
|
||||
else log_info("Start of %s.", PACKAGE_STRING);
|
||||
|
||||
/* config stuff */
|
||||
if(!(cfg = config_create()))
|
||||
fatal_exit("Could not alloc config defaults");
|
||||
if(!config_read(cfg, cfgfile))
|
||||
fatal_exit("Could not read config file: %s", cfgfile);
|
||||
apply_dir(daemon, cfg, cmdline_verbose);
|
||||
|
||||
/* prepare */
|
||||
if(!daemon_open_shared_ports(daemon))
|
||||
fatal_exit("could not open ports");
|
||||
if(!done_chroot) {
|
||||
do_chroot(daemon, cfg, debug_mode);
|
||||
done_chroot = 1;
|
||||
}
|
||||
/* work */
|
||||
daemon_fork(daemon);
|
||||
|
||||
/* clean up for restart */
|
||||
verbose(VERB_ALGO, "cleanup.");
|
||||
daemon_cleanup(daemon);
|
||||
config_delete(cfg);
|
||||
}
|
||||
verbose(VERB_ALGO, "Exit cleanup.");
|
||||
if(daemon->pidfile)
|
||||
unlink(daemon->pidfile);
|
||||
daemon_delete(daemon);
|
||||
}
|
||||
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern int optind;
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
@@ -76,35 +277,24 @@ extern char* optarg;
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
struct worker* worker = NULL;
|
||||
int do_ip4=1, do_ip6=1, do_udp=1, do_tcp=1;
|
||||
size_t numports=3;
|
||||
int baseport=10000;
|
||||
const char* port = UNBOUND_DNS_PORT;
|
||||
int c;
|
||||
const char* fwd = "127.0.0.1";
|
||||
const char* fwdport = UNBOUND_DNS_PORT;
|
||||
const char* cfgfile = NULL;
|
||||
int cmdline_verbose = 0;
|
||||
int debug_mode = 0;
|
||||
|
||||
log_init();
|
||||
log_info("Start of %s.", PACKAGE_STRING);
|
||||
log_init(NULL);
|
||||
/* parse the options */
|
||||
while( (c=getopt(argc, argv, "f:hvp:z:")) != -1) {
|
||||
while( (c=getopt(argc, argv, "c:dhv")) != -1) {
|
||||
switch(c) {
|
||||
case 'f':
|
||||
fwd = optarg;
|
||||
break;
|
||||
case 'z':
|
||||
fwdport = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
if(!atoi(optarg))
|
||||
fatal_exit("invalid port '%s'", optarg);
|
||||
port = optarg;
|
||||
baseport = atoi(optarg)+2000;
|
||||
verbose(VERB_ALGO, "using port: %s", port);
|
||||
case 'c':
|
||||
cfgfile = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
verbosity ++;
|
||||
cmdline_verbose ++;
|
||||
verbosity++;
|
||||
break;
|
||||
case 'd':
|
||||
debug_mode = 1;
|
||||
break;
|
||||
case '?':
|
||||
case 'h':
|
||||
@@ -121,23 +311,6 @@ main(int argc, char* argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* setup */
|
||||
worker = worker_init(port, do_ip4, do_ip6, do_udp, do_tcp, BUFSZ,
|
||||
numports, baseport);
|
||||
if(!worker) {
|
||||
fatal_exit("could not initialize");
|
||||
}
|
||||
if(!worker_set_fwd(worker, fwd, fwdport)) {
|
||||
worker_delete(worker);
|
||||
fatal_exit("could not set forwarder address");
|
||||
}
|
||||
|
||||
/* drop user priviliges and chroot if needed */
|
||||
log_info("start of service (%s).", PACKAGE_STRING);
|
||||
worker_work(worker);
|
||||
|
||||
/* cleanup */
|
||||
verbose(VERB_ALGO, "Exit cleanup.");
|
||||
worker_delete(worker);
|
||||
run_daemon(cfgfile, cmdline_verbose, debug_mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+171
-32
@@ -42,8 +42,11 @@
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/random.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "daemon/daemon.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/config_file.h"
|
||||
#include "services/listen_dnsport.h"
|
||||
#include "services/outside_network.h"
|
||||
|
||||
@@ -51,7 +54,6 @@
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
/** timeout in seconds for UDP queries to auth servers. TODO: proper rtt */
|
||||
@@ -59,6 +61,20 @@
|
||||
/** the size of ID and flags, opcode, rcode in dns packet */
|
||||
#define ID_AND_FLAGS 4
|
||||
|
||||
void
|
||||
worker_send_cmd(struct worker* worker, ldns_buffer* buffer,
|
||||
enum worker_commands cmd)
|
||||
{
|
||||
ldns_buffer_clear(buffer);
|
||||
/* like DNS message, length data */
|
||||
ldns_buffer_write_u16(buffer, sizeof(uint32_t));
|
||||
ldns_buffer_write_u32(buffer, (uint32_t)cmd);
|
||||
ldns_buffer_flip(buffer);
|
||||
if(!write_socket(worker->cmd_send_fd, ldns_buffer_begin(buffer),
|
||||
ldns_buffer_limit(buffer)))
|
||||
log_err("write socket: %s", strerror(errno));
|
||||
}
|
||||
|
||||
/** reply to query with given error code */
|
||||
static void
|
||||
replyerror(int r, struct worker* worker)
|
||||
@@ -66,6 +82,10 @@ replyerror(int r, struct worker* worker)
|
||||
LDNS_QR_SET(ldns_buffer_begin(worker->query_reply.c->buffer));
|
||||
LDNS_RCODE_SET(ldns_buffer_begin(worker->query_reply.c->buffer), r);
|
||||
comm_point_send_reply(&worker->query_reply);
|
||||
if(worker->num_requests == 1) {
|
||||
/* no longer at max, start accepting again. */
|
||||
listen_resume(worker->front);
|
||||
}
|
||||
worker->num_requests --;
|
||||
}
|
||||
|
||||
@@ -75,6 +95,8 @@ worker_handle_reply(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
{
|
||||
struct worker* worker = (struct worker*)arg;
|
||||
verbose(VERB_DETAIL, "reply to query with stored ID %d",
|
||||
worker->query_id);
|
||||
LDNS_ID_SET(ldns_buffer_begin(worker->query_reply.c->buffer),
|
||||
worker->query_id);
|
||||
if(error != 0) {
|
||||
@@ -90,6 +112,10 @@ worker_handle_reply(struct comm_point* c, void* arg, int error,
|
||||
LDNS_QR_SET(ldns_buffer_begin(worker->query_reply.c->buffer));
|
||||
ldns_buffer_flip(worker->query_reply.c->buffer);
|
||||
comm_point_send_reply(&worker->query_reply);
|
||||
if(worker->num_requests == 1) {
|
||||
/* no longer at max, start accepting again. */
|
||||
listen_resume(worker->front);
|
||||
}
|
||||
worker->num_requests --;
|
||||
return 0;
|
||||
}
|
||||
@@ -101,9 +127,10 @@ worker_process_query(struct worker* worker)
|
||||
/* query the forwarding address */
|
||||
worker->query_id = LDNS_ID_WIRE(ldns_buffer_begin(
|
||||
worker->query_reply.c->buffer));
|
||||
verbose(VERB_DETAIL, "process_query ID %d", worker->query_id);
|
||||
pending_udp_query(worker->back, worker->query_reply.c->buffer,
|
||||
&worker->fwd_addr, worker->fwd_addrlen, UDP_QUERY_TIMEOUT,
|
||||
worker_handle_reply, worker);
|
||||
worker_handle_reply, worker, worker->rndstate);
|
||||
}
|
||||
|
||||
/** check request sanity. Returns error code, 0 OK, or -1 discard.
|
||||
@@ -148,6 +175,36 @@ worker_check_request(ldns_buffer* pkt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** process control messages from the main thread. */
|
||||
static int
|
||||
worker_handle_control_cmd(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
{
|
||||
struct worker* worker = (struct worker*)arg;
|
||||
enum worker_commands cmd;
|
||||
if(error != NETEVENT_NOERROR) {
|
||||
if(error == NETEVENT_CLOSED)
|
||||
comm_base_exit(worker->base);
|
||||
else log_info("control event: %d", error);
|
||||
return 0;
|
||||
}
|
||||
if(ldns_buffer_limit(c->buffer) != sizeof(uint32_t)) {
|
||||
fatal_exit("bad control msg length %d",
|
||||
(int)ldns_buffer_limit(c->buffer));
|
||||
}
|
||||
cmd = ldns_buffer_read_u32(c->buffer);
|
||||
switch(cmd) {
|
||||
case worker_cmd_quit:
|
||||
verbose(VERB_ALGO, "got control cmd quit");
|
||||
comm_base_exit(worker->base);
|
||||
break;
|
||||
default:
|
||||
log_err("bad command %d", (int)cmd);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** handles callbacks from listening event interface */
|
||||
static int
|
||||
worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
@@ -170,6 +227,8 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
return 0;
|
||||
}
|
||||
if(worker->num_requests > 0) {
|
||||
/* we could get this due to a slow tcp incoming query,
|
||||
that started before we performed listen_pushback */
|
||||
verbose(VERB_DETAIL, "worker: too many incoming requests "
|
||||
"active. dropping incoming query.");
|
||||
comm_point_drop_reply(repinfo);
|
||||
@@ -177,6 +236,10 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
}
|
||||
/* answer it */
|
||||
worker->num_requests ++;
|
||||
if(worker->num_requests >= 1) {
|
||||
/* the max request number has been reached, stop accepting */
|
||||
listen_pushback(worker->front);
|
||||
}
|
||||
memcpy(&worker->query_reply, repinfo, sizeof(struct comm_reply));
|
||||
worker_process_query(worker);
|
||||
return 0;
|
||||
@@ -191,6 +254,7 @@ worker_sighandler(int sig, void* arg)
|
||||
switch(sig) {
|
||||
case SIGHUP:
|
||||
log_info("caught signal SIGHUP");
|
||||
worker->need_to_restart = 1;
|
||||
comm_base_exit(worker->base);
|
||||
break;
|
||||
case SIGINT:
|
||||
@@ -201,6 +265,10 @@ worker_sighandler(int sig, void* arg)
|
||||
log_info("caught signal SIGQUIT");
|
||||
comm_base_exit(worker->base);
|
||||
break;
|
||||
case SIGTERM:
|
||||
log_info("caught signal SIGTERM");
|
||||
comm_base_exit(worker->base);
|
||||
break;
|
||||
default:
|
||||
log_err("unknown signal: %d, ignored", sig);
|
||||
break;
|
||||
@@ -208,55 +276,120 @@ worker_sighandler(int sig, void* arg)
|
||||
}
|
||||
|
||||
struct worker*
|
||||
worker_init(const char* port, int do_ip4, int do_ip6, int do_udp, int do_tcp,
|
||||
size_t buffer_size, size_t numports, int base_port)
|
||||
worker_create(struct daemon* daemon, int id)
|
||||
{
|
||||
struct worker* worker = (struct worker*)calloc(1,
|
||||
sizeof(struct worker));
|
||||
if(!worker)
|
||||
return NULL;
|
||||
worker->daemon = daemon;
|
||||
worker->thread_num = id;
|
||||
worker->cmd_send_fd = -1;
|
||||
worker->cmd_recv_fd = -1;
|
||||
if(id != 0) {
|
||||
int sv[2];
|
||||
/* create socketpair to communicate with worker */
|
||||
if(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
|
||||
free(worker);
|
||||
log_err("socketpair: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
if(!fd_set_nonblock(sv[0]) || !fd_set_nonblock(sv[1])) {
|
||||
close(sv[0]);
|
||||
close(sv[1]);
|
||||
free(worker);
|
||||
return NULL;
|
||||
}
|
||||
worker->cmd_send_fd = sv[0];
|
||||
worker->cmd_recv_fd = sv[1];
|
||||
}
|
||||
return worker;
|
||||
}
|
||||
|
||||
int
|
||||
worker_init(struct worker* worker, struct config_file *cfg,
|
||||
struct listen_port* ports, size_t buffer_size, int do_sigs)
|
||||
{
|
||||
unsigned int seed;
|
||||
int startport;
|
||||
worker->need_to_restart = 0;
|
||||
worker->base = comm_base_create();
|
||||
if(!worker->base) {
|
||||
log_err("could not create event handling base");
|
||||
worker_delete(worker);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
worker->comsig = comm_signal_create(worker->base, worker_sighandler,
|
||||
worker);
|
||||
if(!worker->comsig || !comm_signal_bind(worker->comsig, SIGHUP)
|
||||
|| !comm_signal_bind(worker->comsig, SIGINT)
|
||||
|| !comm_signal_bind(worker->comsig, SIGQUIT)) {
|
||||
log_err("could not create signal handlers");
|
||||
worker_delete(worker);
|
||||
return NULL;
|
||||
if(do_sigs) {
|
||||
ub_thread_sig_unblock(SIGHUP);
|
||||
ub_thread_sig_unblock(SIGINT);
|
||||
ub_thread_sig_unblock(SIGQUIT);
|
||||
ub_thread_sig_unblock(SIGTERM);
|
||||
#ifndef LIBEVENT_SIGNAL_PROBLEM
|
||||
worker->comsig = comm_signal_create(worker->base,
|
||||
worker_sighandler, worker);
|
||||
if(!worker->comsig || !comm_signal_bind(worker->comsig, SIGHUP)
|
||||
|| !comm_signal_bind(worker->comsig, SIGINT)
|
||||
|| !comm_signal_bind(worker->comsig, SIGTERM)
|
||||
|| !comm_signal_bind(worker->comsig, SIGQUIT)) {
|
||||
log_err("could not create signal handlers");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
#endif /* LIBEVENT_SIGNAL_PROBLEM */
|
||||
} else { /* !do_sigs */
|
||||
worker->comsig = 0;
|
||||
}
|
||||
worker->front = listen_create(worker->base, 0, NULL, port,
|
||||
do_ip4, do_ip6, do_udp, do_tcp, buffer_size,
|
||||
worker_handle_request, worker);
|
||||
worker->front = listen_create(worker->base, ports,
|
||||
buffer_size, worker_handle_request, worker);
|
||||
if(!worker->front) {
|
||||
log_err("could not create listening sockets");
|
||||
worker_delete(worker);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
startport = cfg->outgoing_base_port +
|
||||
cfg->outgoing_num_ports * worker->thread_num;
|
||||
worker->back = outside_network_create(worker->base,
|
||||
buffer_size, numports, NULL, 0, do_ip4, do_ip6, base_port);
|
||||
buffer_size, (size_t)cfg->outgoing_num_ports, cfg->ifs,
|
||||
cfg->num_ifs, cfg->do_ip4, cfg->do_ip6, startport);
|
||||
if(!worker->back) {
|
||||
log_err("could not create outgoing sockets");
|
||||
worker_delete(worker);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
/* init random(), large table size. */
|
||||
if(!(worker->rndstate = (char*)malloc(RND_STATE_SIZE))) {
|
||||
if(!(worker->rndstate = (struct ub_randstate*)calloc(1,
|
||||
sizeof(struct ub_randstate)))) {
|
||||
log_err("malloc rndtable failed.");
|
||||
worker_delete(worker);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
if(!initstate(time(NULL)^getpid(), worker->rndstate, RND_STATE_SIZE)) {
|
||||
seed = (unsigned int)time(NULL) ^ (unsigned int)getpid() ^
|
||||
(unsigned int)worker->thread_num;
|
||||
if(!ub_initstate(seed, worker->rndstate, RND_STATE_SIZE)) {
|
||||
log_err("could not init random numbers.");
|
||||
worker_delete(worker);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
return worker;
|
||||
if(worker->thread_num != 0) {
|
||||
/* start listening to commands */
|
||||
if(!(worker->cmd_com=comm_point_create_local(worker->base,
|
||||
worker->cmd_recv_fd, buffer_size,
|
||||
worker_handle_control_cmd, worker))) {
|
||||
log_err("could not create control compt.");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* set forwarder address */
|
||||
if(cfg->fwd_address && cfg->fwd_address[0]) {
|
||||
if(!worker_set_fwd(worker, cfg->fwd_address, cfg->fwd_port)) {
|
||||
worker_delete(worker);
|
||||
fatal_exit("could not set forwarder address");
|
||||
}
|
||||
}
|
||||
alloc_init(&worker->alloc, &worker->daemon->superalloc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -273,22 +406,28 @@ worker_delete(struct worker* worker)
|
||||
listen_delete(worker->front);
|
||||
outside_network_delete(worker->back);
|
||||
comm_signal_delete(worker->comsig);
|
||||
comm_point_delete(worker->cmd_com);
|
||||
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)
|
||||
close(worker->cmd_send_fd);
|
||||
worker->cmd_send_fd = -1;
|
||||
if(worker->cmd_recv_fd != -1)
|
||||
close(worker->cmd_recv_fd);
|
||||
worker->cmd_recv_fd = -1;
|
||||
alloc_clear(&worker->alloc);
|
||||
free(worker);
|
||||
}
|
||||
|
||||
int
|
||||
worker_set_fwd(struct worker* worker, const char* ip, const char* port)
|
||||
worker_set_fwd(struct worker* worker, const char* ip, int port)
|
||||
{
|
||||
uint16_t p;
|
||||
log_assert(worker && ip);
|
||||
if(port)
|
||||
p = (uint16_t)atoi(port);
|
||||
else p = (uint16_t)atoi(UNBOUND_DNS_PORT);
|
||||
if(!p) {
|
||||
log_err("Bad port number %s", port?port:"default_port");
|
||||
return 0;
|
||||
}
|
||||
p = (uint16_t) port;
|
||||
if(str_is_ip6(ip)) {
|
||||
struct sockaddr_in6* sa =
|
||||
(struct sockaddr_in6*)&worker->fwd_addr;
|
||||
|
||||
+64
-14
@@ -45,17 +45,40 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/locks.h"
|
||||
#include "util/alloc.h"
|
||||
struct listen_dnsport;
|
||||
struct outside_network;
|
||||
struct config_file;
|
||||
struct daemon;
|
||||
struct listen_port;
|
||||
struct ub_randstate;
|
||||
|
||||
/** 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 */
|
||||
worker_cmd_quit
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Structure holding working information for unbound.
|
||||
* Holds globally visible information.
|
||||
*/
|
||||
struct worker {
|
||||
/** the thread number (in daemon array). First in struct for debug. */
|
||||
int thread_num;
|
||||
/** global shared daemon structure */
|
||||
struct daemon* daemon;
|
||||
/** thread id */
|
||||
ub_thread_t thr_id;
|
||||
/** fd 0 of socketpair, write commands for worker to this one */
|
||||
int cmd_send_fd;
|
||||
/** fd 1 of socketpair, worker listens on this one */
|
||||
int cmd_recv_fd;
|
||||
/** the event base this worker works with */
|
||||
struct comm_base* base;
|
||||
/** the frontside listening interface where request events come in */
|
||||
@@ -63,7 +86,9 @@ struct worker {
|
||||
/** the backside outside network interface to the auth servers */
|
||||
struct outside_network* back;
|
||||
/** the signal handler */
|
||||
struct comm_signal *comsig;
|
||||
struct comm_signal* comsig;
|
||||
/** commpoint to listen to commands. */
|
||||
struct comm_point* cmd_com;
|
||||
|
||||
/** number of requests currently active */
|
||||
int num_requests;
|
||||
@@ -78,25 +103,34 @@ struct worker {
|
||||
socklen_t fwd_addrlen;
|
||||
|
||||
/** random() table for this worker. */
|
||||
char* rndstate;
|
||||
struct ub_randstate* rndstate;
|
||||
/** do we need to restart (instead of exit) ? */
|
||||
int need_to_restart;
|
||||
/** allocation cache for this thread */
|
||||
struct alloc_cache alloc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the worker structure. Bare bones version, zeroed struct,
|
||||
* with backpointers only. Use worker_init on it later.
|
||||
* @param daemon: the daemon that this worker thread is part of.
|
||||
* @param id: the thread number from 0.. numthreads-1.
|
||||
* @return: the new worker or NULL on alloc failure.
|
||||
*/
|
||||
struct worker* worker_create(struct daemon* daemon, int id);
|
||||
|
||||
/**
|
||||
* Initialize worker.
|
||||
* Allocates event base, listens to ports
|
||||
* @param port: the port number to bind to.
|
||||
* @param do_ip4: listen to ip4 queries.
|
||||
* @param do_ip6: listen to ip6 queries.
|
||||
* @param do_udp: listen to udp queries.
|
||||
* @param do_tcp: listen to tcp queries.
|
||||
* @param worker: worker to initialize, created with worker_create.
|
||||
* @param cfg: configuration settings.
|
||||
* @param ports: list of shared query ports.
|
||||
* @param buffer_size: size of datagram buffer.
|
||||
* @param numports: number of outgoing ports.
|
||||
* @param base_port: -1 or specify base of outgoing port range.
|
||||
* @return: The worker, or NULL on error.
|
||||
* @param do_sigs: if true, worker installs signal handlers.
|
||||
* @return: false on error.
|
||||
*/
|
||||
struct worker* worker_init(const char* port, int do_ip4, int do_ip6,
|
||||
int do_udp, int do_tcp, size_t buffer_size, size_t numports,
|
||||
int base_port);
|
||||
int worker_init(struct worker* worker, struct config_file *cfg,
|
||||
struct listen_port* ports, size_t buffer_size, int do_sigs);
|
||||
|
||||
/**
|
||||
* Make worker work.
|
||||
@@ -115,6 +149,22 @@ void worker_delete(struct worker* worker);
|
||||
* @param port: port on server or NULL for default 53.
|
||||
* @return: false on error.
|
||||
*/
|
||||
int worker_set_fwd(struct worker* worker, const char* ip, const char* port);
|
||||
int worker_set_fwd(struct worker* worker, const char* ip, int port);
|
||||
|
||||
/**
|
||||
* Send a command to a worker. Uses blocking writes.
|
||||
* @param worker: worker to send command to.
|
||||
* @param buffer: an empty buffer to use.
|
||||
* @param cmd: command to send.
|
||||
*/
|
||||
void worker_send_cmd(struct worker* worker, ldns_buffer* buffer,
|
||||
enum worker_commands cmd);
|
||||
|
||||
/**
|
||||
* Worker signal handler function. User argument is the worker itself.
|
||||
* @param sig: signal number.
|
||||
* @param arg: the worker (main worker) that handles signals.
|
||||
*/
|
||||
void worker_sighandler(int sig, void* arg);
|
||||
|
||||
#endif /* DAEMON_WORKER_H */
|
||||
|
||||
@@ -1,3 +1,92 @@
|
||||
9 March 2007: Wouter
|
||||
- added rwlock writelock checking.
|
||||
So it will keep track of the writelock, and readlocks are enforced
|
||||
to not change protected memory areas.
|
||||
- log_hex function to dump hex strings to the logfile.
|
||||
- checklocks zeroes its destroyed lock after checking memory areas.
|
||||
- unit test for alloc.
|
||||
- identifier for union in checklocks to please older compilers.
|
||||
|
||||
8 March 2007: Wouter
|
||||
- Reviewed checklock code.
|
||||
|
||||
7 March 2007: Wouter
|
||||
- created a wrapper around thread calls that performs some basic
|
||||
checking for data race and deadlock, and basic performance
|
||||
contention measurement.
|
||||
|
||||
6 March 2007: Wouter
|
||||
- Testbed works with threading (different machines, different options).
|
||||
- alloc work, does the special type.
|
||||
|
||||
2 March 2007: Wouter
|
||||
- do not compile fork funcs unless needed. Otherwise will give
|
||||
type errors as their typedefs have not been enabled.
|
||||
- log shows thread numbers much more nicely (and portably).
|
||||
- even on systems with nonthreadsafe libevent signal handling,
|
||||
unbound will exit if given a signal.
|
||||
Reloads will not work, and exit is not graceful.
|
||||
- start of alloc framework layout.
|
||||
|
||||
1 March 2007: Wouter
|
||||
- Signals, libevent and threads work well, with libevent patch and
|
||||
changes to code (close after event_del).
|
||||
- set ipc pipes nonblocking.
|
||||
|
||||
27 February 2007: Wouter
|
||||
- ub_thread_join portable definition.
|
||||
- forking is used if no threading is available.
|
||||
Tested, it works, since pipes work across processes as well.
|
||||
Thread_join is replaced with waitpid.
|
||||
- During reloads the daemon will temporarily handle signals,
|
||||
so that they do not result in problems.
|
||||
- Also randomize the outgoing port range for tests.
|
||||
- If query list is full, will stop selecting listening ports for read.
|
||||
This makes all threads service incoming requests, instead of one.
|
||||
No memory is leaking during reloads, service of queries, etc.
|
||||
- test that uses ldns-testns -f to test threading. Have to answer
|
||||
three queries at the same time.
|
||||
- with verbose=0 operates quietly.
|
||||
|
||||
26 February 2007: Wouter
|
||||
- ub_random code used to select ID and port.
|
||||
- log code prints thread id.
|
||||
- unbound can thread itself, with reload(HUP) and quit working
|
||||
correctly.
|
||||
- don't open pipes for #0, doesn't need it.
|
||||
- listens to SIGTERM, SIGQUIT, SIGINT (all quit) and SIGHUP (reload).
|
||||
|
||||
23 February 2007: Wouter
|
||||
- Can do reloads on sigHUP. Everything is stopped, and freed,
|
||||
except the listening ports. Then the config file is reread.
|
||||
And everything is started again (and listening ports if needed).
|
||||
- Ports for queries are shared.
|
||||
- config file added interface:, chroot: and username:.
|
||||
- config file: directory, logfile, pidfile. And they work too.
|
||||
- will daemonize by default now. Use -d to stay in the foreground.
|
||||
- got BSD random[256 state] code, made it threadsafe. util/random.
|
||||
|
||||
22 February 2007: Wouter
|
||||
- Have a config file. Removed commandline options, moved to config.
|
||||
- tests use config file.
|
||||
|
||||
21 February 2007: Wouter
|
||||
- put -c option in man page.
|
||||
- minievent fd array capped by FD_SETSIZE.
|
||||
|
||||
20 February 2007: Wouter
|
||||
- Added locks code and pthread spinlock detection.
|
||||
- can use no locks, or solaris native thread library.
|
||||
- added yacc and lex configure, and config file parsing code.
|
||||
also makedist.sh, and manpage.
|
||||
- put include errno.h in config.h
|
||||
|
||||
19 February 2007: Wouter
|
||||
- Created 0.0 svn tag.
|
||||
- added acx_pthread.m4 autoconf check for pthreads from
|
||||
the autoconf archive. It is GPL-with-autoconf-exception Licensed.
|
||||
You can specify --with-pthreads, or --without-pthreads to configure.
|
||||
|
||||
16 February 2007: Wouter
|
||||
- Updated testbed script, works better by using make on remote end.
|
||||
- removed check decls, we can compile without them.
|
||||
|
||||
@@ -18,4 +18,11 @@ This software is under BSD license, see LICENSE for details.
|
||||
* run libtoolize -c if config.sub is missing.
|
||||
* autoreconf (autoheader && autoconf), if ./configure is missing.
|
||||
|
||||
Known issues
|
||||
------------
|
||||
o If libevent is older (1.3 and before), 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.
|
||||
|
||||
* mailto:wouter@nlnetlabs.nl
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
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.
|
||||
@@ -0,0 +1,75 @@
|
||||
#
|
||||
# Example configuration file.
|
||||
#
|
||||
# See unbound.conf(5) man page.
|
||||
#
|
||||
# this is a comment.
|
||||
|
||||
#Use this to include other text into the file.
|
||||
#include: "otherfile.conf"
|
||||
|
||||
# The server clause sets the main parameters.
|
||||
server:
|
||||
# whitespace is not necessary, but looks cleaner.
|
||||
|
||||
# verbosity number, 0 is least verbose. 1 is default.
|
||||
verbosity: 2
|
||||
|
||||
# number of threads to create. 1 disables threading.
|
||||
# num-threads: 1
|
||||
|
||||
# specify the interfaces to answer queries from by ip-address.
|
||||
# If you give none the default (all) interface is used.
|
||||
# interface: 127.0.0.1
|
||||
|
||||
# port to answer queries from
|
||||
# port: 53
|
||||
|
||||
# unbound needs to send packets to authoritative nameservers.
|
||||
# it uses a range of ports for that.
|
||||
# the start number of the port range
|
||||
# outgoing-port: 1053
|
||||
|
||||
# number of port to allocate per thread, determines the size of the
|
||||
# port range. A larger port range gives more resistance to certain
|
||||
# spoof attacks, as it gets harder to guess which port is used.
|
||||
# But also takes more system resources (for open sockets).
|
||||
# outgoing-range: 16
|
||||
|
||||
# Enable IPv4, "yes" or "no".
|
||||
# do-ip4: yes
|
||||
|
||||
# Enable IPv6, "yes" or "no".
|
||||
# do-ip6: yes
|
||||
|
||||
# Enable UDP, "yes" or "no".
|
||||
# do-udp: yes
|
||||
|
||||
# Enable TCP, "yes" or "no".
|
||||
# do-tcp: yes
|
||||
|
||||
# Set this to configure unbound to act as a forwarder. All queries are
|
||||
# sent to the remote nameserver that will resolve them.
|
||||
# Set to "" to disable forwarding, or give ip-address to enable.
|
||||
# forward-to: ""
|
||||
|
||||
# The port number to send forwarded queries to.
|
||||
# forward-to-port: 53
|
||||
|
||||
# if given, a chroot(2) is done to the given directory.
|
||||
# i.e. you can chroot to the working directory, for example,
|
||||
# for extra security, but make sure all files are in that directory.
|
||||
# chroot: "/some/directory"
|
||||
|
||||
# if given, user privileges are dropped (after binding port),
|
||||
# and the given username is assumed. Default is nothing "".
|
||||
# username: "unbound"
|
||||
|
||||
# the working directory.
|
||||
# directory: "/etc/unbound"
|
||||
|
||||
# the log file, "" means log to stderr.
|
||||
# logfile: ""
|
||||
|
||||
# the pid file.
|
||||
# pidfile: "unbound.pid"
|
||||
+11
-11
@@ -41,10 +41,9 @@ unbound
|
||||
.Sh SYNOPSIS
|
||||
.Nm unbound
|
||||
.Op Fl h
|
||||
.Op Fl p Ar port
|
||||
.Op Fl f Ar ip
|
||||
.Op Fl z Ar port
|
||||
.Op Fl d
|
||||
.Op Fl v
|
||||
.Op Fl c Ar cfgfile
|
||||
|
||||
.Sh DESCRIPTION
|
||||
.Ic Unbound
|
||||
@@ -57,21 +56,22 @@ The available options are:
|
||||
.It Fl h
|
||||
Show the version and commandline option help.
|
||||
|
||||
.It Fl p Ar port
|
||||
Start listening on the given port. Default is port 53(DNS).
|
||||
.It Fl c Ar cfgfile
|
||||
Set the config file to read with settings for unbound. The syntax is
|
||||
described in
|
||||
.Xr unbound.conf 5 .
|
||||
|
||||
.It Fl f Ar ip
|
||||
Set forwarder address. DNS queries will be forwarded to this server.
|
||||
|
||||
.It Fl z Ar ip
|
||||
Set forwarder port. DNS queries will be forwarded to this port.
|
||||
.It Fl d
|
||||
Debug flag, do not fork into the background, but stay attached to the
|
||||
console.
|
||||
|
||||
.It Fl 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 resolv.conf 5
|
||||
.Xr unbound.conf 5 .
|
||||
|
||||
.Sh AUTHORS
|
||||
.Ic Unbound
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
.\"
|
||||
.\" unbound.conf.5 -- unbound.conf manual
|
||||
.\"
|
||||
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
.\"
|
||||
.\" 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
|
||||
is used to configure
|
||||
.Xr unbound 8 .
|
||||
The file format has attributes and values. Some attributes have attributes inside them.
|
||||
The notation is: attribute: value.
|
||||
|
||||
Comments start with # and last to the end of line. Empty lines are
|
||||
ignored as is whitespace at the beginning of a line.
|
||||
|
||||
.El
|
||||
.Sh FILE FORMAT
|
||||
There must be whitespace between keywords. Attribute keywords end with a colon ':'. An attribute
|
||||
is followed by its containing attributes, or a value.
|
||||
|
||||
.Pp
|
||||
Files can be included using the
|
||||
.Ic 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
|
||||
There may only be one
|
||||
.Ic server:
|
||||
clause.
|
||||
.Bl -tag -width indent
|
||||
.It \fBverbosity:\fR <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.
|
||||
Default is level 1. The verbosity can also be increased from the commandline,
|
||||
see
|
||||
.Xr unbound 8 .
|
||||
.It \fBnum-threads:\fR <number>
|
||||
The number of threads to create to serve clients. Use 1 for no threading.
|
||||
.It \fBport:\fR <port number>
|
||||
The port number, default 53, on which the server responds to queries.
|
||||
.It \fBinterface:\fR <ip address>
|
||||
Interface to use to connect to the network. Can be given multiple times to
|
||||
work on several interfaces. If none are given the default (all) is used.
|
||||
.It \fBoutgoing-port:\fR <port number>
|
||||
The starting port number where the outgoing query port range is allocated.
|
||||
Default is 1053.
|
||||
.It \fBoutgoing-range:\fR <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 \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 \fBforward-to:\fR <ip address>
|
||||
If set (not "") then forwarder mode is enabled. Default is "" (disabled).
|
||||
The ip address is used to forward all DNS queries to.
|
||||
.It \fBforward-to-port:\fR <port number>
|
||||
The port on which the remote server is running that answers forwarded queries.
|
||||
Default is 53.
|
||||
.It \fBchroot:\fR <directory>
|
||||
If given a chroot is done to the given directory. The default is none ("").
|
||||
.It \fBusername:\fR <name>
|
||||
If given, after binding the port the user privileges are dropped. Default is
|
||||
not to change user, username: "". If this user is not capable of binding the
|
||||
port, reloads (by signal HUP) will still retain the opened ports.
|
||||
If you change the port number in the config file, and that new port number
|
||||
requires privileges, then a restart is needed.
|
||||
.It \fBdirectory:\fR <directory>
|
||||
Sets the working directory for the program.
|
||||
.It \fBlogfile:\fR <filename>
|
||||
If "" is given, logging goes to stderr, or nowhere once daemonized.
|
||||
The logfile is appended to, in the following format:
|
||||
[seconds since 1970] unbound[pid:tid]: type: message.
|
||||
.It \fBpidfile:\fR <filename>
|
||||
The process id is written to the file. Default is "unbound.pid". So,
|
||||
kill -HUP `cat /etc/unbound/unbound.pid` will trigger a reload,
|
||||
kill -QUIT `cat /etc/unbound/unbound.pid` will gracefully terminate.
|
||||
|
||||
.Sh FILES
|
||||
.Bl -tag -width indent
|
||||
.It Pa unbound.conf
|
||||
unbound configuration file.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr unbound 8
|
||||
.Sh AUTHORS
|
||||
.Ic Unbound
|
||||
was written by NLnet Labs. Please see CREDITS file
|
||||
in the distribution for further details.
|
||||
+1
-1
@@ -480,7 +480,7 @@ RECURSIVE = YES
|
||||
# excluded from the INPUT source files. This way you can easily exclude a
|
||||
# subdirectory from a directory tree whose root is specified with the INPUT tag.
|
||||
|
||||
EXCLUDE = ./build ./compat
|
||||
EXCLUDE = ./build ./compat util/configparser.c util/configparser.h util/configlexer.c util/locks.h
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
|
||||
# directories that are symbolic links (a Unix filesystem feature) are excluded
|
||||
|
||||
+7
-7
@@ -134,7 +134,7 @@ done
|
||||
# Check if SVNROOT is specified.
|
||||
if [ -z "$SVNROOT" ]; then
|
||||
if test -f .svn/entries; then
|
||||
eval `grep 'url=' .svn/entries`
|
||||
eval `grep 'url=' .svn/entries | head -1`
|
||||
SVNROOT="$url"
|
||||
fi
|
||||
if test -z "$SVNROOT"; then
|
||||
@@ -175,15 +175,15 @@ cd unbound || error_cleanup "Unbound not exported correctly from SVN"
|
||||
info "Adding libtool utils (libtoolize)."
|
||||
libtoolize -c || error_cleanup "libtoolize failed"
|
||||
|
||||
info "Building configure script (autoconf)."
|
||||
autoconf || error_cleanup "Autoconf failed."
|
||||
|
||||
info "Building config.h.in (autoheader)."
|
||||
autoheader || error_cleanup "Autoheader failed."
|
||||
info "Building configure script (autoreconf)."
|
||||
autoreconf || error_cleanup "Autoconf failed."
|
||||
|
||||
rm -r autom4te* || error_cleanup "Failed to remove autoconf cache directory."
|
||||
|
||||
# info "Building lexer and parser."
|
||||
info "Building lexer and parser."
|
||||
echo "#include \"util/configyyrename.h\"" > util/configlexer.c || error_cleanup "Failed to create configlexer"
|
||||
flex -i -t util/configlexer.lex >> util/configlexer.c || error_cleanup "Failed to create configlexer"
|
||||
bison -y -d -o util/configparser.c util/configparser.y || error_cleanup "Failed to create configparser"
|
||||
|
||||
# copy ldns-testpkts from ldns examples
|
||||
#cp $LDNSDIR/examples/ldns-testpkts.c testcode/ldns-testpkts.c || error_cleanup "copy ldns/examples/.. failed"
|
||||
|
||||
+173
-109
@@ -43,13 +43,13 @@
|
||||
#include "services/outside_network.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/** number of queued TCP connections for listen() */
|
||||
@@ -89,7 +89,7 @@ verbose_print_addr(struct addrinfo *addr)
|
||||
int
|
||||
create_udp_sock(struct addrinfo *addr)
|
||||
{
|
||||
int s, flag;
|
||||
int s;
|
||||
# if defined(IPV6_V6ONLY)
|
||||
int on=1;
|
||||
# endif
|
||||
@@ -128,15 +128,8 @@ create_udp_sock(struct addrinfo *addr)
|
||||
log_err("can't bind socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if((flag = fcntl(s, F_GETFL)) == -1) {
|
||||
log_err("can't fcntl F_GETFL: %s", strerror(errno));
|
||||
flag = 0;
|
||||
}
|
||||
flag |= O_NONBLOCK;
|
||||
if(fcntl(s, F_SETFL, flag) == -1) {
|
||||
log_err("can't fcntl F_SETFL: %s", strerror(errno));
|
||||
if(!fd_set_nonblock(s))
|
||||
return -1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -219,87 +212,86 @@ make_sock(int stype, const char* ifname, const char* port,
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for listen_create. Creates one interface (or NULL for default).
|
||||
* Add port to open ports list.
|
||||
* @param list: list head. changed.
|
||||
* @param s: fd.
|
||||
* @param is_udp: 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)
|
||||
{
|
||||
struct listen_port* item = (struct listen_port*)malloc(
|
||||
sizeof(struct listen_port));
|
||||
if(!item)
|
||||
return 0;
|
||||
item->next = *list;
|
||||
item->fd = s;
|
||||
item->is_udp = is_udp;
|
||||
*list = item;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for ports_open. Creates one interface (or NULL for default).
|
||||
* @param ifname: The interface ip address.
|
||||
* @param front: The the listening info.
|
||||
* @param base: Event base.
|
||||
* @param port: Port number to use (as string).
|
||||
* @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.
|
||||
* @param bufsize: TCP buffer size.
|
||||
* @param cb: callback function
|
||||
* @param cb_arg: user parameter for callback function.
|
||||
* @param port: Port number to use (as string).
|
||||
* @param list: list of open ports, appended to, changed to point to list head.
|
||||
* @return: returns false on error.
|
||||
*/
|
||||
static int
|
||||
listen_create_if(const char* ifname, struct listen_dnsport* front,
|
||||
struct comm_base* base, const char* port, int do_udp, int do_tcp,
|
||||
struct addrinfo *hints, size_t bufsize, comm_point_callback_t* cb,
|
||||
void *cb_arg)
|
||||
ports_create_if(const char* ifname, int do_udp, int do_tcp,
|
||||
struct addrinfo *hints, const char* port, struct listen_port** list)
|
||||
{
|
||||
struct comm_point *cp_udp = NULL, *cp_tcp = NULL;
|
||||
struct listen_list *el_udp, *el_tcp;
|
||||
int s;
|
||||
if(!do_udp && !do_tcp)
|
||||
return 0;
|
||||
if(do_udp) {
|
||||
if((s = make_sock(SOCK_DGRAM, ifname, port, hints)) == -1)
|
||||
return 0;
|
||||
cp_udp = comm_point_create_udp(base, s, front->udp_buff,
|
||||
cb, cb_arg);
|
||||
if(!cp_udp) {
|
||||
log_err("can't create commpoint");
|
||||
if(!port_insert(list, s, 1)) {
|
||||
close(s);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if(do_tcp) {
|
||||
if((s = make_sock(SOCK_STREAM, ifname, port, hints)) == -1) {
|
||||
comm_point_delete(cp_udp);
|
||||
return 0;
|
||||
}
|
||||
cp_tcp = comm_point_create_tcp(base, s, TCP_COUNT, bufsize,
|
||||
cb, cb_arg);
|
||||
if(!cp_tcp) {
|
||||
log_err("can't create commpoint");
|
||||
comm_point_delete(cp_udp);
|
||||
if(!port_insert(list, s, 0)) {
|
||||
close(s);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* add commpoints to the listen structure */
|
||||
el_udp = (struct listen_list*)malloc(sizeof(struct listen_list));
|
||||
if(!el_udp) {
|
||||
log_err("out of memory");
|
||||
comm_point_delete(cp_udp);
|
||||
comm_point_delete(cp_tcp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add items to commpoint list in front.
|
||||
* @param c: commpoint to add.
|
||||
* @param front: listen struct.
|
||||
* @return: false on failure.
|
||||
*/
|
||||
static int
|
||||
listen_cp_insert(struct comm_point* c, struct listen_dnsport* front)
|
||||
{
|
||||
struct listen_list* item = (struct listen_list*)malloc(
|
||||
sizeof(struct listen_list));
|
||||
if(!item)
|
||||
return 0;
|
||||
}
|
||||
el_tcp = (struct listen_list*)malloc(sizeof(struct listen_list));
|
||||
if(!el_tcp) {
|
||||
log_err("out of memory");
|
||||
free(el_udp);
|
||||
comm_point_delete(cp_udp);
|
||||
comm_point_delete(cp_tcp);
|
||||
return 0;
|
||||
}
|
||||
el_udp->com = cp_udp;
|
||||
el_udp->next = front->cps;
|
||||
front->cps = el_udp;
|
||||
el_tcp->com = cp_tcp;
|
||||
el_tcp->next = front->cps;
|
||||
front->cps = el_tcp;
|
||||
item->com = c;
|
||||
item->next = front->cps;
|
||||
front->cps = item;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct listen_dnsport*
|
||||
listen_create(struct comm_base* base, int num_ifs, const char* ifs[],
|
||||
const char* port, int do_ip4, int do_ip6, int do_udp, int do_tcp,
|
||||
listen_create(struct comm_base* base, struct listen_port* ports,
|
||||
size_t bufsize, comm_point_callback_t* cb, void *cb_arg)
|
||||
{
|
||||
struct addrinfo hints;
|
||||
int i;
|
||||
struct listen_dnsport* front = (struct listen_dnsport*)
|
||||
malloc(sizeof(struct listen_dnsport));
|
||||
if(!front)
|
||||
@@ -311,59 +303,27 @@ listen_create(struct comm_base* base, int num_ifs, const char* ifs[],
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* getaddrinfo */
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
/* no name lookups on our listening ports */
|
||||
if(num_ifs > 0)
|
||||
hints.ai_flags |= AI_NUMERICHOST;
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
#ifndef INET6
|
||||
do_ip6 = 0;
|
||||
#endif
|
||||
if(!do_ip4 && !do_ip6) {
|
||||
listen_delete(front);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* create ip4 and ip6 ports so that return addresses are nice. */
|
||||
if(num_ifs == 0) {
|
||||
if(do_ip6) {
|
||||
hints.ai_family = AF_INET6;
|
||||
if(!listen_create_if(NULL, front, base, port,
|
||||
do_udp, do_tcp, &hints, bufsize, cb, cb_arg)) {
|
||||
listen_delete(front);
|
||||
return NULL;
|
||||
}
|
||||
/* create comm points as needed */
|
||||
while(ports) {
|
||||
struct comm_point* cp = NULL;
|
||||
if(ports->is_udp)
|
||||
cp = comm_point_create_udp(base, ports->fd,
|
||||
front->udp_buff, cb, cb_arg);
|
||||
else cp = comm_point_create_tcp(base, ports->fd,
|
||||
TCP_COUNT, bufsize, cb, cb_arg);
|
||||
if(!cp) {
|
||||
log_err("can't create commpoint");
|
||||
listen_delete(front);
|
||||
return NULL;
|
||||
}
|
||||
if(do_ip4) {
|
||||
hints.ai_family = AF_INET;
|
||||
if(!listen_create_if(NULL, front, base, port,
|
||||
do_udp, do_tcp, &hints, bufsize, cb, cb_arg)) {
|
||||
listen_delete(front);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
} else for(i = 0; i<num_ifs; i++) {
|
||||
if(str_is_ip6(ifs[i])) {
|
||||
if(!do_ip6)
|
||||
continue;
|
||||
hints.ai_family = AF_INET6;
|
||||
if(!listen_create_if(ifs[i], front, base, port,
|
||||
do_udp, do_tcp, &hints, bufsize, cb, cb_arg)) {
|
||||
listen_delete(front);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if(!do_ip4)
|
||||
continue;
|
||||
hints.ai_family = AF_INET;
|
||||
if(!listen_create_if(ifs[i], front, base, port,
|
||||
do_udp, do_tcp, &hints, bufsize, cb, cb_arg)) {
|
||||
listen_delete(front);
|
||||
return NULL;
|
||||
}
|
||||
cp->do_not_close = 1;
|
||||
if(!listen_cp_insert(cp, front)) {
|
||||
log_err("malloc failed");
|
||||
comm_point_delete(cp);
|
||||
listen_delete(front);
|
||||
return NULL;
|
||||
}
|
||||
ports = ports->next;
|
||||
}
|
||||
if(!front->cps) {
|
||||
log_err("Could not open sockets to accept queries.");
|
||||
@@ -390,3 +350,107 @@ listen_delete(struct listen_dnsport* front)
|
||||
ldns_buffer_free(front->udp_buff);
|
||||
free(front);
|
||||
}
|
||||
|
||||
void listen_pushback(struct listen_dnsport* listen)
|
||||
{
|
||||
struct listen_list *p;
|
||||
log_assert(listen);
|
||||
for(p = listen->cps; p; p = p->next)
|
||||
{
|
||||
if(p->com->type != comm_udp &&
|
||||
p->com->type != comm_tcp_accept)
|
||||
continue;
|
||||
comm_point_stop_listening(p->com);
|
||||
}
|
||||
}
|
||||
|
||||
void listen_resume(struct listen_dnsport* listen)
|
||||
{
|
||||
struct listen_list *p;
|
||||
log_assert(listen);
|
||||
for(p = listen->cps; p; p = p->next)
|
||||
{
|
||||
if(p->com->type != comm_udp &&
|
||||
p->com->type != comm_tcp_accept)
|
||||
continue;
|
||||
comm_point_start_listening(p->com, -1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
struct listen_port*
|
||||
listening_ports_open(struct config_file* cfg)
|
||||
{
|
||||
struct listen_port* list = NULL;
|
||||
struct addrinfo hints;
|
||||
int i, do_ip4, do_ip6;
|
||||
char portbuf[32];
|
||||
snprintf(portbuf, sizeof(portbuf), "%d", cfg->port);
|
||||
do_ip4 = cfg->do_ip4;
|
||||
do_ip6 = cfg->do_ip6;
|
||||
|
||||
/* getaddrinfo */
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
/* no name lookups on our listening ports */
|
||||
if(cfg->num_ifs > 0)
|
||||
hints.ai_flags |= AI_NUMERICHOST;
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
#ifndef INET6
|
||||
do_ip6 = 0;
|
||||
#endif
|
||||
if(!do_ip4 && !do_ip6) {
|
||||
return NULL;
|
||||
}
|
||||
/* create ip4 and ip6 ports so that return addresses are nice. */
|
||||
if(cfg->num_ifs == 0) {
|
||||
if(do_ip6) {
|
||||
hints.ai_family = AF_INET6;
|
||||
if(!ports_create_if(NULL, cfg->do_udp, cfg->do_tcp,
|
||||
&hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if(do_ip4) {
|
||||
hints.ai_family = AF_INET;
|
||||
if(!ports_create_if(NULL, cfg->do_udp, cfg->do_tcp,
|
||||
&hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
} else for(i = 0; i<cfg->num_ifs; i++) {
|
||||
if(str_is_ip6(cfg->ifs[i])) {
|
||||
if(!do_ip6)
|
||||
continue;
|
||||
hints.ai_family = AF_INET6;
|
||||
if(!ports_create_if(cfg->ifs[i], cfg->do_udp,
|
||||
cfg->do_tcp, &hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if(!do_ip4)
|
||||
continue;
|
||||
hints.ai_family = AF_INET;
|
||||
if(!ports_create_if(cfg->ifs[i], cfg->do_udp,
|
||||
cfg->do_tcp, &hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void listening_ports_free(struct listen_port* list)
|
||||
{
|
||||
struct listen_port* nx;
|
||||
while(list) {
|
||||
nx = list->next;
|
||||
if(list->fd != -1)
|
||||
close(list->fd);
|
||||
free(list);
|
||||
list = nx;
|
||||
}
|
||||
}
|
||||
|
||||
+50
-11
@@ -46,6 +46,7 @@
|
||||
#include "util/netevent.h"
|
||||
struct listen_list;
|
||||
struct addrinfo;
|
||||
struct config_file;
|
||||
|
||||
/**
|
||||
* Listening for queries structure.
|
||||
@@ -74,18 +75,38 @@ struct listen_list {
|
||||
};
|
||||
|
||||
/**
|
||||
* Single linked list to store shared ports that have been
|
||||
* opened for use by all threads.
|
||||
*/
|
||||
struct listen_port {
|
||||
/** next in list */
|
||||
struct listen_port* next;
|
||||
/** file descriptor, open and ready for use */
|
||||
int fd;
|
||||
/** type of file descriptor, udp or tcp */
|
||||
int is_udp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create shared listening ports
|
||||
* Getaddrinfo, create socket, bind and listen to zero or more
|
||||
* interfaces for IP4 and/or IP6, for UDP and/or TCP.
|
||||
* On the given port number. It creates the listening sockets.
|
||||
* On the given port number. It creates the sockets.
|
||||
* @param cfg: settings on what ports to open.
|
||||
* @return: linked list of ports or NULL on error.
|
||||
*/
|
||||
struct listen_port* listening_ports_open(struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Close and delete the (list of) listening ports.
|
||||
*/
|
||||
void listening_ports_free(struct listen_port* list);
|
||||
|
||||
/**
|
||||
* Create commpoints with for this thread for the shared ports.
|
||||
* @param base: the comm_base that provides event functionality.
|
||||
* @param num_ifs: number of interfaces to listen on. Can be 0,
|
||||
* for default all ifs.
|
||||
* @param ifs: array of strings with interface specs, IP addresses.
|
||||
* @param port: the port number to bind to.
|
||||
* @param do_ip4: listen to ip4 queries.
|
||||
* @param do_ip6: listen to ip6 queries.
|
||||
* @param do_udp: listen to udp queries.
|
||||
* @param do_tcp: listen to tcp queries.
|
||||
* @param ports: the list of shared ports.
|
||||
* @param bufsize: size of datagram buffer.
|
||||
* @param cb: callback function when a request arrives. It is passed
|
||||
* the packet and user argument. Return true to send a reply.
|
||||
@@ -93,9 +114,27 @@ struct listen_list {
|
||||
* @return: the malloced listening structure, ready for use. NULL on error.
|
||||
*/
|
||||
struct listen_dnsport* listen_create(struct comm_base* base,
|
||||
int num_ifs, const char* ifs[], const char* port,
|
||||
int do_ip4, int do_ip6, int do_udp, int do_tcp,
|
||||
size_t bufsize, comm_point_callback_t* cb, void* cb_arg);
|
||||
struct listen_port* ports, size_t bufsize,
|
||||
comm_point_callback_t* cb, void* cb_arg);
|
||||
|
||||
/**
|
||||
* Stop listening to the dnsports. Ports are still open but not checked
|
||||
* for readability - performs pushback of the load.
|
||||
* @param listen: the listening structs to stop listening on. Note that
|
||||
* udp and tcp-accept handlers stop, but ongoing tcp-handlers are kept
|
||||
* going, since its rude to 'reset connection by peer' them, instead,
|
||||
* we keep them and the callback will be called when its ready. It can
|
||||
* be dropped at that time. New tcp and udp queries can be served by
|
||||
* other threads.
|
||||
*/
|
||||
void listen_pushback(struct listen_dnsport* listen);
|
||||
|
||||
/**
|
||||
* Start listening again to the dnsports.
|
||||
* Call after the listen_pushback has been called.
|
||||
* @param listen: the listening structs to stop listening on.
|
||||
*/
|
||||
void listen_resume(struct listen_dnsport* listen);
|
||||
|
||||
/**
|
||||
* delete the listening structure
|
||||
|
||||
+17
-13
@@ -45,12 +45,12 @@
|
||||
#include "util/netevent.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/random.h"
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/** number of times to retry making a random ID that is unique. */
|
||||
@@ -165,7 +165,7 @@ open_udp_port_range(const char* ifname, struct addrinfo* hints, int porthint)
|
||||
{
|
||||
struct addrinfo *res = NULL;
|
||||
int r, s;
|
||||
char portstr[20];
|
||||
char portstr[32];
|
||||
if(porthint != -1)
|
||||
snprintf(portstr, sizeof(portstr), "%d", porthint);
|
||||
|
||||
@@ -230,7 +230,7 @@ make_udp_range(struct comm_point** coms, const char* ifname,
|
||||
|
||||
/** calculate number of ip4 and ip6 interfaces, times multiplier. */
|
||||
static void
|
||||
calc_num46(const char** ifs, int num_ifs, int do_ip4, int do_ip6,
|
||||
calc_num46(char** ifs, int num_ifs, int do_ip4, int do_ip6,
|
||||
size_t multiplier, size_t* num_ip4, size_t* num_ip6)
|
||||
{
|
||||
int i;
|
||||
@@ -269,7 +269,7 @@ pending_udp_timer_cb(void *arg)
|
||||
|
||||
struct outside_network*
|
||||
outside_network_create(struct comm_base *base, size_t bufsize,
|
||||
size_t num_ports, const char** ifs, int num_ifs, int do_ip4,
|
||||
size_t num_ports, char** ifs, int num_ifs, int do_ip4,
|
||||
int do_ip6, int port_base)
|
||||
{
|
||||
struct outside_network* outnet = (struct outside_network*)
|
||||
@@ -392,7 +392,8 @@ pending_delete(struct outside_network* outnet, struct pending* p)
|
||||
static struct pending*
|
||||
new_pending(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
comm_point_callback_t* callback, void* callback_arg)
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
struct ub_randstate* rnd)
|
||||
{
|
||||
/* alloc */
|
||||
int id_tries = 0;
|
||||
@@ -410,7 +411,7 @@ new_pending(struct outside_network* outnet, ldns_buffer* packet,
|
||||
}
|
||||
/* set */
|
||||
/* id uses lousy random() TODO use better and entropy */
|
||||
pend->id = (random()>>8) & 0xffff;
|
||||
pend->id = ((unsigned)ub_random(rnd)>>8) & 0xffff;
|
||||
LDNS_ID_SET(ldns_buffer_begin(packet), pend->id);
|
||||
memcpy(&pend->addr, addr, addrlen);
|
||||
pend->addrlen = addrlen;
|
||||
@@ -422,7 +423,7 @@ new_pending(struct outside_network* outnet, ldns_buffer* packet,
|
||||
pend->node.key = pend;
|
||||
while(!rbtree_insert(outnet->pending, &pend->node)) {
|
||||
/* change ID to avoid collision */
|
||||
pend->id = (random()>>8) & 0xffff;
|
||||
pend->id = ((unsigned)ub_random(rnd)>>8) & 0xffff;
|
||||
LDNS_ID_SET(ldns_buffer_begin(packet), pend->id);
|
||||
id_tries++;
|
||||
if(id_tries == MAX_ID_RETRY) {
|
||||
@@ -454,9 +455,11 @@ addr_is_ip6(struct sockaddr_storage* addr)
|
||||
* Select outgoing comm point for a query. Fills in c.
|
||||
* @param outnet: network structure that has arrays of ports to choose from.
|
||||
* @param pend: the message to send. c is filled in, randomly chosen.
|
||||
* @param rnd: random state for generating ID and port.
|
||||
*/
|
||||
static void
|
||||
select_port(struct outside_network* outnet, struct pending* pend)
|
||||
select_port(struct outside_network* outnet, struct pending* pend,
|
||||
struct ub_randstate* rnd)
|
||||
{
|
||||
double precho;
|
||||
int chosen, nummax;
|
||||
@@ -474,8 +477,8 @@ select_port(struct outside_network* outnet, struct pending* pend)
|
||||
}
|
||||
|
||||
/* choose a random outgoing port and interface */
|
||||
/* uses lousy random() function. TODO: entropy source. */
|
||||
precho = (double)random() * (double)nummax /
|
||||
/* TODO: entropy source. */
|
||||
precho = (double)ub_random(rnd) * (double)nummax /
|
||||
((double)RAND_MAX + 1.0);
|
||||
chosen = (int)precho;
|
||||
|
||||
@@ -495,18 +498,19 @@ select_port(struct outside_network* outnet, struct pending* pend)
|
||||
void
|
||||
pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
comm_point_callback_t* cb, void* cb_arg)
|
||||
comm_point_callback_t* cb, void* cb_arg, struct ub_randstate* rnd)
|
||||
{
|
||||
struct pending* pend;
|
||||
struct timeval tv;
|
||||
|
||||
/* create pending struct and change ID to be unique */
|
||||
if(!(pend=new_pending(outnet, packet, addr, addrlen, cb, cb_arg))) {
|
||||
if(!(pend=new_pending(outnet, packet, addr, addrlen, cb, cb_arg,
|
||||
rnd))) {
|
||||
/* callback user for the error */
|
||||
(void)(*cb)(NULL, cb_arg, NETEVENT_CLOSED, NULL);
|
||||
return;
|
||||
}
|
||||
select_port(outnet, pend);
|
||||
select_port(outnet, pend, rnd);
|
||||
|
||||
/* send it over the commlink */
|
||||
if(!comm_point_send_udp_msg(pend->c, packet, (struct sockaddr*)addr,
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "util/netevent.h"
|
||||
struct pending;
|
||||
struct pending_timeout;
|
||||
struct ub_randstate;
|
||||
|
||||
/**
|
||||
* Send queries to outside servers and wait for answers from servers.
|
||||
@@ -119,7 +120,7 @@ struct pending {
|
||||
* @return: the new structure (with no pending answers) or NULL on error.
|
||||
*/
|
||||
struct outside_network* outside_network_create(struct comm_base* base,
|
||||
size_t bufsize, size_t num_ports, const char** ifs, int num_ifs,
|
||||
size_t bufsize, size_t num_ports, char** ifs, int num_ifs,
|
||||
int do_ip4, int do_ip6, int port_base);
|
||||
|
||||
/**
|
||||
@@ -140,10 +141,12 @@ void outside_network_delete(struct outside_network* outnet);
|
||||
* The routine does not return an error, instead it calls the callback,
|
||||
* with an error code if an error happens.
|
||||
* @param callback_arg: user argument for callback function.
|
||||
* @param rnd: random state for generating ID and port.
|
||||
*/
|
||||
void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
comm_point_callback_t* callback, void* callback_arg);
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
struct ub_randstate* rnd);
|
||||
|
||||
/**
|
||||
* Delete pending answer.
|
||||
|
||||
@@ -0,0 +1,605 @@
|
||||
/**
|
||||
* testcode/checklocks.c - wrapper on locks that checks access.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <signal.h>
|
||||
#include "util/locks.h" /* include before checklocks.h */
|
||||
#include "testcode/checklocks.h"
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Locks that are checked.
|
||||
*
|
||||
* Ugly hack: uses the fact that workers start with an int thread_num, and
|
||||
* are passed to thread_create to make the thread numbers here the same as
|
||||
* those used for logging which is nice.
|
||||
*
|
||||
* Todo: - check global ordering of instances of locks.
|
||||
* - refcount statistics.
|
||||
* - debug status print, of thread lock stacks, and current waiting.
|
||||
*/
|
||||
#ifdef USE_THREAD_DEBUG
|
||||
|
||||
/** if key has been created */
|
||||
static int key_created = 0;
|
||||
/** we hide the thread debug info with this key. */
|
||||
static ub_thread_key_t thr_debug_key;
|
||||
/** the list of threads, so all threads can be examined. NULL if unused. */
|
||||
static struct thr_check* thread_infos[THRDEBUG_MAX_THREADS];
|
||||
|
||||
/** print pretty lock error and exit */
|
||||
static void lock_error(struct checked_lock* lock,
|
||||
const char* func, const char* file, int line, const char* err)
|
||||
{
|
||||
log_err("lock error (description follows)");
|
||||
log_err("Created at %s %s:%d", lock->create_func,
|
||||
lock->create_file, lock->create_line);
|
||||
log_err("Previously %s %s:%d", lock->holder_func,
|
||||
lock->holder_file, lock->holder_line);
|
||||
log_err("At %s %s:%d", func, file, line);
|
||||
log_err("Error for %s lock: %s",
|
||||
(lock->type==check_lock_mutex)?"mutex": (
|
||||
(lock->type==check_lock_spinlock)?"spinlock": (
|
||||
(lock->type==check_lock_rwlock)?"rwlock": "badtype")), err);
|
||||
fatal_exit("bailing out");
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain lock on debug lock structure. This could be a deadlock by the caller.
|
||||
* The debug code itself does not deadlock. Anyway, check with timeouts.
|
||||
* @param lock: on what to acquire lock.
|
||||
* @param func: user level caller identification.
|
||||
* @param file: user level caller identification.
|
||||
* @param line: user level caller identification.
|
||||
*/
|
||||
static void
|
||||
acquire_locklock(struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
struct timespec to;
|
||||
int err;
|
||||
int contend = 0;
|
||||
/* first try; inc contention counter if not immediately */
|
||||
if((err = pthread_mutex_trylock(&lock->lock))) {
|
||||
if(err==EBUSY)
|
||||
contend++;
|
||||
else fatal_exit("error in mutex_trylock: %s", strerror(err));
|
||||
}
|
||||
if(!err)
|
||||
return; /* immediate success */
|
||||
to.tv_sec = time(NULL) + CHECK_LOCK_TIMEOUT;
|
||||
to.tv_nsec = 0;
|
||||
err = pthread_mutex_timedlock(&lock->lock, &to);
|
||||
if(err) {
|
||||
log_err("in acquiring locklock: %s", strerror(err));
|
||||
lock_error(lock, func, file, line, "acquire locklock");
|
||||
}
|
||||
/* since we hold the lock, we can edit the contention_count */
|
||||
lock->contention_count += contend;
|
||||
}
|
||||
|
||||
/** add protected region */
|
||||
void
|
||||
lock_protect(void *p, void* area, size_t size)
|
||||
{
|
||||
struct checked_lock* lock = *(struct checked_lock**)p;
|
||||
struct protected_area* e = (struct protected_area*)malloc(
|
||||
sizeof(struct protected_area));
|
||||
if(!e)
|
||||
fatal_exit("lock_protect: out of memory");
|
||||
e->region = area;
|
||||
e->size = size;
|
||||
e->hold = malloc(size);
|
||||
if(!e->hold)
|
||||
fatal_exit("lock_protect: out of memory");
|
||||
memcpy(e->hold, e->region, e->size);
|
||||
|
||||
acquire_locklock(lock, __func__, __FILE__, __LINE__);
|
||||
e->next = lock->prot;
|
||||
lock->prot = e;
|
||||
LOCKRET(pthread_mutex_unlock(&lock->lock));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check protected memory region. Memory compare. Exit on error.
|
||||
* @param lock: which lock to check.
|
||||
* @param func: location we are now (when failure is detected).
|
||||
* @param file: location we are now (when failure is detected).
|
||||
* @param line: location we are now (when failure is detected).
|
||||
*/
|
||||
static void
|
||||
prot_check(struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
struct protected_area* p = lock->prot;
|
||||
while(p) {
|
||||
if(memcmp(p->hold, p->region, p->size) != 0) {
|
||||
log_hex("memory prev", p->hold, p->size);
|
||||
log_hex("memory here", p->region, p->size);
|
||||
lock_error(lock, func, file, line,
|
||||
"protected area modified");
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
/** Copy protected memory region. */
|
||||
static void
|
||||
prot_store(struct checked_lock* lock)
|
||||
{
|
||||
struct protected_area* p = lock->prot;
|
||||
while(p) {
|
||||
memcpy(p->hold, p->region, p->size);
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** alloc struct, init lock empty */
|
||||
void
|
||||
checklock_init(enum check_lock_type type, struct checked_lock** lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
struct checked_lock* e = (struct checked_lock*)calloc(1,
|
||||
sizeof(struct checked_lock));
|
||||
if(!e)
|
||||
fatal_exit("%s %s %d: out of memory", func, file, line);
|
||||
*lock = e;
|
||||
e->type = type;
|
||||
e->create_func = func;
|
||||
e->create_file = file;
|
||||
e->create_line = line;
|
||||
LOCKRET(pthread_mutex_init(&e->lock, NULL));
|
||||
switch(e->type) {
|
||||
case check_lock_mutex:
|
||||
LOCKRET(pthread_mutex_init(&e->u.mutex, NULL));
|
||||
break;
|
||||
case check_lock_spinlock:
|
||||
LOCKRET(pthread_spin_init(&e->u.spinlock, PTHREAD_PROCESS_PRIVATE));
|
||||
break;
|
||||
case check_lock_rwlock:
|
||||
LOCKRET(pthread_rwlock_init(&e->u.rwlock, NULL));
|
||||
break;
|
||||
default:
|
||||
log_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
/** delete prot items */
|
||||
static void
|
||||
prot_clear(struct checked_lock* lock)
|
||||
{
|
||||
struct protected_area* p=lock->prot, *np;
|
||||
while(p) {
|
||||
np = p->next;
|
||||
free(p->hold);
|
||||
free(p);
|
||||
p = np;
|
||||
}
|
||||
}
|
||||
|
||||
/** check if type is OK for the lock given */
|
||||
static void
|
||||
checktype(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
if(!lock)
|
||||
fatal_exit("use of null/deleted lock at %s %s:%d",
|
||||
func, file, line);
|
||||
if(type != lock->type) {
|
||||
lock_error(lock, func, file, line, "wrong lock type");
|
||||
}
|
||||
}
|
||||
|
||||
/** check if OK, free struct */
|
||||
void
|
||||
checklock_destroy(enum check_lock_type type, struct checked_lock** lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
const size_t contention_interest = 10;
|
||||
struct checked_lock* e;
|
||||
if(!lock)
|
||||
return;
|
||||
e = *lock;
|
||||
if(!e)
|
||||
return;
|
||||
checktype(type, e, func, file, line);
|
||||
|
||||
/* check if delete is OK */
|
||||
acquire_locklock(e, func, file, line);
|
||||
if(e->hold_count != 0)
|
||||
lock_error(e, func, file, line, "delete while locked.");
|
||||
if(e->wait_count != 0)
|
||||
lock_error(e, func, file, line, "delete while waited on.");
|
||||
prot_check(e, func, file, line);
|
||||
*lock = NULL; /* use after free will fail */
|
||||
LOCKRET(pthread_mutex_unlock(&e->lock));
|
||||
|
||||
/* contention */
|
||||
if(e->contention_count > contention_interest) {
|
||||
log_info("lock created %s %s %d has contention %u",
|
||||
e->create_func, e->create_file, e->create_line,
|
||||
(unsigned int)e->contention_count);
|
||||
}
|
||||
|
||||
/* delete it */
|
||||
LOCKRET(pthread_mutex_destroy(&e->lock));
|
||||
prot_clear(e);
|
||||
/* since nobody holds the lock - see check above, no need to unlink
|
||||
* from the thread-held locks list. */
|
||||
switch(e->type) {
|
||||
case check_lock_mutex:
|
||||
LOCKRET(pthread_mutex_destroy(&e->u.mutex));
|
||||
break;
|
||||
case check_lock_spinlock:
|
||||
LOCKRET(pthread_spin_destroy(&e->u.spinlock));
|
||||
break;
|
||||
case check_lock_rwlock:
|
||||
LOCKRET(pthread_rwlock_destroy(&e->u.rwlock));
|
||||
break;
|
||||
default:
|
||||
log_assert(0);
|
||||
}
|
||||
memset(e, 0, sizeof(struct checked_lock));
|
||||
free(e);
|
||||
}
|
||||
|
||||
/** finish acquiring lock, shared between _(rd|wr||)lock() routines. */
|
||||
static void
|
||||
finish_acquire_lock(struct thr_check* thr, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
thr->waiting = NULL;
|
||||
lock->wait_count --;
|
||||
lock->holder = thr;
|
||||
lock->hold_count ++;
|
||||
lock->holder_func = func;
|
||||
lock->holder_file = file;
|
||||
lock->holder_line = line;
|
||||
|
||||
/* insert in thread lock list, as first */
|
||||
lock->prev_held_lock[thr->num] = NULL;
|
||||
lock->next_held_lock[thr->num] = thr->holding_first;
|
||||
if(thr->holding_first)
|
||||
/* no need to lock it, since this thread already holds the
|
||||
* lock (since it is on this list) and we only edit thr->num
|
||||
* member in array. So it is safe. */
|
||||
thr->holding_first->prev_held_lock[thr->num] = lock;
|
||||
else thr->holding_last = lock;
|
||||
thr->holding_first = lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locking routine.
|
||||
* @param type: as passed by user.
|
||||
* @param lock: as passed by user.
|
||||
* @param func: caller location.
|
||||
* @param file: caller location.
|
||||
* @param line: caller location.
|
||||
* @param tryfunc: the pthread_mutex_trylock or similar function.
|
||||
* @param timedfunc: the pthread_mutex_timedlock or similar function.
|
||||
* Uses absolute timeout value.
|
||||
* @param arg: what to pass to tryfunc and timedlock.
|
||||
* @param exclusive: if lock must be exlusive (only one allowed).
|
||||
* @param getwr: if attempts to get writelock (or readlock) for rwlocks.
|
||||
*/
|
||||
static void
|
||||
checklock_lockit(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line,
|
||||
int (*tryfunc)(void*), int (*timedfunc)(void*, struct timespec*),
|
||||
void* arg, int exclusive, int getwr)
|
||||
{
|
||||
int err;
|
||||
int contend = 0;
|
||||
struct thr_check *thr = (struct thr_check*)pthread_getspecific(
|
||||
thr_debug_key);
|
||||
checktype(type, lock, func, file, line);
|
||||
if(!thr) lock_error(lock, func, file, line, "no thread info");
|
||||
|
||||
acquire_locklock(lock, func, file, line);
|
||||
lock->wait_count ++;
|
||||
thr->waiting = lock;
|
||||
if(exclusive && lock->hold_count > 0 && lock->holder == thr)
|
||||
lock_error(lock, func, file, line, "thread already owns lock");
|
||||
if(type==check_lock_rwlock && getwr && lock->writeholder == thr)
|
||||
lock_error(lock, func, file, line, "thread already has wrlock");
|
||||
LOCKRET(pthread_mutex_unlock(&lock->lock));
|
||||
|
||||
/* first try; if busy increase contention counter */
|
||||
if((err=tryfunc(arg))) {
|
||||
struct timespec to;
|
||||
if(err != EBUSY) log_err("trylock: %s", strerror(err));
|
||||
to.tv_sec = time(NULL) + CHECK_LOCK_TIMEOUT;
|
||||
to.tv_nsec = 0;
|
||||
if((err=timedfunc(arg, &to))) {
|
||||
if(err == ETIMEDOUT)
|
||||
lock_error(lock, func, file, line,
|
||||
"timeout possible deadlock");
|
||||
log_err("timedlock: %s", strerror(err));
|
||||
}
|
||||
contend ++;
|
||||
}
|
||||
/* got the lock */
|
||||
|
||||
acquire_locklock(lock, func, file, line);
|
||||
lock->contention_count += contend;
|
||||
if(exclusive && lock->hold_count > 0)
|
||||
lock_error(lock, func, file, line, "got nonexclusive lock");
|
||||
if(type==check_lock_rwlock && getwr && lock->writeholder)
|
||||
lock_error(lock, func, file, line, "got nonexclusive wrlock");
|
||||
if(type==check_lock_rwlock && getwr)
|
||||
lock->writeholder = thr;
|
||||
/* check the memory areas for unauthorized changes,
|
||||
* between last unlock time and current lock time.
|
||||
* we check while holding the lock (threadsafe).
|
||||
*/
|
||||
if(getwr || exclusive)
|
||||
prot_check(lock, func, file, line);
|
||||
finish_acquire_lock(thr, lock, func, file, line);
|
||||
LOCKRET(pthread_mutex_unlock(&lock->lock));
|
||||
}
|
||||
|
||||
/** helper for rdlock: try */
|
||||
static int try_rd(void* arg)
|
||||
{ return pthread_rwlock_tryrdlock((pthread_rwlock_t*)arg); }
|
||||
/** helper for rdlock: timed */
|
||||
static int timed_rd(void* arg, struct timespec* to)
|
||||
{ return pthread_rwlock_timedrdlock((pthread_rwlock_t*)arg, to); }
|
||||
|
||||
/** check if OK, lock */
|
||||
void
|
||||
checklock_rdlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
|
||||
log_assert(type == check_lock_rwlock);
|
||||
checklock_lockit(type, lock, func, file, line,
|
||||
try_rd, timed_rd, &lock->u.rwlock, 0, 0);
|
||||
}
|
||||
|
||||
/** helper for wrlock: try */
|
||||
static int try_wr(void* arg)
|
||||
{ return pthread_rwlock_trywrlock((pthread_rwlock_t*)arg); }
|
||||
/** helper for wrlock: timed */
|
||||
static int timed_wr(void* arg, struct timespec* to)
|
||||
{ return pthread_rwlock_timedwrlock((pthread_rwlock_t*)arg, to); }
|
||||
|
||||
/** check if OK, lock */
|
||||
void
|
||||
checklock_wrlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
log_assert(type == check_lock_rwlock);
|
||||
checklock_lockit(type, lock, func, file, line,
|
||||
try_wr, timed_wr, &lock->u.rwlock, 0, 1);
|
||||
}
|
||||
|
||||
/** helper for lock mutex: try */
|
||||
static int try_mutex(void* arg)
|
||||
{ return pthread_mutex_trylock((pthread_mutex_t*)arg); }
|
||||
/** helper for lock mutex: timed */
|
||||
static int timed_mutex(void* arg, struct timespec* to)
|
||||
{ return pthread_mutex_timedlock((pthread_mutex_t*)arg, to); }
|
||||
|
||||
/** helper for lock spinlock: try */
|
||||
static int try_spinlock(void* arg)
|
||||
{ return pthread_spin_trylock((pthread_spinlock_t*)arg); }
|
||||
/** helper for lock spinlock: timed */
|
||||
static int timed_spinlock(void* arg, struct timespec* to)
|
||||
{
|
||||
int err;
|
||||
/* spin for 5 seconds. (ouch for the CPU, but it beats forever) */
|
||||
while( (err=try_spinlock(arg)) == EBUSY) {
|
||||
#ifndef S_SPLINT_S
|
||||
if(time(NULL) >= to->tv_sec)
|
||||
return ETIMEDOUT;
|
||||
#endif
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/** check if OK, lock */
|
||||
void
|
||||
checklock_lock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
log_assert(type != check_lock_rwlock);
|
||||
switch(type) {
|
||||
case check_lock_mutex:
|
||||
checklock_lockit(type, lock, func, file, line,
|
||||
try_mutex, timed_mutex, &lock->u.mutex, 1, 0);
|
||||
break;
|
||||
case check_lock_spinlock:
|
||||
/* void* cast needed because 'volatile' on some OS */
|
||||
checklock_lockit(type, lock, func, file, line,
|
||||
try_spinlock, timed_spinlock,
|
||||
(void*)&lock->u.spinlock, 1, 0);
|
||||
break;
|
||||
default:
|
||||
log_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
/** check if OK, unlock */
|
||||
void
|
||||
checklock_unlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
struct thr_check *thr = (struct thr_check*)pthread_getspecific(
|
||||
thr_debug_key);
|
||||
checktype(type, lock, func, file, line);
|
||||
if(!thr) lock_error(lock, func, file, line, "no thread info");
|
||||
|
||||
acquire_locklock(lock, func, file, line);
|
||||
/* was this thread even holding this lock? */
|
||||
if(thr->holding_first != lock &&
|
||||
lock->prev_held_lock[thr->num] == NULL) {
|
||||
lock_error(lock, func, file, line, "unlock nonlocked lock");
|
||||
}
|
||||
if(lock->hold_count <= 0)
|
||||
lock_error(lock, func, file, line, "too many unlocks");
|
||||
|
||||
/* store this point as last touched by */
|
||||
lock->holder = thr;
|
||||
lock->hold_count --;
|
||||
lock->holder_func = func;
|
||||
lock->holder_file = file;
|
||||
lock->holder_line = line;
|
||||
|
||||
/* delete from thread holder list */
|
||||
/* no need to lock other lockstructs, because they are all on the
|
||||
* held-locks list, and this thread holds their locks.
|
||||
* we only touch the thr->num members, so it is safe. */
|
||||
if(thr->holding_first == lock)
|
||||
thr->holding_first = lock->next_held_lock[thr->num];
|
||||
if(thr->holding_last == lock)
|
||||
thr->holding_last = lock->prev_held_lock[thr->num];
|
||||
if(lock->next_held_lock[thr->num])
|
||||
lock->next_held_lock[thr->num]->prev_held_lock[thr->num] =
|
||||
lock->prev_held_lock[thr->num];
|
||||
if(lock->prev_held_lock[thr->num])
|
||||
lock->prev_held_lock[thr->num]->next_held_lock[thr->num] =
|
||||
lock->next_held_lock[thr->num];
|
||||
lock->next_held_lock[thr->num] = NULL;
|
||||
lock->prev_held_lock[thr->num] = NULL;
|
||||
|
||||
if(type==check_lock_rwlock && lock->writeholder == thr) {
|
||||
lock->writeholder = NULL;
|
||||
prot_store(lock);
|
||||
} else if(type != check_lock_rwlock) {
|
||||
/* store memory areas that are protected, for later checks */
|
||||
prot_store(lock);
|
||||
}
|
||||
LOCKRET(pthread_mutex_unlock(&lock->lock));
|
||||
|
||||
/* unlock it */
|
||||
switch(type) {
|
||||
case check_lock_mutex:
|
||||
LOCKRET(pthread_mutex_unlock(&lock->u.mutex));
|
||||
break;
|
||||
case check_lock_spinlock:
|
||||
LOCKRET(pthread_spin_unlock(&lock->u.spinlock));
|
||||
break;
|
||||
case check_lock_rwlock:
|
||||
LOCKRET(pthread_rwlock_unlock(&lock->u.rwlock));
|
||||
break;
|
||||
default:
|
||||
log_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
/** checklock thread main, Inits thread structure. */
|
||||
static void* checklock_main(void* arg)
|
||||
{
|
||||
struct thr_check* thr = (struct thr_check*)arg;
|
||||
void* ret;
|
||||
thr->id = pthread_self();
|
||||
/* 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);
|
||||
thread_infos[thr->num] = thr;
|
||||
LOCKRET(pthread_setspecific(thr_debug_key, thr));
|
||||
ret = thr->func(thr->arg);
|
||||
thread_infos[thr->num] = NULL;
|
||||
free(thr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** init the main thread */
|
||||
void checklock_start()
|
||||
{
|
||||
if(!key_created) {
|
||||
struct thr_check* thisthr = (struct thr_check*)calloc(1,
|
||||
sizeof(struct thr_check));
|
||||
if(!thisthr)
|
||||
fatal_exit("thrcreate: out of memory");
|
||||
key_created = 1;
|
||||
LOCKRET(pthread_key_create(&thr_debug_key, NULL));
|
||||
LOCKRET(pthread_setspecific(thr_debug_key, thisthr));
|
||||
thread_infos[0] = thisthr;
|
||||
}
|
||||
}
|
||||
|
||||
/** stop checklocks */
|
||||
void checklock_stop()
|
||||
{
|
||||
if(key_created) {
|
||||
int i;
|
||||
free(thread_infos[0]);
|
||||
thread_infos[0] = NULL;
|
||||
for(i = 0; i < THRDEBUG_MAX_THREADS; i++)
|
||||
log_assert(thread_infos[i] == NULL);
|
||||
/* should have been cleaned up. */
|
||||
LOCKRET(pthread_key_delete(thr_debug_key));
|
||||
key_created = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** allocate debug info and create thread */
|
||||
void
|
||||
checklock_thrcreate(pthread_t* id, void* (*func)(void*), void* arg)
|
||||
{
|
||||
struct thr_check* thr = (struct thr_check*)calloc(1,
|
||||
sizeof(struct thr_check));
|
||||
if(!thr)
|
||||
fatal_exit("thrcreate: out of memory");
|
||||
if(!key_created) {
|
||||
checklock_start();
|
||||
}
|
||||
thr->func = func;
|
||||
thr->arg = arg;
|
||||
LOCKRET(pthread_create(id, NULL, checklock_main, thr));
|
||||
}
|
||||
|
||||
/** signal handler for join timeout, Exits. */
|
||||
static RETSIGTYPE joinalarm(int ATTR_UNUSED(sig))
|
||||
{
|
||||
fatal_exit("join thread timeout. hangup or deadlock.");
|
||||
}
|
||||
|
||||
/** wait for thread with a timeout. */
|
||||
void
|
||||
checklock_thrjoin(pthread_t thread)
|
||||
{
|
||||
/* wait with a timeout */
|
||||
if(signal(SIGALRM, joinalarm) == SIG_ERR)
|
||||
fatal_exit("signal(): %s", strerror(errno));
|
||||
(void)alarm(CHECK_JOIN_TIMEOUT);
|
||||
LOCKRET(pthread_join(thread, NULL));
|
||||
(void)alarm(0);
|
||||
}
|
||||
|
||||
#endif /* USE_THREAD_DEBUG */
|
||||
@@ -0,0 +1,323 @@
|
||||
/**
|
||||
* testcode/checklocks.h - wrapper on locks that checks access.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TESTCODE_CHECK_LOCKS_H
|
||||
#define TESTCODE_CHECK_LOCKS_H
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Locks that are checked.
|
||||
*
|
||||
* Holds information per lock and per thread.
|
||||
* That information is protected by a mutex (unchecked).
|
||||
*
|
||||
* Checks:
|
||||
* o which func, file, line created the lock.
|
||||
* o contention count, measures amount of contention on the lock.
|
||||
* o the memory region(s) that the lock protects are
|
||||
* memcmp'ed to ascertain no race conditions.
|
||||
* o checks that locks are unlocked properly (before deletion).
|
||||
* keeps which func, file, line that locked it.
|
||||
* o checks deadlocks with timeout so it can print errors for them.
|
||||
*
|
||||
* Limitations:
|
||||
* o Detects unprotected memory access when the lock is locked or freed,
|
||||
* which detects races only if they happen, and only if in protected
|
||||
* memory areas.
|
||||
* o Detects deadlocks by timeout, so approximately, as they happen.
|
||||
* o Does not check order of locking.
|
||||
* o Uses a lot of memory.
|
||||
* o The checks use locks themselves, changing scheduling,
|
||||
* thus changing what races you see.
|
||||
*/
|
||||
|
||||
#ifdef USE_THREAD_DEBUG
|
||||
#ifndef HAVE_PTHREAD
|
||||
/* we need the *timed*lock() routines to use for deadlock detection. */
|
||||
#error "Need pthreads for checked locks"
|
||||
#endif
|
||||
/******************* THREAD DEBUG ************************/
|
||||
#include <pthread.h>
|
||||
|
||||
/** How long to wait before lock attempt is a failure. */
|
||||
#define CHECK_LOCK_TIMEOUT 5 /* seconds */
|
||||
/** How long to wait before join attempt is a failure. */
|
||||
#define CHECK_JOIN_TIMEOUT 120 /* seconds */
|
||||
/** How many threads to allocate for */
|
||||
#define THRDEBUG_MAX_THREADS 32 /* threads */
|
||||
|
||||
/**
|
||||
* Protection memory area.
|
||||
* It is copied to a holding buffer to compare against later.
|
||||
* Note that it may encompass the lock structure.
|
||||
*/
|
||||
struct protected_area {
|
||||
/** where the memory region starts */
|
||||
void* region;
|
||||
/** size of the region */
|
||||
size_t size;
|
||||
/** backbuffer that holds a copy, of same size. */
|
||||
void* hold;
|
||||
/** next protected area in list */
|
||||
struct protected_area* next;
|
||||
};
|
||||
|
||||
/**
|
||||
* Per thread information for locking debug wrappers.
|
||||
*/
|
||||
struct thr_check {
|
||||
/** thread id */
|
||||
pthread_t id;
|
||||
/** real thread func */
|
||||
void* (*func)(void*);
|
||||
/** func user arg */
|
||||
void* arg;
|
||||
/** number of thread in list structure */
|
||||
int num;
|
||||
/**
|
||||
* List of locks that this thread is holding, double
|
||||
* linked list. The first element is the most recent lock acquired.
|
||||
* So it represents the stack of locks acquired. (of all types).
|
||||
*/
|
||||
struct checked_lock *holding_first, *holding_last;
|
||||
/** if the thread is currently waiting for a lock, which one */
|
||||
struct checked_lock* waiting;
|
||||
};
|
||||
|
||||
/**
|
||||
* One structure for all types of locks.
|
||||
*/
|
||||
struct checked_lock {
|
||||
/** mutex for exclusive access to this structure */
|
||||
pthread_mutex_t lock;
|
||||
/** list of memory regions protected by this checked lock */
|
||||
struct protected_area* prot;
|
||||
/** where was this lock created */
|
||||
const char* create_func, *create_file;
|
||||
/** where was this lock created */
|
||||
int create_line;
|
||||
/** contention count */
|
||||
size_t contention_count;
|
||||
/** hold count (how many threads are holding this lock) */
|
||||
int hold_count;
|
||||
/** how many threads are waiting for this lock */
|
||||
int wait_count;
|
||||
/** who touched it last */
|
||||
const char* holder_func, *holder_file;
|
||||
/** who touched it last */
|
||||
int holder_line;
|
||||
/** who owns the lock now */
|
||||
struct thr_check* holder;
|
||||
/** for rwlocks, the writelock holder */
|
||||
struct thr_check* writeholder;
|
||||
|
||||
/** next lock a thread is holding (less recent) */
|
||||
struct checked_lock* next_held_lock[THRDEBUG_MAX_THREADS];
|
||||
/** prev lock a thread is holding (more recent) */
|
||||
struct checked_lock* prev_held_lock[THRDEBUG_MAX_THREADS];
|
||||
|
||||
/** type of lock */
|
||||
enum check_lock_type {
|
||||
/** basic mutex */
|
||||
check_lock_mutex,
|
||||
/** fast spinlock */
|
||||
check_lock_spinlock,
|
||||
/** rwlock */
|
||||
check_lock_rwlock
|
||||
} type;
|
||||
/** the lock itself, see type to disambiguate the union */
|
||||
union {
|
||||
/** mutex */
|
||||
pthread_mutex_t mutex;
|
||||
/** spinlock */
|
||||
pthread_spinlock_t spinlock;
|
||||
/** rwlock */
|
||||
pthread_rwlock_t rwlock;
|
||||
} u;
|
||||
};
|
||||
|
||||
/**
|
||||
* Additional call for the user to specify what areas are protected
|
||||
* @param lock: the lock that protects the area. It can be inside the area.
|
||||
* The lock must be inited. Call with user lock. (any type).
|
||||
* It demangles the lock itself (struct checked_lock**).
|
||||
* @param area: ptr to mem.
|
||||
* @param size: length of area.
|
||||
* You can call it multiple times with the same lock to give several areas.
|
||||
* Call it when you are done initialising the area, since it will be copied
|
||||
* at this time and protected right away against unauthorised changes until
|
||||
* the next lock() call is done.
|
||||
*/
|
||||
void lock_protect(void* lock, void* area, size_t size);
|
||||
|
||||
/**
|
||||
* Initialise checklock. Sets up internal debug structures.
|
||||
*/
|
||||
void checklock_start();
|
||||
|
||||
/**
|
||||
* Cleanup internal debug state.
|
||||
*/
|
||||
void checklock_stop();
|
||||
|
||||
/**
|
||||
* Init locks.
|
||||
* @param type: what type of lock this is.
|
||||
* @param lock: ptr to user alloced ptr structure. This is inited.
|
||||
* So an alloc is done and the ptr is stored as result.
|
||||
* @param func: caller function name.
|
||||
* @param file: caller file name.
|
||||
* @param line: caller line number.
|
||||
*/
|
||||
void checklock_init(enum check_lock_type type, struct checked_lock** lock,
|
||||
const char* func, const char* file, int line);
|
||||
|
||||
/**
|
||||
* Destroy locks. Free the structure.
|
||||
* @param type: what type of lock this is.
|
||||
* @param lock: ptr to user alloced structure. This is destroyed.
|
||||
* @param func: caller function name.
|
||||
* @param file: caller file name.
|
||||
* @param line: caller line number.
|
||||
*/
|
||||
void checklock_destroy(enum check_lock_type type, struct checked_lock** lock,
|
||||
const char* func, const char* file, int line);
|
||||
|
||||
/**
|
||||
* Acquire readlock.
|
||||
* @param type: what type of lock this is. Had better be a rwlock.
|
||||
* @param lock: ptr to lock.
|
||||
* @param func: caller function name.
|
||||
* @param file: caller file name.
|
||||
* @param line: caller line number.
|
||||
*/
|
||||
void checklock_rdlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line);
|
||||
|
||||
/**
|
||||
* Acquire writelock.
|
||||
* @param type: what type of lock this is. Had better be a rwlock.
|
||||
* @param lock: ptr to lock.
|
||||
* @param func: caller function name.
|
||||
* @param file: caller file name.
|
||||
* @param line: caller line number.
|
||||
*/
|
||||
void checklock_wrlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line);
|
||||
|
||||
/**
|
||||
* Locks.
|
||||
* @param type: what type of lock this is. Had better be mutex or spinlock.
|
||||
* @param lock: the lock.
|
||||
* @param func: caller function name.
|
||||
* @param file: caller file name.
|
||||
* @param line: caller line number.
|
||||
*/
|
||||
void checklock_lock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line);
|
||||
|
||||
/**
|
||||
* Unlocks.
|
||||
* @param type: what type of lock this is.
|
||||
* @param lock: the lock.
|
||||
* @param func: caller function name.
|
||||
* @param file: caller file name.
|
||||
* @param line: caller line number.
|
||||
*/
|
||||
void checklock_unlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line);
|
||||
|
||||
/**
|
||||
* Create thread.
|
||||
* @param thr: Thread id, where to store result.
|
||||
* @param func: thread start function.
|
||||
* @param arg: user argument.
|
||||
*/
|
||||
void checklock_thrcreate(pthread_t* thr, void* (*func)(void*), void* arg);
|
||||
|
||||
/**
|
||||
* Wait for thread to exit. Returns thread return value.
|
||||
* @param thread: thread to wait for.
|
||||
*/
|
||||
void checklock_thrjoin(pthread_t thread);
|
||||
|
||||
/** structures to enable compiler type checking on the locks.
|
||||
* Also the pointer makes it so that the lock can be part of the protected
|
||||
* region without any possible problem (since the ptr will stay the same.)
|
||||
* i.e. there can be contention and readlocks stored in checked_lock, while
|
||||
* the protected area stays the same, even though it contains (ptr to) lock.
|
||||
*/
|
||||
struct checked_lock_rw { struct checked_lock* c_rw; };
|
||||
/** structures to enable compiler type checking on the locks. */
|
||||
struct checked_lock_mutex { struct checked_lock* c_m; };
|
||||
/** structures to enable compiler type checking on the locks. */
|
||||
struct checked_lock_spl { struct checked_lock* c_spl; };
|
||||
|
||||
/** debugging rwlock */
|
||||
typedef struct checked_lock_rw lock_rw_t;
|
||||
#define lock_rw_init(lock) checklock_init(check_lock_rwlock, &((lock)->c_rw), __func__, __FILE__, __LINE__)
|
||||
#define lock_rw_destroy(lock) checklock_destroy(check_lock_rwlock, &((lock)->c_rw), __func__, __FILE__, __LINE__)
|
||||
#define lock_rw_rdlock(lock) checklock_rdlock(check_lock_rwlock, (lock)->c_rw, __func__, __FILE__, __LINE__)
|
||||
#define lock_rw_wrlock(lock) checklock_wrlock(check_lock_rwlock, (lock)->c_rw, __func__, __FILE__, __LINE__)
|
||||
#define lock_rw_unlock(lock) checklock_unlock(check_lock_rwlock, (lock)->c_rw, __func__, __FILE__, __LINE__)
|
||||
|
||||
/** debugging mutex */
|
||||
typedef struct checked_lock_mutex lock_basic_t;
|
||||
#define lock_basic_init(lock) checklock_init(check_lock_mutex, &((lock)->c_m), __func__, __FILE__, __LINE__)
|
||||
#define lock_basic_destroy(lock) checklock_destroy(check_lock_mutex, &((lock)->c_m), __func__, __FILE__, __LINE__)
|
||||
#define lock_basic_lock(lock) checklock_lock(check_lock_mutex, (lock)->c_m, __func__, __FILE__, __LINE__)
|
||||
#define lock_basic_unlock(lock) checklock_unlock(check_lock_mutex, (lock)->c_m, __func__, __FILE__, __LINE__)
|
||||
|
||||
/** debugging spinlock */
|
||||
typedef struct checked_lock_spl lock_quick_t;
|
||||
#define lock_quick_init(lock) checklock_init(check_lock_spinlock, &((lock)->c_spl), __func__, __FILE__, __LINE__)
|
||||
#define lock_quick_destroy(lock) checklock_destroy(check_lock_spinlock, &((lock)->c_spl), __func__, __FILE__, __LINE__)
|
||||
#define lock_quick_lock(lock) checklock_lock(check_lock_spinlock, (lock)->c_spl, __func__, __FILE__, __LINE__)
|
||||
#define lock_quick_unlock(lock) checklock_unlock(check_lock_spinlock, (lock)->c_spl, __func__, __FILE__, __LINE__)
|
||||
|
||||
/** we use the pthread id, our thr_check structure is kept behind the scenes */
|
||||
typedef pthread_t ub_thread_t;
|
||||
#define ub_thread_create(thr, func, arg) checklock_thrcreate(thr, func, arg)
|
||||
#define ub_thread_self() pthread_self()
|
||||
#define ub_thread_join(thread) checklock_thrjoin(thread)
|
||||
|
||||
typedef pthread_key_t ub_thread_key_t;
|
||||
#define ub_thread_key_create(key, f) LOCKRET(pthread_key_create(key, f))
|
||||
#define ub_thread_key_set(key, v) LOCKRET(pthread_setspecific(key, v))
|
||||
#define ub_thread_key_get(key) pthread_getspecific(key)
|
||||
|
||||
#endif /* USE_THREAD_DEBUG */
|
||||
|
||||
#endif /* TESTCODE_CHECK_LOCKS_H */
|
||||
+36
-6
@@ -479,10 +479,7 @@ run_scenario(struct replay_runtime* runtime)
|
||||
/*********** Dummy routines ***********/
|
||||
|
||||
struct listen_dnsport*
|
||||
listen_create(struct comm_base* base, int ATTR_UNUSED(num_ifs),
|
||||
const char* ATTR_UNUSED(ifs[]), const char* ATTR_UNUSED(port),
|
||||
int ATTR_UNUSED(do_ip4), int ATTR_UNUSED(do_ip6),
|
||||
int ATTR_UNUSED(do_udp), int ATTR_UNUSED(do_tcp),
|
||||
listen_create(struct comm_base* base, struct listen_port* ATTR_UNUSED(ports),
|
||||
size_t bufsize, comm_point_callback_t* cb, void* cb_arg)
|
||||
{
|
||||
struct replay_runtime* runtime = (struct replay_runtime*)base;
|
||||
@@ -620,7 +617,7 @@ comm_point_drop_reply(struct comm_reply* repinfo)
|
||||
|
||||
struct outside_network*
|
||||
outside_network_create(struct comm_base* base, size_t bufsize,
|
||||
size_t ATTR_UNUSED(num_ports), const char** ATTR_UNUSED(ifs),
|
||||
size_t ATTR_UNUSED(num_ports), char** ATTR_UNUSED(ifs),
|
||||
int ATTR_UNUSED(num_ifs), int ATTR_UNUSED(do_ip4),
|
||||
int ATTR_UNUSED(do_ip6), int ATTR_UNUSED(port_base))
|
||||
{
|
||||
@@ -647,7 +644,8 @@ outside_network_delete(struct outside_network* outnet)
|
||||
void
|
||||
pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
comm_point_callback_t* callback, void* callback_arg)
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
struct ub_randstate* ATTR_UNUSED(rnd))
|
||||
{
|
||||
struct replay_runtime* runtime = (struct replay_runtime*)outnet->base;
|
||||
struct fake_pending* pend = (struct fake_pending*)calloc(1,
|
||||
@@ -690,4 +688,36 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
runtime->pending_list = pend;
|
||||
}
|
||||
|
||||
struct listen_port* listening_ports_open(struct config_file* ATTR_UNUSED(cfg))
|
||||
{
|
||||
return calloc(1, 1);
|
||||
}
|
||||
|
||||
void listening_ports_free(struct listen_port* list)
|
||||
{
|
||||
free(list);
|
||||
}
|
||||
|
||||
void listen_pushback(struct listen_dnsport* ATTR_UNUSED(listen))
|
||||
{
|
||||
}
|
||||
|
||||
void listen_resume(struct listen_dnsport* ATTR_UNUSED(listen))
|
||||
{
|
||||
}
|
||||
|
||||
struct comm_point* comm_point_create_local(struct comm_base* ATTR_UNUSED(base),
|
||||
int ATTR_UNUSED(fd), size_t ATTR_UNUSED(bufsize),
|
||||
comm_point_callback_t* ATTR_UNUSED(callback),
|
||||
void* ATTR_UNUSED(callback_arg))
|
||||
{
|
||||
return calloc(1, 1);
|
||||
}
|
||||
|
||||
/* only cmd com _local gets deleted */
|
||||
void comm_point_delete(struct comm_point* c)
|
||||
{
|
||||
free(c);
|
||||
}
|
||||
|
||||
/*********** End of Dummy routines ***********/
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ function dotest()
|
||||
configure: configure.ac
|
||||
$AC_CMD
|
||||
touch configure
|
||||
Makefile: configure
|
||||
Makefile: configure Makefile.in
|
||||
./configure $CONFIGURE_FLAGS $DISABLE
|
||||
touch Makefile
|
||||
EOF
|
||||
|
||||
@@ -151,10 +151,12 @@ main(int argc, char* argv[])
|
||||
char* init_optarg = optarg;
|
||||
struct replay_scenario* scen = NULL;
|
||||
|
||||
log_init(NULL);
|
||||
log_info("Start of %s testbound program.", PACKAGE_STRING);
|
||||
/* determine commandline options for the daemon */
|
||||
pass_argc = 1;
|
||||
pass_argv[0] = "unbound";
|
||||
add_opts("-d", &pass_argc, pass_argv);
|
||||
while( (c=getopt(argc, argv, "ho:p:")) != -1) {
|
||||
switch(c) {
|
||||
case 'p':
|
||||
|
||||
@@ -47,6 +47,46 @@ int testcount = 0;
|
||||
/** test bool x, exits on failure, increases testcount. */
|
||||
#define unit_assert(x) testcount++; log_assert(x);
|
||||
|
||||
#include "util/alloc.h"
|
||||
/** test alloc code */
|
||||
static void
|
||||
alloc_test() {
|
||||
alloc_special_t *t1, *t2;
|
||||
struct alloc_cache major, minor1, minor2;
|
||||
int i;
|
||||
|
||||
checklock_start();
|
||||
alloc_init(&major, NULL);
|
||||
alloc_init(&minor1, &major);
|
||||
alloc_init(&minor2, &major);
|
||||
|
||||
t1 = alloc_special_obtain(&minor1);
|
||||
alloc_clear(&minor1);
|
||||
|
||||
alloc_special_release(&minor2, t1);
|
||||
t2 = alloc_special_obtain(&minor2);
|
||||
unit_assert( t1 == t2 ); /* reused */
|
||||
alloc_special_release(&minor2, t1);
|
||||
|
||||
for(i=0; i<100; i++) {
|
||||
t1 = alloc_special_obtain(&minor1);
|
||||
alloc_special_release(&minor2, t1);
|
||||
}
|
||||
if(0) {
|
||||
alloc_stats(&minor1);
|
||||
alloc_stats(&minor2);
|
||||
alloc_stats(&major);
|
||||
}
|
||||
/* reuse happened */
|
||||
unit_assert(minor1.num_quar + minor2.num_quar + major.num_quar == 11);
|
||||
|
||||
alloc_clear(&minor1);
|
||||
alloc_clear(&minor2);
|
||||
unit_assert(major.num_quar == 11);
|
||||
alloc_clear(&major);
|
||||
checklock_stop();
|
||||
}
|
||||
|
||||
#include "util/net_help.h"
|
||||
/** test net code */
|
||||
static void
|
||||
@@ -70,6 +110,7 @@ net_test()
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
log_init(NULL);
|
||||
if(argc != 1) {
|
||||
printf("usage: %s\n", argv[0]);
|
||||
printf("\tperforms unit tests.\n");
|
||||
@@ -77,6 +118,7 @@ main(int argc, char* argv[])
|
||||
}
|
||||
printf("Start of %s unit test.\n", PACKAGE_STRING);
|
||||
net_test();
|
||||
alloc_test();
|
||||
printf("%d tests succeeded\n", testcount);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
+191
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* util/alloc.c - memory allocation service.
|
||||
*
|
||||
* 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 memory allocation functions.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/alloc.h"
|
||||
|
||||
/** prealloc some entries in the cache. To minimize contention.
|
||||
* Result is 1 lock per alloc_max newly created entries.
|
||||
* @param alloc: the structure to fill up.
|
||||
*/
|
||||
static void
|
||||
prealloc(struct alloc_cache* alloc)
|
||||
{
|
||||
alloc_special_t* p;
|
||||
int i;
|
||||
for(i=0; i<ALLOC_SPECIAL_MAX; i++) {
|
||||
if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t))))
|
||||
fatal_exit("prealloc: out of memory");
|
||||
alloc_special_next(p) = alloc->quar;
|
||||
alloc->quar = p;
|
||||
alloc->num_quar++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
alloc_init(struct alloc_cache* alloc, struct alloc_cache* super)
|
||||
{
|
||||
memset(alloc, 0, sizeof(*alloc));
|
||||
alloc->super = super;
|
||||
if(!alloc->super) {
|
||||
lock_quick_init(&alloc->lock);
|
||||
lock_protect(&alloc->lock, alloc, sizeof(*alloc));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
alloc_clear(struct alloc_cache* alloc)
|
||||
{
|
||||
alloc_special_t* p, *np;
|
||||
if(!alloc)
|
||||
return;
|
||||
if(!alloc->super) {
|
||||
lock_quick_destroy(&alloc->lock);
|
||||
}
|
||||
if(alloc->super && alloc->quar) {
|
||||
/* push entire list into super */
|
||||
p = alloc->quar;
|
||||
while(alloc_special_next(p)) /* find last */
|
||||
p = alloc_special_next(p);
|
||||
lock_quick_lock(&alloc->super->lock);
|
||||
alloc_special_next(p) = alloc->super->quar;
|
||||
alloc->super->quar = alloc->quar;
|
||||
alloc->super->num_quar += alloc->num_quar;
|
||||
lock_quick_unlock(&alloc->super->lock);
|
||||
} else {
|
||||
/* free */
|
||||
p = alloc->quar;
|
||||
while(p) {
|
||||
np = alloc_special_next(p);
|
||||
free(p);
|
||||
p = np;
|
||||
}
|
||||
}
|
||||
alloc->quar = 0;
|
||||
alloc->num_quar = 0;
|
||||
}
|
||||
|
||||
alloc_special_t*
|
||||
alloc_special_obtain(struct alloc_cache* alloc)
|
||||
{
|
||||
alloc_special_t* p;
|
||||
log_assert(alloc);
|
||||
/* see if in local cache */
|
||||
if(alloc->quar) {
|
||||
p = alloc->quar;
|
||||
alloc->quar = alloc_special_next(p);
|
||||
alloc->num_quar--;
|
||||
alloc_special_clean(p);
|
||||
return p;
|
||||
}
|
||||
/* see if in global cache */
|
||||
if(alloc->super) {
|
||||
/* could maybe grab alloc_max/2 entries in one go,
|
||||
* but really, isn't that just as fast as this code? */
|
||||
lock_quick_lock(&alloc->super->lock);
|
||||
if((p = alloc->super->quar)) {
|
||||
alloc->super->quar = alloc_special_next(p);
|
||||
alloc->super->num_quar--;
|
||||
}
|
||||
lock_quick_unlock(&alloc->super->lock);
|
||||
if(p) {
|
||||
alloc_special_clean(p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
/* allocate new */
|
||||
prealloc(alloc);
|
||||
if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t))))
|
||||
fatal_exit("alloc_special_obtain: out of memory");
|
||||
alloc_special_clean(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
/** push mem and some more items to the super */
|
||||
static void
|
||||
pushintosuper(struct alloc_cache* alloc, alloc_special_t* mem)
|
||||
{
|
||||
int i;
|
||||
alloc_special_t *p = alloc->quar;
|
||||
log_assert(p);
|
||||
log_assert(alloc && alloc->super &&
|
||||
alloc->num_quar >= ALLOC_SPECIAL_MAX);
|
||||
/* push ALLOC_SPECIAL_MAX/2 after mem */
|
||||
alloc_special_next(mem) = alloc->quar;
|
||||
for(i=1; i<ALLOC_SPECIAL_MAX/2; i++) {
|
||||
p = alloc_special_next(p);
|
||||
}
|
||||
alloc->quar = alloc_special_next(p);
|
||||
alloc->num_quar -= ALLOC_SPECIAL_MAX/2;
|
||||
|
||||
/* dump mem+list into the super quar list */
|
||||
lock_quick_lock(&alloc->super->lock);
|
||||
alloc_special_next(p) = alloc->super->quar;
|
||||
alloc->super->quar = mem;
|
||||
alloc->super->num_quar += ALLOC_SPECIAL_MAX/2 + 1;
|
||||
lock_quick_unlock(&alloc->super->lock);
|
||||
/* so 1 lock per mem+alloc/2 deletes */
|
||||
}
|
||||
|
||||
void
|
||||
alloc_special_release(struct alloc_cache* alloc, alloc_special_t* mem)
|
||||
{
|
||||
log_assert(alloc);
|
||||
if(!mem)
|
||||
return;
|
||||
alloc_special_clean(mem);
|
||||
if(alloc->super && alloc->num_quar >= ALLOC_SPECIAL_MAX) {
|
||||
/* push it to the super structure */
|
||||
pushintosuper(alloc, mem);
|
||||
return;
|
||||
}
|
||||
|
||||
alloc_special_next(mem) = alloc->quar;
|
||||
alloc->quar = mem;
|
||||
alloc->num_quar++;
|
||||
}
|
||||
|
||||
void
|
||||
alloc_stats(struct alloc_cache* alloc)
|
||||
{
|
||||
log_info("%salloc: %d in cache.", alloc->super?"":"sup",
|
||||
(int)alloc->num_quar);
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* util/alloc.h - memory allocation service.
|
||||
*
|
||||
* 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 memory allocation functions.
|
||||
*
|
||||
* The reasons for this service are:
|
||||
* o Avoid locking costs of getting global lock to call malloc().
|
||||
* o The packed rrset type needs to be kept on special freelists,
|
||||
* so that they are reused for other packet rrset allocations.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef UTIL_ALLOC_H
|
||||
#define UTIL_ALLOC_H
|
||||
|
||||
#include "util/locks.h"
|
||||
|
||||
/** The special type, packed rrset. Not allowed to be used for other memory */
|
||||
typedef uint64_t alloc_special_t;
|
||||
/** clean the special type. Pass pointer. */
|
||||
#define alloc_special_clean(x) memset(x, 0, sizeof(alloc_special_t))
|
||||
/** access next pointer. (in available spot). Pass pointer. */
|
||||
#define alloc_special_next(x) (*((alloc_special_t**)(x)))
|
||||
|
||||
/** how many blocks to cache locally. */
|
||||
#define ALLOC_SPECIAL_MAX 10
|
||||
|
||||
/**
|
||||
* Structure that provides allocation. Use one per thread.
|
||||
* The one on top has a NULL super pointer.
|
||||
*/
|
||||
struct alloc_cache {
|
||||
/** lock, only used for the super. */
|
||||
lock_quick_t lock;
|
||||
/** global allocator above this one. NULL for none (malloc/free) */
|
||||
struct alloc_cache* super;
|
||||
/** singly linked lists of special type. These are free for use. */
|
||||
alloc_special_t* quar;
|
||||
/** number of items in quarantine. */
|
||||
size_t num_quar;
|
||||
};
|
||||
|
||||
/**
|
||||
* Init alloc (zeroes the struct).
|
||||
* @param alloc: this parameter is allocated by the caller.
|
||||
* @param super: super to use (init that before with super_init).
|
||||
* Pass this argument NULL to init the toplevel alloc structure.
|
||||
*/
|
||||
void alloc_init(struct alloc_cache* alloc, struct alloc_cache* super);
|
||||
|
||||
/**
|
||||
* Free the alloc. Pushes all the cached items into the super structure.
|
||||
* Or deletes them if alloc->super is NULL.
|
||||
* Does not free the alloc struct itself (it was also allocated by caller).
|
||||
* @param alloc: is almost zeroed on exit (except some stats).
|
||||
*/
|
||||
void alloc_clear(struct alloc_cache* alloc);
|
||||
|
||||
/**
|
||||
* Get a new special_t element.
|
||||
* @param alloc: where to alloc it.
|
||||
* @return: memory block. Will not return NULL (instead fatal_exit).
|
||||
* The block is zeroed.
|
||||
*/
|
||||
alloc_special_t* alloc_special_obtain(struct alloc_cache* alloc);
|
||||
|
||||
/**
|
||||
* Return special_t back to pool.
|
||||
* The block is cleaned up (zeroed) which also invalidates the ID inside.
|
||||
* @param alloc: where to alloc it.
|
||||
* @param mem: block to free.
|
||||
*/
|
||||
void alloc_special_release(struct alloc_cache* alloc, alloc_special_t* mem);
|
||||
|
||||
/**
|
||||
* Print debug information (statistics).
|
||||
* @param alloc: on what alloc.
|
||||
*/
|
||||
void alloc_stats(struct alloc_cache* alloc);
|
||||
|
||||
#endif /* UTIL_ALLOC_H */
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* util/config_file.c - reads and stores the config file for unbound.
|
||||
*
|
||||
* 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 for the config file.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
|
||||
#include "util/configyyrename.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/configparser.h"
|
||||
/** global config during parsing */
|
||||
struct config_parser_state* cfg_parser = 0;
|
||||
/** lex in file */
|
||||
extern FILE* ub_c_in;
|
||||
/** lex out file */
|
||||
extern FILE* ub_c_out;
|
||||
/** the yacc lex generated parse function */
|
||||
int ub_c_parse(void);
|
||||
/** the lexer function */
|
||||
int ub_c_lex(void);
|
||||
/** wrap function */
|
||||
int ub_c_wrap(void);
|
||||
/** print error with file and line number */
|
||||
void ub_c_error(const char *message);
|
||||
|
||||
struct config_file*
|
||||
config_create()
|
||||
{
|
||||
struct config_file* cfg;
|
||||
cfg = (struct config_file*)calloc(1, sizeof(struct config_file));
|
||||
if(!cfg)
|
||||
return NULL;
|
||||
/* the defaults if no config is present */
|
||||
cfg->verbosity = 1;
|
||||
cfg->num_threads = 1;
|
||||
cfg->port = UNBOUND_DNS_PORT;
|
||||
cfg->do_ip4 = 1;
|
||||
cfg->do_ip6 = 1;
|
||||
cfg->do_udp = 1;
|
||||
cfg->do_tcp = 1;
|
||||
cfg->outgoing_base_port = cfg->port + 1000;
|
||||
cfg->outgoing_num_ports = 16;
|
||||
if(!(cfg->fwd_address = strdup(""))) {config_delete(cfg); return NULL;}
|
||||
if(!(cfg->username = strdup(""))) {config_delete(cfg); return NULL;}
|
||||
if(!(cfg->chrootdir = strdup(""))) {config_delete(cfg); return NULL;}
|
||||
if(!(cfg->directory = strdup("/etc/unbound"))) {config_delete(cfg); return NULL;}
|
||||
if(!(cfg->logfile = strdup(""))) {config_delete(cfg); return NULL;}
|
||||
if(!(cfg->pidfile = strdup("unbound.pid"))) {config_delete(cfg); return NULL;}
|
||||
cfg->fwd_port = UNBOUND_DNS_PORT;
|
||||
cfg->do_daemonize = 1;
|
||||
cfg->num_ifs = 0;
|
||||
cfg->ifs = NULL;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
/** initialize the global cfg_parser object. */
|
||||
static void
|
||||
create_cfg_parser(struct config_file* cfg, char* filename)
|
||||
{
|
||||
static struct config_parser_state st;
|
||||
cfg_parser = &st;
|
||||
cfg_parser->filename = filename;
|
||||
cfg_parser->line = 1;
|
||||
cfg_parser->errors = 0;
|
||||
cfg_parser->cfg = cfg;
|
||||
cfg_parser->server_settings_seen = 0;
|
||||
}
|
||||
|
||||
int
|
||||
config_read(struct config_file* cfg, const char* filename)
|
||||
{
|
||||
FILE *in;
|
||||
if(!filename)
|
||||
return 1;
|
||||
in = fopen(filename, "r");
|
||||
if(!in) {
|
||||
log_err("Could not open %s: %s", filename, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
create_cfg_parser(cfg, (char*)filename);
|
||||
ub_c_in = in;
|
||||
ub_c_parse();
|
||||
fclose(in);
|
||||
|
||||
if(cfg_parser->errors != 0) {
|
||||
fprintf(stderr, "read %s failed: %d errors in configuration file\n",
|
||||
cfg_parser->filename, cfg_parser->errors);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
config_delete(struct config_file* cfg)
|
||||
{
|
||||
if(!cfg) return;
|
||||
free(cfg->fwd_address);
|
||||
free(cfg->username);
|
||||
free(cfg->chrootdir);
|
||||
free(cfg->directory);
|
||||
free(cfg->logfile);
|
||||
free(cfg->pidfile);
|
||||
if(cfg->ifs) {
|
||||
int i;
|
||||
for(i=0; i<cfg->num_ifs; i++)
|
||||
free(cfg->ifs[i]);
|
||||
free(cfg->ifs);
|
||||
}
|
||||
free(cfg);
|
||||
}
|
||||
|
||||
/** print error with file and line number */
|
||||
void ub_c_error_va_list(const char *fmt, va_list args)
|
||||
{
|
||||
cfg_parser->errors++;
|
||||
fprintf(stderr, "%s:%d: error: ", cfg_parser->filename,
|
||||
cfg_parser->line);
|
||||
vfprintf(stderr, fmt, args);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
/** print error with file and line number */
|
||||
void ub_c_error_msg(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
ub_c_error_va_list(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void ub_c_error(const char *str)
|
||||
{
|
||||
cfg_parser->errors++;
|
||||
fprintf(stderr, "%s:%d: error: %s\n", cfg_parser->filename,
|
||||
cfg_parser->line, str);
|
||||
}
|
||||
|
||||
int ub_c_wrap()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* util/config_file.h - reads and stores the config file for unbound.
|
||||
*
|
||||
* 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 for the config file.
|
||||
*/
|
||||
|
||||
#ifndef UTIL_CONFIG_FILE_H
|
||||
#define UTIL_CONFIG_FILE_H
|
||||
|
||||
/**
|
||||
* The configuration options.
|
||||
* Strings are malloced.
|
||||
*/
|
||||
struct config_file {
|
||||
/** verbosity level as specified in the config file */
|
||||
int verbosity;
|
||||
|
||||
/** number of threads to create */
|
||||
int num_threads;
|
||||
|
||||
/** port on which queries are answered. */
|
||||
int port;
|
||||
/** do ip4 query support. */
|
||||
int do_ip4;
|
||||
/** do ip6 query support. */
|
||||
int do_ip6;
|
||||
/** do udp query support. */
|
||||
int do_udp;
|
||||
/** do tcp query support. */
|
||||
int do_tcp;
|
||||
|
||||
/** outgoing port range base number */
|
||||
int outgoing_base_port;
|
||||
/** outgoing port range number of ports (per thread, per if) */
|
||||
int outgoing_num_ports;
|
||||
|
||||
/** forwarder address. string. If not NULL fwder mode is enabled. */
|
||||
char* fwd_address;
|
||||
/** forwarder port */
|
||||
int fwd_port;
|
||||
|
||||
/** number of interfaces to open. If 0 default all interfaces. */
|
||||
int num_ifs;
|
||||
/** interface description strings (IP addresses) */
|
||||
char **ifs;
|
||||
|
||||
/** chrootdir, if not "" or chroot will be done */
|
||||
char* chrootdir;
|
||||
/** username to change to, if not "". */
|
||||
char* username;
|
||||
/** working directory */
|
||||
char* directory;
|
||||
/** filename to log to. */
|
||||
char* logfile;
|
||||
/** pidfile to write pid to. */
|
||||
char* pidfile;
|
||||
|
||||
/** daemonize, i.e. fork into the background. */
|
||||
int do_daemonize;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create config file structure. Filled with default values.
|
||||
* @return: the new structure or NULL on memory error.
|
||||
*/
|
||||
struct config_file* config_create();
|
||||
|
||||
/**
|
||||
* Read the config file from the specified filename.
|
||||
* @param config: where options are stored into, must be freshly created.
|
||||
* @param filename: name of configfile. If NULL nothing is done.
|
||||
* @return: false on error.
|
||||
*/
|
||||
int config_read(struct config_file* config, const char* filename);
|
||||
|
||||
/**
|
||||
* Destroy the config file structure.
|
||||
* @param config: to delete.
|
||||
*/
|
||||
void config_delete(struct config_file* config);
|
||||
|
||||
/**
|
||||
* Used during options parsing
|
||||
*/
|
||||
struct config_parser_state {
|
||||
/** name of file being parser */
|
||||
char* filename;
|
||||
/** line number in the file, starts at 1 */
|
||||
int line;
|
||||
/** number of errors encountered */
|
||||
int errors;
|
||||
/** the result of parsing is stored here. */
|
||||
struct config_file* cfg;
|
||||
/** has server: already been seen. */
|
||||
int server_settings_seen;
|
||||
};
|
||||
|
||||
/** global config parser object used during config parsing */
|
||||
extern struct config_parser_state* cfg_parser;
|
||||
/** parsing helpers: print error with file and line numbers. */
|
||||
void ub_c_error(const char* msg);
|
||||
/** parsing helpers: print error with file and line numbers. */
|
||||
void ub_c_error_msg(const char* fmt, ...) ATTR_FORMAT(printf, 1, 2);
|
||||
|
||||
#endif /* UTIL_CONFIG_FILE_H */
|
||||
@@ -0,0 +1,176 @@
|
||||
%{
|
||||
/*
|
||||
* configlexer.lex - lexical analyzer for unbound config file
|
||||
*
|
||||
* Copyright (c) 2001-2006, NLnet Labs. All rights reserved
|
||||
*
|
||||
* See LICENSE for the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "util/configyyrename.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/configparser.h"
|
||||
void ub_c_error(const char *message);
|
||||
|
||||
#define YY_NO_UNPUT
|
||||
|
||||
#if 0
|
||||
#define LEXOUT(s) printf s /* used ONLY when debugging */
|
||||
#else
|
||||
#define LEXOUT(s)
|
||||
#endif
|
||||
|
||||
struct inc_state {
|
||||
char* filename;
|
||||
int line;
|
||||
};
|
||||
static struct inc_state parse_stack[MAXINCLUDES];
|
||||
static YY_BUFFER_STATE include_stack[MAXINCLUDES];
|
||||
static int config_include_stack_ptr = 0;
|
||||
|
||||
static void config_start_include(const char* filename)
|
||||
{
|
||||
FILE *input;
|
||||
if(strlen(filename) == 0) {
|
||||
ub_c_error_msg("empty include file name");
|
||||
return;
|
||||
}
|
||||
if(config_include_stack_ptr >= MAXINCLUDES) {
|
||||
ub_c_error_msg("includes nested too deeply, skipped (>%d)", MAXINCLUDES);
|
||||
return;
|
||||
}
|
||||
input = fopen(filename, "r");
|
||||
if(!input) {
|
||||
ub_c_error_msg("cannot open include file '%s': %s",
|
||||
filename, strerror(errno));
|
||||
return;
|
||||
}
|
||||
LEXOUT(("switch_to_include_file(%s) ", filename));
|
||||
parse_stack[config_include_stack_ptr].filename = cfg_parser->filename;
|
||||
parse_stack[config_include_stack_ptr].line = cfg_parser->line;
|
||||
include_stack[config_include_stack_ptr] = YY_CURRENT_BUFFER;
|
||||
cfg_parser->filename = strdup(filename);
|
||||
cfg_parser->line = 1;
|
||||
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
|
||||
++config_include_stack_ptr;
|
||||
}
|
||||
|
||||
static void config_end_include(void)
|
||||
{
|
||||
--config_include_stack_ptr;
|
||||
free(cfg_parser->filename);
|
||||
cfg_parser->filename = parse_stack[config_include_stack_ptr].filename;
|
||||
cfg_parser->line = parse_stack[config_include_stack_ptr].line;
|
||||
yy_delete_buffer(YY_CURRENT_BUFFER);
|
||||
yy_switch_to_buffer(include_stack[config_include_stack_ptr]);
|
||||
}
|
||||
|
||||
#ifndef yy_set_bol /* compat definition, for flex 2.4.6 */
|
||||
#define yy_set_bol(at_bol) \
|
||||
{ \
|
||||
if ( ! yy_current_buffer ) \
|
||||
yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
|
||||
yy_current_buffer->yy_ch_buf[0] = ((at_bol)?'\n':' '); \
|
||||
}
|
||||
#endif
|
||||
|
||||
%}
|
||||
|
||||
SPACE [ \t]
|
||||
LETTER [a-zA-Z]
|
||||
UNQUOTEDLETTER [^\"\n\r \t\\]|\\.
|
||||
NEWLINE [\r\n]
|
||||
COMMENT \#
|
||||
COLON \:
|
||||
ANY [^\"\n\r\\]|\\.
|
||||
|
||||
%x quotedstring include include_quoted
|
||||
|
||||
%%
|
||||
{SPACE}* { LEXOUT(("SP ")); /* ignore */ }
|
||||
{SPACE}*{COMMENT}.* { LEXOUT(("comment(%s) ", yytext)); /* ignore */ }
|
||||
server{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SERVER;}
|
||||
num-threads{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NUM_THREADS;}
|
||||
verbosity{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_VERBOSITY;}
|
||||
port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PORT;}
|
||||
outgoing-port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_OUTGOING_PORT;}
|
||||
outgoing-range{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_OUTGOING_RANGE;}
|
||||
do-ip4{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DO_IP4;}
|
||||
do-ip6{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DO_IP6;}
|
||||
do-udp{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DO_UDP;}
|
||||
do-tcp{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DO_TCP;}
|
||||
forward-to{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_FORWARD_TO;}
|
||||
forward-to-port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_FORWARD_TO_PORT;}
|
||||
interface{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_INTERFACE;}
|
||||
chroot{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CHROOT;}
|
||||
username{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_USERNAME;}
|
||||
directory{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DIRECTORY;}
|
||||
logfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_LOGFILE;}
|
||||
pidfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PIDFILE;}
|
||||
{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;}
|
||||
|
||||
/* Quoted strings. Strip leading and ending quotes */
|
||||
\" { BEGIN(quotedstring); LEXOUT(("QS ")); }
|
||||
<quotedstring><<EOF>> {
|
||||
yyerror("EOF inside quoted string");
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
<quotedstring>{ANY}* { LEXOUT(("STR(%s) ", yytext)); yymore(); }
|
||||
<quotedstring>\n { cfg_parser->line++; yymore(); }
|
||||
<quotedstring>\" {
|
||||
LEXOUT(("QE "));
|
||||
BEGIN(INITIAL);
|
||||
yytext[yyleng - 1] = '\0';
|
||||
yylval.str = strdup(yytext);
|
||||
if(!yylval.str)
|
||||
yyerror("out of memory");
|
||||
return STRING;
|
||||
}
|
||||
|
||||
/* include: directive */
|
||||
include{COLON} { LEXOUT(("v(%s) ", yytext)); BEGIN(include); }
|
||||
<include><<EOF>> {
|
||||
yyerror("EOF inside include directive");
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
<include>{SPACE}* { LEXOUT(("ISP ")); /* ignore */ }
|
||||
<include>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;}
|
||||
<include>\" { LEXOUT(("IQS ")); BEGIN(include_quoted); }
|
||||
<include>{UNQUOTEDLETTER}* {
|
||||
LEXOUT(("Iunquotedstr(%s) ", yytext));
|
||||
config_start_include(yytext);
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
<include_quoted><<EOF>> {
|
||||
yyerror("EOF inside quoted string");
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
<include_quoted>{ANY}* { LEXOUT(("ISTR(%s) ", yytext)); yymore(); }
|
||||
<include_quoted>{NEWLINE} { cfg_parser->line++; yymore(); }
|
||||
<include_quoted>\" {
|
||||
LEXOUT(("IQE "));
|
||||
yytext[yyleng - 1] = '\0';
|
||||
config_start_include(yytext);
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
<INITIAL><<EOF>> {
|
||||
yy_set_bol(1); /* Set beginning of line, so "^" rules match. */
|
||||
if (config_include_stack_ptr == 0) {
|
||||
yyterminate();
|
||||
} else {
|
||||
fclose(yyin);
|
||||
config_end_include();
|
||||
}
|
||||
}
|
||||
|
||||
{UNQUOTEDLETTER}* { LEXOUT(("unquotedstr(%s) ", yytext));
|
||||
yylval.str = strdup(yytext); return STRING; }
|
||||
|
||||
%%
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* configparser.y -- yacc grammar for unbound configuration files
|
||||
*
|
||||
* Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
%{
|
||||
#include "config.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "util/configyyrename.h"
|
||||
#include "util/config_file.h"
|
||||
|
||||
int ub_c_lex(void);
|
||||
void ub_c_error(const char *message);
|
||||
|
||||
/* these need to be global, otherwise they cannot be used inside yacc */
|
||||
extern struct config_parser_state* cfg_parser;
|
||||
|
||||
#if 0
|
||||
#define OUTYY(s) printf s /* used ONLY when debugging */
|
||||
#else
|
||||
#define OUTYY(s)
|
||||
#endif
|
||||
|
||||
%}
|
||||
%union {
|
||||
char* str;
|
||||
}
|
||||
|
||||
%token SPACE LETTER NEWLINE COMMENT COLON ANY ZONESTR
|
||||
%token <str> STRING
|
||||
%token VAR_SERVER VAR_VERBOSITY VAR_NUM_THREADS VAR_PORT
|
||||
%token VAR_OUTGOING_PORT VAR_OUTGOING_RANGE VAR_INTERFACE
|
||||
%token VAR_DO_IP4 VAR_DO_IP6 VAR_DO_UDP VAR_DO_TCP
|
||||
%token VAR_FORWARD_TO VAR_FORWARD_TO_PORT VAR_CHROOT
|
||||
%token VAR_USERNAME VAR_DIRECTORY VAR_LOGFILE VAR_PIDFILE
|
||||
|
||||
%%
|
||||
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
|
||||
toplevelvar: serverstart contents_server ;
|
||||
|
||||
/* server: declaration */
|
||||
serverstart: VAR_SERVER
|
||||
{ OUTYY(("\nP(server:)\n"));
|
||||
if(cfg_parser->server_settings_seen) {
|
||||
yyerror("duplicate server: element.");
|
||||
}
|
||||
cfg_parser->server_settings_seen = 1;
|
||||
}
|
||||
;
|
||||
contents_server: contents_server content_server | ;
|
||||
content_server: server_num_threads | server_verbosity | server_port |
|
||||
server_outgoing_port | server_outgoing_range | server_do_ip4 |
|
||||
server_do_ip6 | server_do_udp | server_do_tcp | server_forward_to |
|
||||
server_forward_to_port | server_interface | server_chroot |
|
||||
server_username | server_directory | server_logfile | server_pidfile;
|
||||
server_num_threads: VAR_NUM_THREADS STRING
|
||||
{
|
||||
OUTYY(("P(server_num_threads:%s)\n", $2));
|
||||
if(atoi($2) == 0 && strcmp($2, "0") != 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->num_threads = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_verbosity: VAR_VERBOSITY STRING
|
||||
{
|
||||
OUTYY(("P(server_verbosity:%s)\n", $2));
|
||||
if(atoi($2) == 0 && strcmp($2, "0") != 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->verbosity = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_port: VAR_PORT STRING
|
||||
{
|
||||
OUTYY(("P(server_port:%s)\n", $2));
|
||||
if(atoi($2) == 0)
|
||||
yyerror("port number expected");
|
||||
else cfg_parser->cfg->port = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_interface: VAR_INTERFACE STRING
|
||||
{
|
||||
OUTYY(("P(server_interface:%s)\n", $2));
|
||||
if(cfg_parser->cfg->num_ifs == 0)
|
||||
cfg_parser->cfg->ifs = calloc(1, sizeof(char*));
|
||||
else cfg_parser->cfg->ifs = realloc(cfg_parser->cfg->ifs,
|
||||
(cfg_parser->cfg->num_ifs+1)*sizeof(char*));
|
||||
if(!cfg_parser->cfg->ifs)
|
||||
yyerror("out of memory");
|
||||
else
|
||||
cfg_parser->cfg->ifs[cfg_parser->cfg->num_ifs++] = $2;
|
||||
}
|
||||
;
|
||||
server_outgoing_port: VAR_OUTGOING_PORT STRING
|
||||
{
|
||||
OUTYY(("P(server_outgoing_port:%s)\n", $2));
|
||||
if(atoi($2) == 0)
|
||||
yyerror("port number expected");
|
||||
else cfg_parser->cfg->outgoing_base_port = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_outgoing_range: VAR_OUTGOING_RANGE STRING
|
||||
{
|
||||
OUTYY(("P(server_outgoing_range:%s)\n", $2));
|
||||
if(atoi($2) == 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->outgoing_num_ports = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_do_ip4: VAR_DO_IP4 STRING
|
||||
{
|
||||
OUTYY(("P(server_do_ip4:%s)\n", $2));
|
||||
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
|
||||
yyerror("expected yes or no.");
|
||||
else cfg_parser->cfg->do_ip4 = (strcmp($2, "yes")==0);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_do_ip6: VAR_DO_IP6 STRING
|
||||
{
|
||||
OUTYY(("P(server_do_ip6:%s)\n", $2));
|
||||
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
|
||||
yyerror("expected yes or no.");
|
||||
else cfg_parser->cfg->do_ip6 = (strcmp($2, "yes")==0);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_do_udp: VAR_DO_UDP STRING
|
||||
{
|
||||
OUTYY(("P(server_do_udp:%s)\n", $2));
|
||||
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
|
||||
yyerror("expected yes or no.");
|
||||
else cfg_parser->cfg->do_udp = (strcmp($2, "yes")==0);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_do_tcp: VAR_DO_TCP STRING
|
||||
{
|
||||
OUTYY(("P(server_do_tcp:%s)\n", $2));
|
||||
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
|
||||
yyerror("expected yes or no.");
|
||||
else cfg_parser->cfg->do_tcp = (strcmp($2, "yes")==0);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_forward_to: VAR_FORWARD_TO STRING
|
||||
{
|
||||
OUTYY(("P(server_forward_to:%s)\n", $2));
|
||||
free(cfg_parser->cfg->fwd_address);
|
||||
cfg_parser->cfg->fwd_address = $2;
|
||||
}
|
||||
;
|
||||
server_forward_to_port: VAR_FORWARD_TO_PORT STRING
|
||||
{
|
||||
OUTYY(("P(server_forward_to_port:%s)\n", $2));
|
||||
if(atoi($2) == 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->fwd_port = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_chroot: VAR_CHROOT STRING
|
||||
{
|
||||
OUTYY(("P(server_chroot:%s)\n", $2));
|
||||
free(cfg_parser->cfg->chrootdir);
|
||||
cfg_parser->cfg->chrootdir = $2;
|
||||
}
|
||||
;
|
||||
server_username: VAR_USERNAME STRING
|
||||
{
|
||||
OUTYY(("P(server_username:%s)\n", $2));
|
||||
free(cfg_parser->cfg->username);
|
||||
cfg_parser->cfg->username = $2;
|
||||
}
|
||||
;
|
||||
server_directory: VAR_DIRECTORY STRING
|
||||
{
|
||||
OUTYY(("P(server_directory:%s)\n", $2));
|
||||
free(cfg_parser->cfg->directory);
|
||||
cfg_parser->cfg->directory = $2;
|
||||
}
|
||||
;
|
||||
server_logfile: VAR_LOGFILE STRING
|
||||
{
|
||||
OUTYY(("P(server_logfile:%s)\n", $2));
|
||||
free(cfg_parser->cfg->logfile);
|
||||
cfg_parser->cfg->logfile = $2;
|
||||
}
|
||||
;
|
||||
server_pidfile: VAR_PIDFILE STRING
|
||||
{
|
||||
OUTYY(("P(server_pidfile:%s)\n", $2));
|
||||
free(cfg_parser->cfg->pidfile);
|
||||
cfg_parser->cfg->pidfile = $2;
|
||||
}
|
||||
;
|
||||
%%
|
||||
|
||||
/* parse helper routines could be here */
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* configyyrename.h -- renames for config file yy values to avoid conflicts.
|
||||
*
|
||||
* Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* See LICENSE for the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef UTIL_CONFIGYYRENAME_H
|
||||
#define UTIL_CONFIGYYRENAME_H
|
||||
#include "config.h"
|
||||
|
||||
/* defines to change symbols so that no yacc/lex symbols clash */
|
||||
#define yymaxdepth ub_c_maxdepth
|
||||
#define yyparse ub_c_parse
|
||||
#define yylex ub_c_lex
|
||||
#define yyerror ub_c_error
|
||||
#define yylval ub_c_lval
|
||||
#define yychar ub_c_char
|
||||
#define yydebug ub_c_debug
|
||||
#define yypact ub_c_pact
|
||||
#define yyr1 ub_c_r1
|
||||
#define yyr2 ub_c_r2
|
||||
#define yydef ub_c_def
|
||||
#define yychk ub_c_chk
|
||||
#define yypgo ub_c_pgo
|
||||
#define yyact ub_c_act
|
||||
#define yyexca ub_c_exca
|
||||
#define yyerrflag ub_c_errflag
|
||||
#define yynerrs ub_c_nerrs
|
||||
#define yyps ub_c_ps
|
||||
#define yypv ub_c_pv
|
||||
#define yys ub_c_s
|
||||
#define yy_yys ub_c_yys
|
||||
#define yystate ub_c_state
|
||||
#define yytmp ub_c_tmp
|
||||
#define yyv ub_c_v
|
||||
#define yy_yyv ub_c_yyv
|
||||
#define yyval ub_c_val
|
||||
#define yylloc ub_c_lloc
|
||||
#define yyreds ub_c_reds
|
||||
#define yytoks ub_c_toks
|
||||
#define yylhs ub_c_yylhs
|
||||
#define yylen ub_c_yylen
|
||||
#define yydefred ub_c_yydefred
|
||||
#define yydgoto ub_c_yydgoto
|
||||
#define yysindex ub_c_yysindex
|
||||
#define yyrindex ub_c_yyrindex
|
||||
#define yygindex ub_c_yygindex
|
||||
#define yytable ub_c_yytable
|
||||
#define yycheck ub_c_yycheck
|
||||
#define yyname ub_c_yyname
|
||||
#define yyrule ub_c_yyrule
|
||||
#define yyin ub_c_in
|
||||
#define yyout ub_c_out
|
||||
#define yywrap ub_c_wrap
|
||||
#define yy_load_buffer_state ub_c_load_buffer_state
|
||||
#define yy_switch_to_buffer ub_c_switch_to_buffer
|
||||
#define yy_flush_buffer ub_c_flush_buffer
|
||||
#define yy_init_buffer ub_c_init_buffer
|
||||
#define yy_scan_buffer ub_c_scan_buffer
|
||||
#define yy_scan_bytes ub_c_scan_bytes
|
||||
#define yy_scan_string ub_c_scan_string
|
||||
#define yy_create_buffer ub_c_create_buffer
|
||||
#define yyrestart ub_c_restart
|
||||
#define yy_delete_buffer ub_c_delete_buffer
|
||||
#define yypop_buffer_state ub_c_pop_buffer_state
|
||||
#define yypush_buffer_state ub_c_push_buffer_state
|
||||
#define yyunput ub_c_unput
|
||||
#define yyset_in ub_c_set_in
|
||||
#define yyget_in ub_c_get_in
|
||||
#define yyset_out ub_c_set_out
|
||||
#define yyget_out ub_c_get_out
|
||||
#define yyget_lineno ub_c_get_lineno
|
||||
#define yyset_lineno ub_c_set_lineno
|
||||
#define yyset_debug ub_c_set_debug
|
||||
#define yyget_debug ub_c_get_debug
|
||||
#define yy_flex_debug ub_c_flex_debug
|
||||
#define yylex_destroy ub_c_lex_destroy
|
||||
#define yyfree ub_c_free
|
||||
#define yyrealloc ub_c_realloc
|
||||
#define yyalloc ub_c_alloc
|
||||
#define yymalloc ub_c_malloc
|
||||
#define yyget_leng ub_c_get_leng
|
||||
#define yylineno ub_c_lineno
|
||||
#define yyget_text ub_c_get_text
|
||||
|
||||
#endif /* UTIL_CONFIGYYRENAME_H */
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* util/locks.c - unbound locking primitives
|
||||
*
|
||||
* 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
|
||||
* Implementation of locking and threading support.
|
||||
* A place for locking debug code since most locking functions are macros.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/locks.h"
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
/** block all signals, masks them away. */
|
||||
void
|
||||
ub_thread_blocksigs()
|
||||
{
|
||||
int err;
|
||||
sigset_t sigset;
|
||||
sigfillset(&sigset);
|
||||
#ifdef HAVE_PTHREAD
|
||||
if((err=pthread_sigmask(SIG_SETMASK, &sigset, NULL)))
|
||||
fatal_exit("pthread_sigmask: %s", strerror(err));
|
||||
#else
|
||||
# ifdef HAVE_SOLARIS_THREADS
|
||||
if((err=thr_sigsetmask(SIG_SETMASK, &sigset, NULL)))
|
||||
fatal_exit("thr_sigsetmask: %s", strerror(err));
|
||||
# else
|
||||
/* have nothing, do single process signal mask */
|
||||
if((err=sigprocmask(SIG_SETMASK, &sigset, NULL)))
|
||||
fatal_exit("sigprocmask: %s", strerror(errno));
|
||||
# endif /* HAVE_SOLARIS_THREADS */
|
||||
#endif /* HAVE_PTHREAD */
|
||||
}
|
||||
|
||||
/** unblock one signal, so we can catch it. */
|
||||
void ub_thread_sig_unblock(int sig)
|
||||
{
|
||||
int err;
|
||||
sigset_t sigset;
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, sig);
|
||||
#ifdef HAVE_PTHREAD
|
||||
if((err=pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)))
|
||||
fatal_exit("pthread_sigmask: %s", strerror(err));
|
||||
#else
|
||||
# ifdef HAVE_SOLARIS_THREADS
|
||||
if((err=thr_sigsetmask(SIG_UNBLOCK, &sigset, NULL)))
|
||||
fatal_exit("thr_sigsetmask: %s", strerror(err));
|
||||
# else
|
||||
/* have nothing, do nothing */
|
||||
if((err=sigprocmask(SIG_UNBLOCK, &sigset, NULL)))
|
||||
fatal_exit("sigprocmask: %s", strerror(errno));
|
||||
# endif /* HAVE_SOLARIS_THREADS */
|
||||
#endif /* HAVE_PTHREAD */
|
||||
}
|
||||
|
||||
#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
|
||||
/**
|
||||
* No threading available: fork a new process.
|
||||
* This means no shared data structure, and no locking.
|
||||
* Only the main thread ever returns. Exits on errors.
|
||||
* @param thr: the location where to store the thread-id.
|
||||
* @param func: function body of the thread. Return value of func is lost.
|
||||
* @param arg: user argument to func.
|
||||
*/
|
||||
void
|
||||
ub_thr_fork_create(ub_thread_t* thr, void* (*func)(void*), void* arg)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
switch(pid) {
|
||||
default: /* main */
|
||||
*thr = (ub_thread_t)pid;
|
||||
return;
|
||||
case 0: /* child */
|
||||
*thr = (ub_thread_t)getpid();
|
||||
(void)(*func)(arg);
|
||||
exit(0);
|
||||
case -1: /* error */
|
||||
fatal_exit("could not fork: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* There is no threading. Wait for a process to terminate.
|
||||
* Note that ub_thread_t is defined as pid_t.
|
||||
* @param thread: the process id to wait for.
|
||||
*/
|
||||
void ub_thr_fork_wait(ub_thread_t thread)
|
||||
{
|
||||
int status = 0;
|
||||
if(waitpid((pid_t)thread, &status, 0) == -1)
|
||||
log_err("waitpid(%d): %s", (int)thread, strerror(errno));
|
||||
if(status != 0)
|
||||
log_warn("process %d abnormal exit with status %d",
|
||||
(int)thread, status);
|
||||
}
|
||||
#endif /* !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) */
|
||||
|
||||
#ifdef HAVE_SOLARIS_THREADS
|
||||
void* ub_thread_key_get(ub_thread_key_t key)
|
||||
{
|
||||
void* ret=NULL;
|
||||
LOCKRET(thr_getspecific(key, &ret));
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* util/locks.h - unbound locking primitives
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef UTIL_LOCKS_H
|
||||
#define UTIL_LOCKS_H
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Locking primitives.
|
||||
* If pthreads is available, these are used.
|
||||
* If no locking exists, they do nothing.
|
||||
*
|
||||
* The idea is to have different sorts of locks for different tasks.
|
||||
* This allows the locking code to be ported more easily.
|
||||
*
|
||||
* Types of locks that are supported.
|
||||
* o lock_rw: lock that has many readers and one writer (to a data entry).
|
||||
* o lock_basic: simple mutex. Blocking, one person has access only.
|
||||
* This lock is meant for non performance sensitive uses.
|
||||
* o lock_quick: speed lock. For performance sensitive locking of critical
|
||||
* sections. Could be implemented by a mutex or a spinlock.
|
||||
*
|
||||
* Also thread creation and deletion functions are defined here.
|
||||
*/
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
/**
|
||||
* The following macro is used to check the return value of the
|
||||
* pthread calls. They return 0 on success and an errno on error.
|
||||
* The errno is logged to the logfile with a descriptive comment.
|
||||
*/
|
||||
#define LOCKRET(func) do {\
|
||||
int err; \
|
||||
if( (err=(func)) != 0) \
|
||||
log_err("%s at %d could not " #func ": %s", \
|
||||
__FILE__, __LINE__, strerror(err)); \
|
||||
} while(0)
|
||||
|
||||
/** DEBUG: use thread debug whenever possible */
|
||||
#if defined(HAVE_PTHREAD) && defined(HAVE_PTHREAD_SPINLOCK_T)
|
||||
# define USE_THREAD_DEBUG
|
||||
#endif
|
||||
|
||||
#ifdef USE_THREAD_DEBUG
|
||||
/******************* THREAD DEBUG ************************/
|
||||
/* (some) checking; to detect races and deadlocks. */
|
||||
#include "testcode/checklocks.h"
|
||||
|
||||
#else /* USE_THREAD_DEBUG */
|
||||
#define lock_protect(lock, area, size) /* nop */
|
||||
#define checklock_start() /* nop */
|
||||
#define checklock_stop() /* nop */
|
||||
|
||||
#ifdef HAVE_PTHREAD
|
||||
#include <pthread.h>
|
||||
|
||||
/******************* PTHREAD ************************/
|
||||
|
||||
/** we use the pthread rwlock */
|
||||
typedef pthread_rwlock_t lock_rw_t;
|
||||
/** small front for pthread init func, NULL is default attrs. */
|
||||
#define lock_rw_init(lock) LOCKRET(pthread_rwlock_init(lock, NULL))
|
||||
#define lock_rw_destroy(lock) LOCKRET(pthread_rwlock_destroy(lock))
|
||||
#define lock_rw_rdlock(lock) LOCKRET(pthread_rwlock_rdlock(lock))
|
||||
#define lock_rw_wrlock(lock) LOCKRET(pthread_rwlock_wrlock(lock))
|
||||
#define lock_rw_unlock(lock) LOCKRET(pthread_rwlock_unlock(lock))
|
||||
|
||||
/** use pthread mutex for basic lock */
|
||||
typedef pthread_mutex_t lock_basic_t;
|
||||
/** small front for pthread init func, NULL is default attrs. */
|
||||
#define lock_basic_init(lock) LOCKRET(pthread_mutex_init(lock, NULL))
|
||||
#define lock_basic_destroy(lock) LOCKRET(pthread_mutex_destroy(lock))
|
||||
#define lock_basic_lock(lock) LOCKRET(pthread_mutex_lock(lock))
|
||||
#define lock_basic_unlock(lock) LOCKRET(pthread_mutex_unlock(lock))
|
||||
|
||||
#ifndef HAVE_PTHREAD_SPINLOCK_T
|
||||
/** in case spinlocks are not supported, use a mutex. */
|
||||
typedef pthread_mutex_t lock_quick_t;
|
||||
/** small front for pthread init func, NULL is default attrs. */
|
||||
#define lock_quick_init(lock) LOCKRET(pthread_mutex_init(lock, NULL))
|
||||
#define lock_quick_destroy(lock) LOCKRET(pthread_mutex_destroy(lock))
|
||||
#define lock_quick_lock(lock) LOCKRET(pthread_mutex_lock(lock))
|
||||
#define lock_quick_unlock(lock) LOCKRET(pthread_mutex_unlock(lock))
|
||||
|
||||
#else /* HAVE_PTHREAD_SPINLOCK_T */
|
||||
/** use pthread spinlock for the quick lock */
|
||||
typedef pthread_spinlock_t lock_quick_t;
|
||||
/**
|
||||
* allocate process private since this is available whether
|
||||
* Thread Process-Shared Synchronization is supported or not.
|
||||
* This means only threads inside this process may access the lock.
|
||||
* (not threads from another process that shares memory).
|
||||
* spinlocks are not supported on all pthread platforms.
|
||||
*/
|
||||
#define lock_quick_init(lock) LOCKRET(pthread_spin_init(lock, PTHREAD_PROCESS_PRIVATE))
|
||||
#define lock_quick_destroy(lock) LOCKRET(pthread_spin_destroy(lock))
|
||||
#define lock_quick_lock(lock) LOCKRET(pthread_spin_lock(lock))
|
||||
#define lock_quick_unlock(lock) LOCKRET(pthread_spin_unlock(lock))
|
||||
|
||||
#endif /* HAVE SPINLOCK */
|
||||
|
||||
/** Thread creation */
|
||||
typedef pthread_t ub_thread_t;
|
||||
/** Pass where to store tread_t in thr. Use default NULL attributes. */
|
||||
#define ub_thread_create(thr, func, arg) LOCKRET(pthread_create(thr, NULL, func, arg))
|
||||
/** get self id. */
|
||||
#define ub_thread_self() pthread_self()
|
||||
/** wait for another thread to terminate */
|
||||
#define ub_thread_join(thread) LOCKRET(pthread_join(thread, NULL))
|
||||
typedef pthread_key_t ub_thread_key_t;
|
||||
#define ub_thread_key_create(key, f) LOCKRET(pthread_key_create(key, f))
|
||||
#define ub_thread_key_set(key, v) LOCKRET(pthread_setspecific(key, v))
|
||||
#define ub_thread_key_get(key) pthread_getspecific(key)
|
||||
|
||||
#else /* we do not HAVE_PTHREAD */
|
||||
#ifdef HAVE_SOLARIS_THREADS
|
||||
|
||||
/******************* SOLARIS THREADS ************************/
|
||||
#include <synch.h>
|
||||
#include <thread.h>
|
||||
|
||||
typedef rwlock_t lock_rw_t;
|
||||
#define lock_rw_init(lock) LOCKRET(rwlock_init(lock, USYNC_THREAD, NULL))
|
||||
#define lock_rw_destroy(lock) LOCKRET(rwlock_destroy(lock))
|
||||
#define lock_rw_rdlock(lock) LOCKRET(rw_rdlock(lock))
|
||||
#define lock_rw_wrlock(lock) LOCKRET(rw_wrlock(lock))
|
||||
#define lock_rw_unlock(lock) LOCKRET(rw_unlock(lock))
|
||||
|
||||
/** use basic mutex */
|
||||
typedef mutex_t lock_basic_t;
|
||||
#define lock_basic_init(lock) LOCKRET(mutex_init(lock, USYNC_THREAD, NULL))
|
||||
#define lock_basic_destroy(lock) LOCKRET(mutex_destroy(lock))
|
||||
#define lock_basic_lock(lock) LOCKRET(mutex_lock(lock))
|
||||
#define lock_basic_unlock(lock) LOCKRET(mutex_unlock(lock))
|
||||
|
||||
/** No spinlocks in solaris threads API. Use a mutex. */
|
||||
typedef mutex_t lock_quick_t;
|
||||
#define lock_quick_init(lock) LOCKRET(mutex_init(lock, USYNC_THREAD, NULL))
|
||||
#define lock_quick_destroy(lock) LOCKRET(mutex_destroy(lock))
|
||||
#define lock_quick_lock(lock) LOCKRET(mutex_lock(lock))
|
||||
#define lock_quick_unlock(lock) LOCKRET(mutex_unlock(lock))
|
||||
|
||||
/** Thread creation, create a default thread. */
|
||||
typedef thread_t ub_thread_t;
|
||||
#define ub_thread_create(thr, func, arg) LOCKRET(thr_create(NULL, NULL, func, arg, NULL, thr))
|
||||
#define ub_thread_self() thr_self()
|
||||
#define ub_thread_join(thread) LOCKRET(thr_join(thread, NULL, NULL))
|
||||
typedef thread_key_t ub_thread_key_t;
|
||||
#define ub_thread_key_create(key, f) LOCKRET(thr_keycreate(key, f))
|
||||
#define ub_thread_key_set(key, v) LOCKRET(thr_setspecific(key, v))
|
||||
void* ub_thread_key_get(ub_thread_key_t key);
|
||||
|
||||
#else /* we do not HAVE_SOLARIS_THREADS and no PTHREADS */
|
||||
|
||||
/******************* NO THREADS ************************/
|
||||
/** In case there is no thread support, define locks to do nothing */
|
||||
typedef int lock_rw_t;
|
||||
#define lock_rw_init(lock) /* nop */
|
||||
#define lock_rw_destroy(lock) /* nop */
|
||||
#define lock_rw_rdlock(lock) /* nop */
|
||||
#define lock_rw_wrlock(lock) /* nop */
|
||||
#define lock_rw_unlock(lock) /* nop */
|
||||
|
||||
/** define locks to do nothing */
|
||||
typedef int lock_basic_t;
|
||||
#define lock_basic_init(lock) /* nop */
|
||||
#define lock_basic_destroy(lock) /* nop */
|
||||
#define lock_basic_lock(lock) /* nop */
|
||||
#define lock_basic_unlock(lock) /* nop */
|
||||
|
||||
/** define locks to do nothing */
|
||||
typedef int lock_quick_t;
|
||||
#define lock_quick_init(lock) /* nop */
|
||||
#define lock_quick_destroy(lock) /* nop */
|
||||
#define lock_quick_lock(lock) /* nop */
|
||||
#define lock_quick_unlock(lock) /* nop */
|
||||
|
||||
/** Thread creation, threads do not exist */
|
||||
typedef pid_t ub_thread_t;
|
||||
/** ub_thread_create is simulated with fork (extremely heavy threads,
|
||||
* with no shared memory). */
|
||||
#define ub_thread_create(thr, func, arg) \
|
||||
ub_thr_fork_create(thr, func, arg)
|
||||
#define ub_thread_self() getpid()
|
||||
#define ub_thread_join(thread) ub_thr_fork_wait(thread)
|
||||
void ub_thr_fork_wait(ub_thread_t thread);
|
||||
void ub_thr_fork_create(ub_thread_t* thr, void* (*func)(void*), void* arg);
|
||||
typedef void* ub_thread_key_t;
|
||||
#define ub_thread_key_create(key, f) (*(key)) = NULL
|
||||
#define ub_thread_key_set(key, v) (key) = (v)
|
||||
#define ub_thread_key_get(key) (key)
|
||||
|
||||
#endif /* HAVE_SOLARIS_THREADS */
|
||||
#endif /* HAVE_PTHREAD */
|
||||
#endif /* USE_THREAD_DEBUG */
|
||||
|
||||
/**
|
||||
* Block all signals for this thread.
|
||||
* fatal exit on error.
|
||||
*/
|
||||
void ub_thread_blocksigs();
|
||||
|
||||
/**
|
||||
* unblock one signal for this thread.
|
||||
*/
|
||||
void ub_thread_sig_unblock(int sig);
|
||||
|
||||
#endif /* UTIL_LOCKS_H */
|
||||
+69
-4
@@ -39,15 +39,50 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/locks.h"
|
||||
#ifdef HAVE_TIME_H
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
enum verbosity_value verbosity = 0;
|
||||
/** the file logged to. */
|
||||
static FILE* logfile = 0;
|
||||
/** if key has been created */
|
||||
static int key_created = 0;
|
||||
/** pthread key for thread ids in logfile */
|
||||
static ub_thread_key_t logkey;
|
||||
|
||||
void
|
||||
log_init()
|
||||
log_init(const char* filename)
|
||||
{
|
||||
FILE *f;
|
||||
if(!key_created) {
|
||||
key_created = 1;
|
||||
ub_thread_key_create(&logkey, NULL);
|
||||
}
|
||||
|
||||
if(!filename || !filename[0]) {
|
||||
if(logfile && logfile != stderr)
|
||||
fclose(logfile);
|
||||
logfile = stderr;
|
||||
return;
|
||||
}
|
||||
/* open the file for logging */
|
||||
f = fopen(filename, "a");
|
||||
if(!f) {
|
||||
log_err("Could not open logfile %s: %s", filename,
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
verbose(VERB_DETAIL, "switching to logfile %s", filename);
|
||||
if(logfile && logfile != stderr)
|
||||
fclose(logfile);
|
||||
logfile = f;
|
||||
}
|
||||
|
||||
void log_thread_set(int* num)
|
||||
{
|
||||
ub_thread_key_set(logkey, num);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -55,10 +90,12 @@ log_vmsg(const char* type, const char *format, va_list args)
|
||||
{
|
||||
char message[MAXSYSLOGMSGLEN];
|
||||
const char* ident="unbound";
|
||||
unsigned int* tid = (unsigned int*)ub_thread_key_get(logkey);
|
||||
vsnprintf(message, sizeof(message), format, args);
|
||||
fprintf(stderr, "[%d] %s[%d] %s: %s\n",
|
||||
(int)time(NULL), ident, (int)getpid(), type, message);
|
||||
fflush(stderr);
|
||||
fprintf(logfile, "[%d] %s[%d:%x] %s: %s\n",
|
||||
(int)time(NULL), ident, (int)getpid(),
|
||||
tid?*tid:0, type, message);
|
||||
fflush(logfile);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,6 +124,19 @@ log_err(const char *format, ...)
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* implementation of log_warn
|
||||
* @param format: format string printf-style.
|
||||
*/
|
||||
void
|
||||
log_warn(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log_vmsg("warning", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* implementation of fatal_exit
|
||||
* @param format: format string printf-style.
|
||||
@@ -116,3 +166,18 @@ verbose(enum verbosity_value level, const char* format, ...)
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
log_hex(const char* msg, void* data, size_t length)
|
||||
{
|
||||
size_t i;
|
||||
uint8_t* data8 = (uint8_t*)data;
|
||||
const char const* hexchar = "0123456789ABCDEF";
|
||||
char* buf = malloc(length*2 + 1); /* alloc hex chars + \0 */
|
||||
for(i=0; i<length; i++) {
|
||||
buf[i*2] = hexchar[ data8[i] >> 4 ];
|
||||
buf[i*2 + 1] = hexchar[ data8[i] & 0xF ];
|
||||
}
|
||||
buf[length*2] = 0;
|
||||
log_info("%s[%d] %s", msg, length, buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
+26
-1
@@ -76,8 +76,17 @@ void verbose(enum verbosity_value level,
|
||||
|
||||
/**
|
||||
* call this to initialize logging services.
|
||||
* @param filename: if NULL stderr is used.
|
||||
*/
|
||||
void log_init();
|
||||
void log_init(const char* filename);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param num: number to print for this thread. Owned by caller, must
|
||||
* continue to exist.
|
||||
*/
|
||||
void log_thread_set(int* num);
|
||||
|
||||
/**
|
||||
* Log informational message.
|
||||
@@ -93,6 +102,22 @@ void log_info(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
|
||||
*/
|
||||
void log_err(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
|
||||
|
||||
/**
|
||||
* Log warning message.
|
||||
* Pass printf formatted arguments. No trailing newline is needed.
|
||||
* @param format: printf-style format string. Arguments follow.
|
||||
*/
|
||||
void log_warn(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
|
||||
|
||||
/**
|
||||
* Log a hex-string to the log. Can be any length.
|
||||
* performs mallocs to do so, slow. But debug useful.
|
||||
* @param msg: string desc to accompany the hexdump.
|
||||
* @param data: data to dump in hex format.
|
||||
* @param length: length of data.
|
||||
*/
|
||||
void log_hex(const char* msg, void* data, size_t length);
|
||||
|
||||
/**
|
||||
* Log fatal error message, and exit the current process.
|
||||
* Pass printf formatted arguments. No trailing newline is needed.
|
||||
|
||||
+9
-3
@@ -79,7 +79,13 @@ void *event_init(void)
|
||||
event_base_free(base);
|
||||
return NULL;
|
||||
}
|
||||
base->fds = (struct event**)calloc(MAX_FDS, sizeof(struct event*));
|
||||
base->capfd = MAX_FDS;
|
||||
#ifdef FD_SETSIZE
|
||||
if((int)FD_SETSIZE < base->capfd)
|
||||
base->capfd = (int)FD_SETSIZE;
|
||||
#endif
|
||||
base->fds = (struct event**)calloc((size_t)base->capfd,
|
||||
sizeof(struct event*));
|
||||
if(!base->fds) {
|
||||
event_base_free(base);
|
||||
return NULL;
|
||||
@@ -239,7 +245,7 @@ int event_add(struct event* ev, struct timeval* tv)
|
||||
{
|
||||
if(ev->added)
|
||||
event_del(ev);
|
||||
if(ev->ev_fd != -1 && ev->ev_fd >= MAX_FDS)
|
||||
if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
|
||||
return -1;
|
||||
if( (ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
|
||||
ev->ev_base->fds[ev->ev_fd] = ev;
|
||||
@@ -264,7 +270,7 @@ int event_add(struct event* ev, struct timeval* tv)
|
||||
/** remove event, you may change it again */
|
||||
int event_del(struct event* ev)
|
||||
{
|
||||
if(ev->ev_fd != -1 && ev->ev_fd >= MAX_FDS)
|
||||
if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
|
||||
return -1;
|
||||
if(ev->ev_events&EV_TIMEOUT)
|
||||
(void)rbtree_delete(ev->ev_base->times, &ev->node);
|
||||
|
||||
+17
-4
@@ -37,6 +37,16 @@
|
||||
* \file
|
||||
* This file implements part of the event(3) libevent api.
|
||||
* The back end is only select. Max number of fds is limited.
|
||||
* Max number of signals is limited, one handler per signal only.
|
||||
* And one handler per fd.
|
||||
*
|
||||
* Although limited to select() and a max (1024) open fds, it
|
||||
* is efficient:
|
||||
* o dispatch call caches fd_sets to use.
|
||||
* o handler calling takes time ~ to the number of fds.
|
||||
* o timeouts are stored in a redblack tree, sorted, so take log(n).
|
||||
* Timeouts are only accurate to the second (no subsecond accuracy).
|
||||
* To avoid cpu hogging, fractional timeouts are rounded up to a whole second.
|
||||
*/
|
||||
|
||||
#ifndef MINI_EVENT_H
|
||||
@@ -76,6 +86,8 @@ struct event_base
|
||||
struct event** fds;
|
||||
/** max fd in use */
|
||||
int maxfd;
|
||||
/** capacity - size of the fds array */
|
||||
int capfd;
|
||||
/** fdset for read write */
|
||||
fd_set reads, writes;
|
||||
/** array of 0 - maxsig of ptr to event for it */
|
||||
@@ -95,9 +107,9 @@ struct event {
|
||||
|
||||
/** event base it belongs to */
|
||||
struct event_base *ev_base;
|
||||
/** fd to poll or -1 for timeouts */
|
||||
/** fd to poll or -1 for timeouts. signal number for sigs. */
|
||||
int ev_fd;
|
||||
/** events this event is interested in */
|
||||
/** what events this event is interested in, see EV_.. above. */
|
||||
short ev_events;
|
||||
/** timeout value */
|
||||
struct timeval ev_timeout;
|
||||
@@ -123,7 +135,7 @@ int event_base_loopexit(struct event_base *, struct timeval *);
|
||||
void event_base_free(struct event_base *);
|
||||
/** set content of event */
|
||||
void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
|
||||
/** add event to a base */
|
||||
/** add event to a base. You *must* call this for every event. */
|
||||
int event_base_set(struct event_base *, struct event *);
|
||||
/** add event to make it active. You may not change it with event_set anymore */
|
||||
int event_add(struct event *, struct timeval *);
|
||||
@@ -133,7 +145,8 @@ int event_del(struct event *);
|
||||
#define evtimer_add(ev, tv) event_add(ev, tv)
|
||||
#define evtimer_del(ev) event_del(ev)
|
||||
|
||||
/* uses different implementation. Cannot mix fd/timeouts and signals. */
|
||||
/* uses different implementation. Cannot mix fd/timeouts and signals inside
|
||||
* the same struct event. create several event structs for that. */
|
||||
/** install signal handler */
|
||||
int signal_add(struct event *, struct timeval *);
|
||||
/** set signal event contents */
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/log.h"
|
||||
#include <fcntl.h>
|
||||
|
||||
/** returns true is string addr is an ip6 specced address. */
|
||||
int
|
||||
@@ -49,3 +51,39 @@ str_is_ip6(const char* str)
|
||||
else return 0;
|
||||
}
|
||||
|
||||
int
|
||||
write_socket(int s, const void *buf, size_t size)
|
||||
{
|
||||
const char* data = (const char*)buf;
|
||||
size_t total_count = 0;
|
||||
|
||||
while (total_count < size) {
|
||||
ssize_t count
|
||||
= write(s, data + total_count, size - total_count);
|
||||
if (count == -1) {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
return 0;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
total_count += count;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
fd_set_nonblock(int s)
|
||||
{
|
||||
int flag;
|
||||
if((flag = fcntl(s, F_GETFL)) == -1) {
|
||||
log_err("can't fcntl F_GETFL: %s", strerror(errno));
|
||||
flag = 0;
|
||||
}
|
||||
flag |= O_NONBLOCK;
|
||||
if(fcntl(s, F_SETFL, flag) == -1) {
|
||||
log_err("can't fcntl F_SETFL: %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -49,4 +49,21 @@
|
||||
*/
|
||||
int str_is_ip6(const char* str);
|
||||
|
||||
/**
|
||||
* Write (blocking) to a nonblocking socket.
|
||||
* @param s: fd.
|
||||
* @param buf: data buffer.
|
||||
* @param size: length of data to send.
|
||||
* @return: 0 on error. errno is set.
|
||||
*/
|
||||
int
|
||||
write_socket(int s, const void *buf, size_t size);
|
||||
|
||||
/**
|
||||
* Set fd nonblocking.
|
||||
* @param s: file descriptor.
|
||||
* @return: 0 on error (error is printed to log).
|
||||
*/
|
||||
int fd_set_nonblock(int s);
|
||||
|
||||
#endif /* NET_HELP_H */
|
||||
|
||||
+90
-12
@@ -41,7 +41,6 @@
|
||||
|
||||
#include "util/netevent.h"
|
||||
#include "util/log.h"
|
||||
#include <errno.h>
|
||||
|
||||
/* -------- Start of local definitions -------- */
|
||||
/** The TCP reading or writing query timeout in seconds */
|
||||
@@ -138,6 +137,9 @@ static void comm_timer_callback(int fd, short event, void* arg);
|
||||
*/
|
||||
static void comm_signal_callback(int fd, short event, void* arg);
|
||||
|
||||
/** libevent callback for AF_UNIX fds. */
|
||||
static void comm_point_local_handle_callback(int fd, short event, void* arg);
|
||||
|
||||
/** create a tcp handler with a parent */
|
||||
static struct comm_point* comm_point_create_tcp_handler(
|
||||
struct comm_base *base, struct comm_point* parent, size_t bufsize,
|
||||
@@ -171,6 +173,8 @@ comm_base_create()
|
||||
void
|
||||
comm_base_delete(struct comm_base* b)
|
||||
{
|
||||
if(!b)
|
||||
return;
|
||||
#ifdef HAVE_EVENT_BASE_FREE
|
||||
/* only libevent 1.2+ has it */
|
||||
event_base_free(b->eb->base);
|
||||
@@ -328,7 +332,8 @@ tcp_callback_writer(struct comm_point* c)
|
||||
{
|
||||
log_assert(c->type == comm_tcp);
|
||||
ldns_buffer_clear(c->buffer);
|
||||
c->tcp_is_reading = 1;
|
||||
if(c->tcp_do_toggle_rw)
|
||||
c->tcp_is_reading = 1;
|
||||
c->tcp_byte_count = 0;
|
||||
comm_point_stop_listening(c);
|
||||
/* for listening socket */
|
||||
@@ -340,11 +345,13 @@ static void
|
||||
tcp_callback_reader(struct comm_point* c)
|
||||
{
|
||||
struct comm_reply rep;
|
||||
log_assert(c->type == comm_tcp);
|
||||
log_assert(c->type == comm_tcp || c->type == comm_local);
|
||||
ldns_buffer_flip(c->buffer);
|
||||
c->tcp_is_reading = 0;
|
||||
if(c->tcp_do_toggle_rw)
|
||||
c->tcp_is_reading = 0;
|
||||
c->tcp_byte_count = 0;
|
||||
comm_point_stop_listening(c);
|
||||
if(c->type == comm_tcp)
|
||||
comm_point_stop_listening(c);
|
||||
rep.c = c;
|
||||
rep.addrlen = 0;
|
||||
if( (*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, &rep) ) {
|
||||
@@ -355,13 +362,14 @@ tcp_callback_reader(struct comm_point* c)
|
||||
/** Handle tcp reading callback.
|
||||
* @param fd: file descriptor of socket.
|
||||
* @param c: comm point to read from into buffer.
|
||||
* @param short_ok: if true, very short packets are OK (for comm_local).
|
||||
* @return: 0 on error
|
||||
*/
|
||||
static int
|
||||
comm_point_tcp_handle_read(int fd, struct comm_point* c)
|
||||
comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
|
||||
{
|
||||
ssize_t r;
|
||||
log_assert(c->type == comm_tcp);
|
||||
log_assert(c->type == comm_tcp || c->type == comm_local);
|
||||
if(!c->tcp_is_reading)
|
||||
return 0;
|
||||
|
||||
@@ -387,7 +395,8 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c)
|
||||
}
|
||||
ldns_buffer_set_limit(c->buffer,
|
||||
ldns_buffer_read_u16_at(c->buffer, 0));
|
||||
if(ldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
|
||||
if(!short_ok &&
|
||||
ldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
|
||||
verbose(VERB_DETAIL, "tcp: dropped bogus too short.");
|
||||
return 0;
|
||||
}
|
||||
@@ -463,7 +472,7 @@ comm_point_tcp_handle_callback(int fd, short event, void* arg)
|
||||
log_assert(c->type == comm_tcp);
|
||||
|
||||
if(event&EV_READ) {
|
||||
if(!comm_point_tcp_handle_read(fd, c)) {
|
||||
if(!comm_point_tcp_handle_read(fd, c, 0)) {
|
||||
reclaim_tcp_handler(c);
|
||||
if(!c->tcp_do_close)
|
||||
(void)(*c->callback)(c, c->cb_arg,
|
||||
@@ -491,6 +500,21 @@ comm_point_tcp_handle_callback(int fd, short event, void* arg)
|
||||
log_err("Ignored event %d for tcphdl.", event);
|
||||
}
|
||||
|
||||
static void comm_point_local_handle_callback(int fd, short event, void* arg)
|
||||
{
|
||||
struct comm_point* c = (struct comm_point*)arg;
|
||||
log_assert(c->type == comm_local);
|
||||
|
||||
if(event&EV_READ) {
|
||||
if(!comm_point_tcp_handle_read(fd, c, 1)) {
|
||||
(void)(*c->callback)(c, c->cb_arg, NETEVENT_CLOSED,
|
||||
NULL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
log_err("Ignored event %d for localhdl.", event);
|
||||
}
|
||||
|
||||
struct comm_point*
|
||||
comm_point_create_udp(struct comm_base *base, int fd, ldns_buffer* buffer,
|
||||
comm_point_callback_t* callback, void* callback_arg)
|
||||
@@ -517,6 +541,7 @@ comm_point_create_udp(struct comm_base *base, int fd, ldns_buffer* buffer,
|
||||
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->callback = callback;
|
||||
c->cb_arg = callback_arg;
|
||||
@@ -570,7 +595,8 @@ comm_point_create_tcp_handler(struct comm_base *base,
|
||||
c->tcp_free = NULL;
|
||||
c->type = comm_tcp;
|
||||
c->tcp_do_close = 0;
|
||||
c->tcp_do_toggle_rw = 0;
|
||||
c->do_not_close = 0;
|
||||
c->tcp_do_toggle_rw = 1;
|
||||
c->callback = callback;
|
||||
c->cb_arg = callback_arg;
|
||||
/* add to parent free list */
|
||||
@@ -624,6 +650,7 @@ comm_point_create_tcp(struct comm_base *base, int fd, int num, size_t bufsize,
|
||||
c->tcp_free = NULL;
|
||||
c->type = comm_tcp_accept;
|
||||
c->tcp_do_close = 0;
|
||||
c->do_not_close = 0;
|
||||
c->tcp_do_toggle_rw = 0;
|
||||
c->callback = NULL;
|
||||
c->cb_arg = NULL;
|
||||
@@ -651,6 +678,56 @@ comm_point_create_tcp(struct comm_base *base, int fd, int num, size_t bufsize,
|
||||
return c;
|
||||
}
|
||||
|
||||
struct comm_point*
|
||||
comm_point_create_local(struct comm_base *base, int fd, size_t bufsize,
|
||||
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 = ldns_buffer_new(bufsize);
|
||||
if(!c->buffer) {
|
||||
free(c->ev);
|
||||
free(c);
|
||||
return NULL;
|
||||
}
|
||||
c->timeout = NULL;
|
||||
c->tcp_is_reading = 1;
|
||||
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_local;
|
||||
c->tcp_do_close = 0;
|
||||
c->do_not_close = 1;
|
||||
c->tcp_do_toggle_rw = 0;
|
||||
c->callback = callback;
|
||||
c->cb_arg = callback_arg;
|
||||
/* libevent stuff */
|
||||
evbits = EV_PERSIST | EV_READ;
|
||||
event_set(&c->ev->ev, c->fd, evbits, comm_point_local_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 tcphdl event");
|
||||
free(c->ev);
|
||||
free(c);
|
||||
return NULL;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
comm_point_close(struct comm_point* c)
|
||||
{
|
||||
@@ -660,7 +737,7 @@ comm_point_close(struct comm_point* c)
|
||||
log_err("could not event_del on close");
|
||||
}
|
||||
/* close fd after removing from event lists, or epoll.. is messed up */
|
||||
if(c->fd != -1)
|
||||
if(c->fd != -1 && !c->do_not_close)
|
||||
close(c->fd);
|
||||
c->fd = -1;
|
||||
}
|
||||
@@ -677,7 +754,8 @@ comm_point_delete(struct comm_point* c)
|
||||
comm_point_delete(c->tcp_handlers[i]);
|
||||
free(c->tcp_handlers);
|
||||
}
|
||||
if(c->type == comm_tcp)
|
||||
free(c->timeout);
|
||||
if(c->type == comm_tcp || c->type == comm_local)
|
||||
ldns_buffer_free(c->buffer);
|
||||
free(c->ev);
|
||||
free(c);
|
||||
|
||||
+19
-1
@@ -135,10 +135,15 @@ struct comm_point {
|
||||
/** TCP accept socket - only creates handlers if readable. */
|
||||
comm_tcp_accept,
|
||||
/** TCP handler socket - handle byteperbyte readwrite. */
|
||||
comm_tcp
|
||||
comm_tcp,
|
||||
/** AF_UNIX socket - for internal commands. */
|
||||
comm_local
|
||||
} type;
|
||||
|
||||
/* ---------- Behaviour ----------- */
|
||||
/** if set the connection is NOT closed on delete. */
|
||||
int do_not_close;
|
||||
|
||||
/** if set, the connection is closed on error, on timeout,
|
||||
and after read/write completes. No callback is done. */
|
||||
int tcp_do_close;
|
||||
@@ -283,6 +288,19 @@ struct comm_point* comm_point_create_tcp(struct comm_base* base,
|
||||
int fd, int num, size_t bufsize,
|
||||
comm_point_callback_t* callback, void* callback_arg);
|
||||
|
||||
/**
|
||||
* Create commpoint to listen to a local domain file descriptor.
|
||||
* @param base: in which base to alloc the commpoint.
|
||||
* @param fd: file descriptor of open AF_UNIX socket set to listen nonblocking.
|
||||
* @param bufsize: size of buffer to create for handlers.
|
||||
* @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_local(struct comm_base* base,
|
||||
int fd, size_t bufsize,
|
||||
comm_point_callback_t* callback, void* callback_arg);
|
||||
|
||||
/**
|
||||
* Close a comm point fd.
|
||||
* @param c: comm point to close.
|
||||
|
||||
+363
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* util/random.c - random numbers thread safe.
|
||||
* BSD licensed, taken from binutils 2.17.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/random.h"
|
||||
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. [rescinded 22 July 1999]
|
||||
* 4. Neither the name of the University 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 REGENTS 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is derived from the Berkeley source:
|
||||
* @(#)random.c 5.5 (Berkeley) 7/6/88
|
||||
* It was reworked for the GNU C Library by Roland McGrath.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Thread safe random functions. Similar to random(3) and initstate(3).
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef ULONG_MAX
|
||||
/** in case its not defined */
|
||||
#define ULONG_MAX ((unsigned long)(~0L)) /* 0xFFFFFFFF for 32-bits */
|
||||
#endif
|
||||
#ifndef LONG_MAX
|
||||
/** in case its not defined */
|
||||
#define LONG_MAX ((long)(ULONG_MAX >> 1)) /* 0x7FFFFFFF for 32-bits*/
|
||||
#endif
|
||||
|
||||
|
||||
/* An improved random number generation package. In addition to the standard
|
||||
rand()/srand() like interface, this package also has a special state info
|
||||
interface. The initstate() routine is called with a seed, an array of
|
||||
bytes, and a count of how many bytes are being passed in; this array is
|
||||
then initialized to contain information for random number generation with
|
||||
that much state information. Good sizes for the amount of state
|
||||
information are 32, 64, 128, and 256 bytes. The state can be switched by
|
||||
calling the setstate() function with the same array as was initiallized
|
||||
with initstate(). By default, the package runs with 128 bytes of state
|
||||
information and generates far better random numbers than a linear
|
||||
congruential generator. If the amount of state information is less than
|
||||
32 bytes, a simple linear congruential R.N.G. is used. Internally, the
|
||||
state information is treated as an array of longs; the zeroeth element of
|
||||
the array is the type of R.N.G. being used (small integer); the remainder
|
||||
of the array is the state information for the R.N.G. Thus, 32 bytes of
|
||||
state information will give 7 longs worth of state information, which will
|
||||
allow a degree seven polynomial. (Note: The zeroeth word of state
|
||||
information also has some other information stored in it; see setstate
|
||||
for details). The random number generation technique is a linear feedback
|
||||
shift register approach, employing trinomials (since there are fewer terms
|
||||
to sum up that way). In this approach, the least significant bit of all
|
||||
the numbers in the state table will act as a linear feedback shift register,
|
||||
and will have period 2^deg - 1 (where deg is the degree of the polynomial
|
||||
being used, assuming that the polynomial is irreducible and primitive).
|
||||
The higher order bits will have longer periods, since their values are
|
||||
also influenced by pseudo-random carries out of the lower bits. The
|
||||
total period of the generator is approximately deg*(2**deg - 1); thus
|
||||
doubling the amount of state information has a vast influence on the
|
||||
period of the generator. Note: The deg*(2**deg - 1) is an approximation
|
||||
only good for large deg, when the period of the shift register is the
|
||||
dominant factor. With deg equal to seven, the period is actually much
|
||||
longer than the 7*(2**7 - 1) predicted by this formula. */
|
||||
|
||||
|
||||
|
||||
/* For each of the currently supported random number generators, we have a
|
||||
break value on the amount of state information (you need at least thi
|
||||
bytes of state info to support this random number generator), a degree for
|
||||
the polynomial (actually a trinomial) that the R.N.G. is based on, and
|
||||
separation between the two lower order coefficients of the trinomial. */
|
||||
|
||||
/** Linear congruential. */
|
||||
#define TYPE_0 0
|
||||
/** the break */
|
||||
#define BREAK_0 8
|
||||
/** the degree */
|
||||
#define DEG_0 0
|
||||
/** the sep */
|
||||
#define SEP_0 0
|
||||
|
||||
/** x**7 + x**3 + 1. */
|
||||
#define TYPE_1 1
|
||||
/** the break */
|
||||
#define BREAK_1 32
|
||||
/** the degree */
|
||||
#define DEG_1 7
|
||||
/** the sep */
|
||||
#define SEP_1 3
|
||||
|
||||
/** x**15 + x + 1. */
|
||||
#define TYPE_2 2
|
||||
/** the break */
|
||||
#define BREAK_2 64
|
||||
/** the degree */
|
||||
#define DEG_2 15
|
||||
/** the sep */
|
||||
#define SEP_2 1
|
||||
|
||||
/** x**31 + x**3 + 1. */
|
||||
#define TYPE_3 3
|
||||
/** the break */
|
||||
#define BREAK_3 128
|
||||
/** the degree */
|
||||
#define DEG_3 31
|
||||
/** the sep */
|
||||
#define SEP_3 3
|
||||
|
||||
/** x**63 + x + 1. */
|
||||
#define TYPE_4 4
|
||||
/** the break */
|
||||
#define BREAK_4 256
|
||||
/** the degree */
|
||||
#define DEG_4 63
|
||||
/** the sep */
|
||||
#define SEP_4 1
|
||||
|
||||
|
||||
/* Array versions of the above information to make code run faster.
|
||||
Relies on fact that TYPE_i == i. */
|
||||
|
||||
/** Max number of types above. */
|
||||
#define MAX_TYPES 5
|
||||
|
||||
/*
|
||||
static int degrees[MAX_TYPES] = { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 };
|
||||
static int seps[MAX_TYPES] = { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 };
|
||||
*/
|
||||
|
||||
|
||||
/* Initially, everything is set up as if from:
|
||||
initstate(1, randtbl, 128);
|
||||
Note that this initialization takes advantage of the fact that srandom
|
||||
advances the front and rear pointers 10*rand_deg times, and hence the
|
||||
rear pointer which starts at 0 will also end up at zero; thus the zeroeth
|
||||
element of the state information, which contains info about the current
|
||||
position of the rear pointer is just
|
||||
(MAX_TYPES * (rptr - state)) + TYPE_3 == TYPE_3. */
|
||||
|
||||
/*
|
||||
static long int randtbl[DEG_3 + 1] =
|
||||
{ TYPE_3,
|
||||
0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342,
|
||||
0xde3b81e0, 0xdf0a6fb5, 0xf103bc02, 0x48f340fb,
|
||||
0x7449e56b, 0xbeb1dbb0, 0xab5c5918, 0x946554fd,
|
||||
0x8c2e680f, 0xeb3d799f, 0xb11ee0b7, 0x2d436b86,
|
||||
0xda672e2a, 0x1588ca88, 0xe369735d, 0x904f35f7,
|
||||
0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc,
|
||||
0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b,
|
||||
0xf5ad9d0e, 0x8999220b, 0x27fb47b9
|
||||
};
|
||||
*/
|
||||
|
||||
/* FPTR and RPTR are two pointers into the state info, a front and a rear
|
||||
pointer. These two pointers are always rand_sep places aparts, as they
|
||||
cycle through the state information. (Yes, this does mean we could get
|
||||
away with just one pointer, but the code for random is more efficient
|
||||
this way). The pointers are left positioned as they would be from the call:
|
||||
initstate(1, randtbl, 128);
|
||||
(The position of the rear pointer, rptr, is really 0 (as explained above
|
||||
in the initialization of randtbl) because the state table pointer is set
|
||||
to point to randtbl[1] (as explained below).) */
|
||||
|
||||
/*
|
||||
static long int *fptr = &randtbl[SEP_3 + 1];
|
||||
static long int *rptr = &randtbl[1];
|
||||
*/
|
||||
|
||||
|
||||
/* The following things are the pointer to the state information table,
|
||||
the type of the current generator, the degree of the current polynomial
|
||||
being used, and the separation between the two pointers.
|
||||
Note that for efficiency of random, we remember the first location of
|
||||
the state information, not the zeroeth. Hence it is valid to access
|
||||
state[-1], which is used to store the type of the R.N.G.
|
||||
Also, we remember the last location, since this is more efficient than
|
||||
indexing every time to find the address of the last element to see if
|
||||
the front and rear pointers have wrapped. */
|
||||
|
||||
/*
|
||||
static long int *state = &randtbl[1];
|
||||
|
||||
static int rand_type = TYPE_3;
|
||||
static int rand_deg = DEG_3;
|
||||
static int rand_sep = SEP_3;
|
||||
|
||||
static long int *end_ptr = &randtbl[sizeof(randtbl) / sizeof(randtbl[0])];
|
||||
*/
|
||||
|
||||
/* Initialize the random number generator based on the given seed. If the
|
||||
type is the trivial no-state-information type, just remember the seed.
|
||||
Otherwise, initializes state[] based on the given "seed" via a linear
|
||||
congruential generator. Then, the pointers are set to known locations
|
||||
that are exactly rand_sep places apart. Lastly, it cycles the state
|
||||
information a given number of times to get rid of any initial dependencies
|
||||
introduced by the L.C.R.N.G. Note that the initialization of randtbl[]
|
||||
for default usage relies on values produced by this routine. */
|
||||
/** init state.
|
||||
* @param s: state to init.
|
||||
* @param x: seed.
|
||||
*/
|
||||
static void
|
||||
ub_srandom (struct ub_randstate* s, unsigned int x)
|
||||
{
|
||||
s->state[0] = (long int)x;
|
||||
if (s->rand_type != TYPE_0)
|
||||
{
|
||||
register long int i;
|
||||
for (i = 1; i < s->rand_deg; ++i)
|
||||
s->state[i] = (1103515145 * s->state[i - 1]) + 12345;
|
||||
s->fptr = &s->state[s->rand_sep];
|
||||
s->rptr = &s->state[0];
|
||||
for (i = 0; i < 10 * s->rand_deg; ++i)
|
||||
(void)ub_random(s);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the state information in the given array of N bytes for
|
||||
future random number generation. Based on the number of bytes we
|
||||
are given, and the break values for the different R.N.G.'s, we choose
|
||||
the best (largest) one we can and set things up for it. srandom is
|
||||
then called to initialize the state information. Note that on return
|
||||
from srandom, we set state[-1] to be the type multiplexed with the current
|
||||
value of the rear pointer; this is so successive calls to initstate won't
|
||||
lose this information and will be able to restart with setstate.
|
||||
Note: The first thing we do is save the current state, if any, just like
|
||||
setstate so that it doesn't matter when initstate is called.
|
||||
Returns a pointer to the old state. */
|
||||
int
|
||||
ub_initstate (unsigned int seed, struct ub_randstate* state, unsigned long n)
|
||||
{
|
||||
memset(state, 0, sizeof(state));
|
||||
state->state = calloc(1, n);
|
||||
if(!state->state)
|
||||
return 0;
|
||||
|
||||
if (n < BREAK_1)
|
||||
{
|
||||
if (n < BREAK_0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
state->rand_type = TYPE_0;
|
||||
state->rand_deg = DEG_0;
|
||||
state->rand_sep = SEP_0;
|
||||
}
|
||||
else if (n < BREAK_2)
|
||||
{
|
||||
state->rand_type = TYPE_1;
|
||||
state->rand_deg = DEG_1;
|
||||
state->rand_sep = SEP_1;
|
||||
}
|
||||
else if (n < BREAK_3)
|
||||
{
|
||||
state->rand_type = TYPE_2;
|
||||
state->rand_deg = DEG_2;
|
||||
state->rand_sep = SEP_2;
|
||||
}
|
||||
else if (n < BREAK_4)
|
||||
{
|
||||
state->rand_type = TYPE_3;
|
||||
state->rand_deg = DEG_3;
|
||||
state->rand_sep = SEP_3;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->rand_type = TYPE_4;
|
||||
state->rand_deg = DEG_4;
|
||||
state->rand_sep = SEP_4;
|
||||
}
|
||||
|
||||
/* Must set END_PTR before srandom. */
|
||||
state->end_ptr = &state->state[state->rand_deg];
|
||||
ub_srandom(state, seed);
|
||||
/*
|
||||
if (state->rand_type == TYPE_0)
|
||||
state->state[-1] = state->rand_type;
|
||||
else
|
||||
state->state[-1] = (MAX_TYPES * (state->rptr - state->state)) + state->rand_type;
|
||||
*/
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If we are using the trivial TYPE_0 R.N.G., just do the old linear
|
||||
congruential bit. Otherwise, we do our fancy trinomial stuff, which is the
|
||||
same in all ther other cases due to all the global variables that have been
|
||||
set up. The basic operation is to add the number at the rear pointer into
|
||||
the one at the front pointer. Then both pointers are advanced to the next
|
||||
location cyclically in the table. The value returned is the sum generated,
|
||||
reduced to 31 bits by throwing away the "least random" low bit.
|
||||
Note: The code takes advantage of the fact that both the front and
|
||||
rear pointers can't wrap on the same call by not testing the rear
|
||||
pointer if the front one has wrapped. Returns a 31-bit random number. */
|
||||
|
||||
long int
|
||||
ub_random (struct ub_randstate* s)
|
||||
{
|
||||
if (s->rand_type == TYPE_0)
|
||||
{
|
||||
s->state[0] = ((s->state[0] * 1103515245) + 12345) & LONG_MAX;
|
||||
return s->state[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
long int i;
|
||||
*s->fptr += *s->rptr;
|
||||
/* Chucking least random bit. */
|
||||
i = (*s->fptr >> 1) & LONG_MAX;
|
||||
++s->fptr;
|
||||
if (s->fptr >= s->end_ptr)
|
||||
{
|
||||
s->fptr = s->state;
|
||||
++s->rptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
++s->rptr;
|
||||
if (s->rptr >= s->end_ptr)
|
||||
s->rptr = s->state;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ub_randfree(struct ub_randstate* state)
|
||||
{
|
||||
if(!state)
|
||||
return;
|
||||
free(state->state);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* util/random.h - thread safe random generator, which is reasonably secure.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef UTIL_RANDOM_H
|
||||
#define UTIL_RANDOM_H
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Thread safe random functions. Similar to random(3) and initstate(3).
|
||||
*/
|
||||
|
||||
/**
|
||||
* random state structure.
|
||||
*/
|
||||
struct ub_randstate {
|
||||
/** state array, malloced */
|
||||
long int* state;
|
||||
/** front ptr */
|
||||
long int* fptr;
|
||||
/** rear ptr */
|
||||
long int* rptr;
|
||||
/** rng type */
|
||||
int rand_type;
|
||||
/** rng degree */
|
||||
int rand_deg;
|
||||
/** rng sep */
|
||||
int rand_sep;
|
||||
/** rng end ptr */
|
||||
long int* end_ptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize a random generator state for use
|
||||
* @param seed: seed value to create state contents.
|
||||
* @param state: struct allocated by caller.
|
||||
* @param n: size of state->state. 8, 32, 64, 128, or 256 bytes.
|
||||
* @return false alloc failure.
|
||||
*/
|
||||
int ub_initstate(unsigned int seed, struct ub_randstate* state,
|
||||
unsigned long n);
|
||||
|
||||
/**
|
||||
* Generate next random number from the state passed along.
|
||||
* Thread safe, so random numbers are repeatable.
|
||||
* @param state: must have been initialised with ub_initstate.
|
||||
* @return: random 31 bit value.
|
||||
*/
|
||||
long int ub_random(struct ub_randstate* state);
|
||||
|
||||
/**
|
||||
* Delete the random state.
|
||||
* @param state: to delete.
|
||||
*/
|
||||
void ub_randfree(struct ub_randstate* state);
|
||||
|
||||
#endif /* UTIL_RANDOM_H */
|
||||
Reference in New Issue
Block a user