Compare commits

...
45 Commits
Author SHA1 Message Date
Wouter Wijngaards 31cc7670c6 No empty if body in nonevent compile.
git-svn-id: file:///svn/unbound/trunk@171 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-09 14:55:57 +00:00
Wouter Wijngaards 39da393425 Please older compilers.
git-svn-id: file:///svn/unbound/trunk@170 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-09 14:31:48 +00:00
Wouter Wijngaards 9d89d05bd1 Debug threads when possible.
git-svn-id: file:///svn/unbound/trunk@169 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-09 14:09:32 +00:00
Wouter Wijngaards 1ea78ab032 cleanup and unit test for alloc, also lock protection statements.
git-svn-id: file:///svn/unbound/trunk@168 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-09 13:37:57 +00:00
Wouter Wijngaards 138aa8eebc rwlock writelock checking.
git-svn-id: file:///svn/unbound/trunk@167 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-09 09:01:03 +00:00
Wouter Wijngaards 78ab5f41d7 review of checklocks.
git-svn-id: file:///svn/unbound/trunk@166 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-08 14:59:41 +00:00
Wouter Wijngaards a43a042792 Simple thread problem detector code.
git-svn-id: file:///svn/unbound/trunk@165 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-07 16:21:31 +00:00
Wouter Wijngaards 696d38318c alloc quar work.
git-svn-id: file:///svn/unbound/trunk@164 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-06 15:46:17 +00:00
Wouter Wijngaards 1a38497dbe Test result OK.
git-svn-id: file:///svn/unbound/trunk@163 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-06 09:49:15 +00:00
Wouter Wijngaards 06afa3d5d0 Alloc layout.
git-svn-id: file:///svn/unbound/trunk@162 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-02 15:03:58 +00:00
Wouter Wijngaards aa4918c25b exit on signal, compatible with bad versions of libevent.
git-svn-id: file:///svn/unbound/trunk@161 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-02 09:48:31 +00:00
Wouter Wijngaards 1c24c78462 Porting to solaris.
git-svn-id: file:///svn/unbound/trunk@160 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-02 09:08:12 +00:00
Wouter Wijngaards b150a499db no warnings for thread code (porting), and nicer logging.
git-svn-id: file:///svn/unbound/trunk@159 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-02 09:03:14 +00:00
Wouter Wijngaards 3a1187b2f6 Small changes, nicer deallocation, nonblocking.
git-svn-id: file:///svn/unbound/trunk@158 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-03-01 15:50:57 +00:00
Wouter Wijngaards a3abab528b Cast to make warning disappear.
git-svn-id: file:///svn/unbound/trunk@157 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-27 15:46:28 +00:00
Wouter Wijngaards 06af702c20 quieter.
git-svn-id: file:///svn/unbound/trunk@156 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-27 14:28:20 +00:00
Wouter Wijngaards 8b4c06230c New test.
git-svn-id: file:///svn/unbound/trunk@155 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-27 13:55:09 +00:00
Wouter Wijngaards 9c52b0a7bd Pushback when full makes all threads help out.
git-svn-id: file:///svn/unbound/trunk@154 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-27 11:25:44 +00:00
Wouter Wijngaards 3f6f9e5a7c Random outgoing port ranges.
git-svn-id: file:///svn/unbound/trunk@153 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-27 10:46:35 +00:00
Wouter Wijngaards 0e3a023540 Signal solution
git-svn-id: file:///svn/unbound/trunk@152 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-27 10:33:04 +00:00
Wouter Wijngaards 78d01c6f48 Fork if no threading is available.
git-svn-id: file:///svn/unbound/trunk@151 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-27 09:41:34 +00:00
Wouter Wijngaards 82b4130aae Nicer thread alternatives.
git-svn-id: file:///svn/unbound/trunk@150 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-27 09:28:53 +00:00
Wouter Wijngaards 04e0a37e53 - don't open pipes that are unused (no pipes if 1 thread only).
- SIGTERM.


git-svn-id: file:///svn/unbound/trunk@149 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-26 16:18:35 +00:00
Wouter Wijngaards af2f95454a Makes compile and test pass.
git-svn-id: file:///svn/unbound/trunk@148 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-26 16:11:32 +00:00
Wouter Wijngaards 1dca586a28 TODO item for later.
Doxygen fixes and lint fix.


git-svn-id: file:///svn/unbound/trunk@147 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-26 16:05:18 +00:00
Wouter Wijngaards e4d39152b3 Threading.
git-svn-id: file:///svn/unbound/trunk@146 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-26 14:49:11 +00:00
Wouter Wijngaards e679f51ed9 Use random (thread safe version).
git-svn-id: file:///svn/unbound/trunk@145 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-26 09:42:05 +00:00
Wouter Wijngaards 7ccdfe8f7c Random nrs.
git-svn-id: file:///svn/unbound/trunk@144 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-23 16:40:58 +00:00
Wouter Wijngaards 64f8b35c9e random code cleaned up.
git-svn-id: file:///svn/unbound/trunk@143 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-23 16:27:10 +00:00
Wouter Wijngaards 5f92286f32 Commit of straight random.c, modifications can show up in svn.
git-svn-id: file:///svn/unbound/trunk@142 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-23 15:48:25 +00:00
Wouter Wijngaards f0a6f5130f daemonize by default. -d to debugmode.
git-svn-id: file:///svn/unbound/trunk@141 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-23 15:23:33 +00:00
Wouter Wijngaards a92f1cf9ac Fixup.
git-svn-id: file:///svn/unbound/trunk@140 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-23 13:59:45 +00:00
Wouter Wijngaards 7e4700ec5f config file: directory, logfile, pidfile. And they work too.
and log_warn.


git-svn-id: file:///svn/unbound/trunk@139 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-23 13:38:54 +00:00
Wouter Wijngaards c4d87f4f5d New config options.
git-svn-id: file:///svn/unbound/trunk@138 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-23 11:00:55 +00:00
Wouter Wijngaards 84053077eb Ports are shared and you can kill -HUP to do a reload (of config file).
git-svn-id: file:///svn/unbound/trunk@137 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-23 10:04:50 +00:00
Wouter Wijngaards d5de0d10d5 daemon code.
git-svn-id: file:///svn/unbound/trunk@136 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-22 16:22:54 +00:00
Wouter Wijngaards 618ef6acbb config file is used.
git-svn-id: file:///svn/unbound/trunk@135 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-22 13:36:29 +00:00
Wouter Wijngaards a36b64a9e7 manpage , fd-setsize for minievent.
git-svn-id: file:///svn/unbound/trunk@134 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-21 09:52:46 +00:00
Wouter Wijngaards 2534d6b120 config file param.
git-svn-id: file:///svn/unbound/trunk@133 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-20 16:19:00 +00:00
Wouter Wijngaards a87e39c8e6 locks.h doxygen not enforced, it looks bad.
no lint and doxygen on yacc and lex generated files.
added doc to config_file.h


git-svn-id: file:///svn/unbound/trunk@132 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-20 15:47:27 +00:00
Wouter Wijngaards 4e33e351a3 Config file.
git-svn-id: file:///svn/unbound/trunk@131 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-20 15:28:27 +00:00
Wouter Wijngaards d6a8a0c37b Solaris threads support.
git-svn-id: file:///svn/unbound/trunk@130 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-20 13:25:29 +00:00
Wouter Wijngaards ed1f699789 Locks
git-svn-id: file:///svn/unbound/trunk@129 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-20 11:45:45 +00:00
Wouter Wijngaards 095a1dd291 Pthread configure.ac fu.
git-svn-id: file:///svn/unbound/trunk@128 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-19 15:32:07 +00:00
Wouter Wijngaards f0eeb50fee Fixup makedist script :)
git-svn-id: file:///svn/unbound/trunk@127 be551aaa-1e26-0410-a405-d3ace91eadb9
2007-02-19 10:30:28 +00:00
49 changed files with 5035 additions and 290 deletions
+18 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 */
+89
View File
@@ -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.
+7
View File
@@ -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
+5
View File
@@ -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.
+75
View File
@@ -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
View File
@@ -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
+109
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+5 -2
View File
@@ -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.
+605
View File
@@ -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 */
+323
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2
View File
@@ -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':
+42
View File
@@ -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;
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+191
View File
@@ -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
View File
@@ -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 */
+179
View File
@@ -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;
}
+140
View File
@@ -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 */
+176
View File
@@ -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; }
%%
+241
View File
@@ -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 */
+89
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 */
+38
View File
@@ -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;
}
+17
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+88
View File
@@ -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 */