diff options
author | sthen <sthen@openbsd.org> | 2013-09-03 09:21:33 +0000 |
---|---|---|
committer | sthen <sthen@openbsd.org> | 2013-09-03 09:21:33 +0000 |
commit | 12455795acacf9190d3c7cb7b343ea7bf28e940b (patch) | |
tree | 27c397e882163b9e51c4e2e96ccbd5bfa300205d /usr.sbin | |
parent | Only free the per-protocol descriptor if a touchpad cannot be correctly (diff) | |
download | wireguard-openbsd-12455795acacf9190d3c7cb7b343ea7bf28e940b.tar.xz wireguard-openbsd-12455795acacf9190d3c7cb7b343ea7bf28e940b.zip |
update to NSD 3.2.16, ok deraadt@ brad@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/nsd/acx_nlnetlabs.m4 | 38 | ||||
-rw-r--r-- | usr.sbin/nsd/compat/snprintf.c | 1750 | ||||
-rw-r--r-- | usr.sbin/nsd/configlexer.lex | 4 | ||||
-rw-r--r-- | usr.sbin/nsd/difffile.c | 8 | ||||
-rw-r--r-- | usr.sbin/nsd/ipc.c | 12 | ||||
-rw-r--r-- | usr.sbin/nsd/namedb.c | 1 | ||||
-rw-r--r-- | usr.sbin/nsd/netio.c | 7 | ||||
-rw-r--r-- | usr.sbin/nsd/netio.h | 7 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.conf.sample.in | 19 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.h | 6 | ||||
-rw-r--r-- | usr.sbin/nsd/options.c | 5 | ||||
-rw-r--r-- | usr.sbin/nsd/options.h | 7 | ||||
-rw-r--r-- | usr.sbin/nsd/rrl.c | 83 | ||||
-rw-r--r-- | usr.sbin/nsd/rrl.h | 11 | ||||
-rw-r--r-- | usr.sbin/nsd/xfrd-disk.c | 26 | ||||
-rw-r--r-- | usr.sbin/nsd/xfrd.c | 120 | ||||
-rw-r--r-- | usr.sbin/nsd/xfrd.h | 1 |
17 files changed, 1271 insertions, 834 deletions
diff --git a/usr.sbin/nsd/acx_nlnetlabs.m4 b/usr.sbin/nsd/acx_nlnetlabs.m4 index e90c81ea02a..719112645aa 100644 --- a/usr.sbin/nsd/acx_nlnetlabs.m4 +++ b/usr.sbin/nsd/acx_nlnetlabs.m4 @@ -2,7 +2,10 @@ # Copyright 2009, Wouter Wijngaards, NLnet Labs. # BSD licensed. # -# Version 21 +# Version 24 +# 2013-06-25 FLTO has --disable-flto option. +# 2013-05-03 Update W32_SLEEP for newer mingw that links but not defines it. +# 2013-03-22 Fix ACX_RSRC_VERSION for long version numbers. # 2012-02-09 Fix AHX_MEMCMP_BROKEN with undef in compat/memcmp.h. # 2012-01-20 Fix COMPILER_FLAGS_UNBOUND for gcc 4.6.2 assigned-not-used-warns. # 2011-12-05 Fix getaddrinfowithincludes on windows with fedora16 mingw32-gcc. @@ -101,7 +104,7 @@ dnl Calculate comma separated windows-resource numbers from package version. dnl Picks the first three(,0) or four numbers out of the name. dnl $1: variable for the result AC_DEFUN([ACX_RSRC_VERSION], -[$1=[`echo $PACKAGE_VERSION | sed -e 's/^[^0-9]*\([0-9]\)[^0-9]*\([0-9]\)[^0-9]*\([0-9]\)[^0-9]*\([0-9]\).*$/\1,\2,\3,\4/' -e 's/^[^0-9]*\([0-9]\)[^0-9]*\([0-9]\)[^0-9]*\([0-9]\)[^0-9]*$/\1,\2,\3,0/' `] +[$1=[`echo $PACKAGE_VERSION | sed -e 's/^[^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\).*$/\1,\2,\3,\4/' -e 's/^[^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\)[^0-9]*$/\1,\2,\3,0/' `] ]) dnl Routine to help check for compiler flags. @@ -405,19 +408,22 @@ int test() { dnl Check if CC supports -flto. dnl in a way that supports clang and suncc (that flag does something else, dnl but fails to link). It sets it in CFLAGS if it works. -AC_DEFUN([ACX_CHECK_FLTO], -[AC_MSG_CHECKING([if $CC supports -flto]) -BAKCFLAGS="$CFLAGS" -CFLAGS="$CFLAGS -flto" -AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [ - if $CC $CFLAGS -o conftest conftest.c 2>&1 | grep "warning: no debug symbols in executable" >/dev/null; then - CFLAGS="$BAKCFLAGS" - AC_MSG_RESULT(no) - else - AC_MSG_RESULT(yes) - fi - rm -f conftest conftest.c conftest.o -], [CFLAGS="$BAKCFLAGS" ; AC_MSG_RESULT(no)]) +AC_DEFUN([ACX_CHECK_FLTO], [ + AC_ARG_ENABLE([flto], AS_HELP_STRING([--disable-flto], [Disable link-time optimization])) + AS_IF([test "x$enable_flto" != "xno"], [ + AC_MSG_CHECKING([if $CC supports -flto]) + BAKCFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -flto" + AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [ + if $CC $CFLAGS -o conftest conftest.c 2>&1 | grep "warning: no debug symbols in executable" >/dev/null; then + CFLAGS="$BAKCFLAGS" + AC_MSG_RESULT(no) + else + AC_MSG_RESULT(yes) + fi + rm -f conftest conftest.c conftest.o + ], [CFLAGS="$BAKCFLAGS" ; AC_MSG_RESULT(no)]) + ]) ]) dnl Check the printf-format attribute (if any) @@ -1208,7 +1214,7 @@ struct tm *gmtime_r(const time_t *timep, struct tm *result); dnl provide w32 compat definition for sleep AC_DEFUN([AHX_CONFIG_W32_SLEEP], [ -#ifndef HAVE_SLEEP +#if !defined(HAVE_SLEEP) || defined(HAVE_WINDOWS_H) #define sleep(x) Sleep((x)*1000) /* on win32 */ #endif /* HAVE_SLEEP */ ]) diff --git a/usr.sbin/nsd/compat/snprintf.c b/usr.sbin/nsd/compat/snprintf.c index 52429edf06d..65959309c9c 100644 --- a/usr.sbin/nsd/compat/snprintf.c +++ b/usr.sbin/nsd/compat/snprintf.c @@ -1,770 +1,1036 @@ -#include <config.h> - -#ifndef HAVE_SNPRINTF +/* snprintf - compatibility implementation of snprintf, vsnprintf + * + * Copyright (c) 2013, 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 <stdio.h> #include <ctype.h> -#include <sys/types.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <errno.h> +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif -/* Define this as a fall through, HAVE_STDARG_H is probably already set */ +/* for test */ +/* #define SNPRINTF_TEST 1 */ +#ifdef SNPRINTF_TEST +#define snprintf my_snprintf +#define vsnprintf my_vsnprintf +#endif /* SNPRINTF_TEST */ -#define HAVE_VARARGS_H +int snprintf(char* str, size_t size, const char* format, ...); +int vsnprintf(char* str, size_t size, const char* format, va_list arg); -/************************************************************** - * Original: - * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 - * A bombproof version of doprnt (dopr) included. - * Sigh. This sort of thing is always nasty do deal with. Note that - * the version here does not include floating point... - * - * snprintf() is used instead of sprintf() as it does limit checks - * for string length. This covers a nasty loophole. +/** + * Very portable snprintf implementation, limited in functionality, + * esp. for %[capital] %[nonportable] and so on. Reduced float functionality, + * mostly in formatting and range (e+-16), for %f and %g. * - * The other functions are there to prevent NULL pointers from - * causing nast effects. - * - * More Recently: - * Brandon Long (blong@fiction.net) 9/15/96 for mutt 0.43 - * This was ugly. It is still ugly. I opted out of floating point - * numbers, but the formatter understands just about everything - * from the normal C string format, at least as far as I can tell from - * the Solaris 2.5 printf(3S) man page. - * - * Brandon Long (blong@fiction.net) 10/22/97 for mutt 0.87.1 - * Ok, added some minimal floating point support, which means this - * probably requires libm on most operating systems. Don't yet - * support the exponent (e,E) and sigfig (g,G). Also, fmtint() - * was pretty badly broken, it just wasn't being exercised in ways - * which showed it, so that's been fixed. Also, formated the code - * to mutt conventions, and removed dead code left over from the - * original. Also, there is now a builtin-test, just compile with: - * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm - * and run snprintf for results. - * - **************************************************************/ + * %s, %d, %u, %i, %x, %c, %n and %% are fully supported. + * This includes width, precision, flags 0- +, and *(arg for wid,prec). + * %f, %g, %m, %p have reduced support, support for wid,prec,flags,*, but + * less floating point range, no %e formatting for %g. + */ +int snprintf(char* str, size_t size, const char* format, ...) +{ + int r; + va_list args; + va_start(args, format); + r = vsnprintf(str, size, format, args); + va_end(args); + return r; +} +/** add padding to string */ +static void +print_pad(char** at, size_t* left, int* ret, char p, int num) +{ + while(num--) { + if(*left > 1) { + *(*at)++ = p; + (*left)--; + } + (*ret)++; + } +} -/* varargs declarations: */ +/** get negative symbol, 0 if none */ +static char +get_negsign(int negative, int plus, int space) +{ + if(negative) + return '-'; + if(plus) + return '+'; + if(space) + return ' '; + return 0; +} -#if defined(HAVE_STDARG_H) -# include <stdarg.h> -# define HAVE_STDARGS /* let's hope that works everywhere (mj) */ -# define VA_LOCAL_DECL va_list ap -# define VA_START(f) va_start(ap, f) -# define VA_SHIFT(v,t) ; /* no-op for ANSI */ -# define VA_END va_end(ap) -#else -# if defined(HAVE_VARARGS_H) -# include <varargs.h> -# undef HAVE_STDARGS -# define VA_LOCAL_DECL va_list ap -# define VA_START(f) va_start(ap) /* f is ignored! */ -# define VA_SHIFT(v,t) v = va_arg(ap,t) -# define VA_END va_end(ap) -# else -/*XX ** NO VARARGS ** XX*/ -# endif -#endif +#define PRINT_DEC_BUFSZ 32 /* 20 is enough for 64 bit decimals */ +/** print decimal into buffer, returns length */ +static int +print_dec(char* buf, int max, unsigned int value) +{ + int i = 0; + if(value == 0) { + if(max > 0) { + buf[0] = '0'; + i = 1; + } + } else while(value && i < max) { + buf[i++] = '0' + value % 10; + value /= 10; + } + return i; +} + +/** print long decimal into buffer, returns length */ +static int +print_dec_l(char* buf, int max, unsigned long value) +{ + int i = 0; + if(value == 0) { + if(max > 0) { + buf[0] = '0'; + i = 1; + } + } else while(value && i < max) { + buf[i++] = '0' + value % 10; + value /= 10; + } + return i; +} + +/** print long decimal into buffer, returns length */ +static int +print_dec_ll(char* buf, int max, unsigned long long value) +{ + int i = 0; + if(value == 0) { + if(max > 0) { + buf[0] = '0'; + i = 1; + } + } else while(value && i < max) { + buf[i++] = '0' + value % 10; + value /= 10; + } + return i; +} + +/** print hex into buffer, returns length */ +static int +print_hex(char* buf, int max, unsigned int value) +{ + const char* h = "0123456789abcdef"; + int i = 0; + if(value == 0) { + if(max > 0) { + buf[0] = '0'; + i = 1; + } + } else while(value && i < max) { + buf[i++] = h[value & 0x0f]; + value >>= 4; + } + return i; +} + +/** print long hex into buffer, returns length */ +static int +print_hex_l(char* buf, int max, unsigned long value) +{ + const char* h = "0123456789abcdef"; + int i = 0; + if(value == 0) { + if(max > 0) { + buf[0] = '0'; + i = 1; + } + } else while(value && i < max) { + buf[i++] = h[value & 0x0f]; + value >>= 4; + } + return i; +} + +/** print long long hex into buffer, returns length */ +static int +print_hex_ll(char* buf, int max, unsigned long long value) +{ + const char* h = "0123456789abcdef"; + int i = 0; + if(value == 0) { + if(max > 0) { + buf[0] = '0'; + i = 1; + } + } else while(value && i < max) { + buf[i++] = h[value & 0x0f]; + value >>= 4; + } + return i; +} + +/** copy string into result, reversed */ +static void +spool_str_rev(char** at, size_t* left, int* ret, const char* buf, int len) +{ + int i = len; + while(i) { + if(*left > 1) { + *(*at)++ = buf[--i]; + (*left)--; + } else --i; + (*ret)++; + } +} + +/** copy string into result */ +static void +spool_str(char** at, size_t* left, int* ret, const char* buf, int len) +{ + int i; + for(i=0; i<len; i++) { + if(*left > 1) { + *(*at)++ = buf[i]; + (*left)--; + } + (*ret)++; + } +} + +/** print number formatted */ +static void +print_num(char** at, size_t* left, int* ret, int minw, int precision, + int prgiven, int zeropad, int minus, int plus, int space, + int zero, int negative, char* buf, int len) +{ + int w = len; /* excludes minus sign */ + char s = get_negsign(negative, plus, space); + if(minus) { + /* left adjust the number into the field, space padding */ + /* calc numw = [sign][zeroes][number] */ + int numw = w; + if(precision == 0 && zero) numw = 0; + if(numw < precision) numw = precision; + if(s) numw++; + + /* sign */ + if(s) print_pad(at, left, ret, s, 1); + + /* number */ + if(precision == 0 && zero) { + /* "" for the number */ + } else { + if(w < precision) + print_pad(at, left, ret, '0', precision - w); + spool_str_rev(at, left, ret, buf, len); + } + /* spaces */ + if(numw < minw) + print_pad(at, left, ret, ' ', minw - numw); + } else { + /* pad on the left of the number */ + /* calculate numw has width of [sign][zeroes][number] */ + int numw = w; + if(precision == 0 && zero) numw = 0; + if(numw < precision) numw = precision; + if(!prgiven && zeropad && numw < minw) numw = minw; + else if(s) numw++; + + /* pad with spaces */ + if(numw < minw) + print_pad(at, left, ret, ' ', minw - numw); + /* print sign (and one less zeropad if so) */ + if(s) { + print_pad(at, left, ret, s, 1); + numw--; + } + /* pad with zeroes */ + if(w < numw) + print_pad(at, left, ret, '0', numw - w); + if(precision == 0 && zero) + return; + /* print the characters for the value */ + spool_str_rev(at, left, ret, buf, len); + } +} + +/** print %d and %i */ +static void +print_num_d(char** at, size_t* left, int* ret, int value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = (value < 0); + int zero = (value == 0); + int len = print_dec(buf, (int)sizeof(buf), + (unsigned int)(negative?-value:value)); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} + +/** print %ld and %li */ +static void +print_num_ld(char** at, size_t* left, int* ret, long value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = (value < 0); + int zero = (value == 0); + int len = print_dec_l(buf, (int)sizeof(buf), + (unsigned long)(negative?-value:value)); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} -int snprintf (char *str, size_t count, const char *fmt, ...); -int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); +/** print %lld and %lli */ +static void +print_num_lld(char** at, size_t* left, int* ret, long long value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = (value < 0); + int zero = (value == 0); + int len = print_dec_ll(buf, (int)sizeof(buf), + (unsigned long long)(negative?-value:value)); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} -static void dopr (char *buffer, size_t maxlen, const char *format, - va_list args); -static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, - char *value, int flags, int min, int max); -static void fmtint (char *buffer, size_t *currlen, size_t maxlen, - long value, int base, int min, int max, int flags); -static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, - long double fvalue, int min, int max, int flags); -static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c ); +/** print %u */ +static void +print_num_u(char** at, size_t* left, int* ret, unsigned int value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); + int len = print_dec(buf, (int)sizeof(buf), value); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} -int vsnprintf (char *str, size_t count, const char *fmt, va_list args) +/** print %lu */ +static void +print_num_lu(char** at, size_t* left, int* ret, unsigned long value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) { - str[0] = 0; - dopr(str, count, fmt, args); - return(strlen(str)); + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); + int len = print_dec_l(buf, (int)sizeof(buf), value); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); } -/* VARARGS3 */ -#ifdef HAVE_STDARGS -int snprintf (char *str,size_t count,const char *fmt,...) +/** print %llu */ +static void +print_num_llu(char** at, size_t* left, int* ret, unsigned long long value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); + int len = print_dec_ll(buf, (int)sizeof(buf), value); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} + +/** print %x */ +static void +print_num_x(char** at, size_t* left, int* ret, unsigned int value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); + int len = print_hex(buf, (int)sizeof(buf), value); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} + +/** print %lx */ +static void +print_num_lx(char** at, size_t* left, int* ret, unsigned long value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); + int len = print_hex_l(buf, (int)sizeof(buf), value); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} + +/** print %llx */ +static void +print_num_llx(char** at, size_t* left, int* ret, unsigned long long value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); + int len = print_hex_ll(buf, (int)sizeof(buf), value); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} + +/** print %llp */ +static void +print_num_llp(char** at, size_t* left, int* ret, void* value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); +#if defined(UINTPTR_MAX) && defined(UINT32_MAX) && (UINTPTR_MAX == UINT32_MAX) + /* avoid warning about upcast on 32bit systems */ + unsigned long long llvalue = (unsigned long)value; #else -int snprintf (va_alist) va_dcl + unsigned long long llvalue = (unsigned long long)value; #endif + int len = print_hex_ll(buf, (int)sizeof(buf), llvalue); + if(zero) { + buf[0]=')'; + buf[1]='l'; + buf[2]='i'; + buf[3]='n'; + buf[4]='('; + len = 5; + } else { + /* put '0x' in front of the (reversed) buffer result */ + if(len < PRINT_DEC_BUFSZ) + buf[len++] = 'x'; + if(len < PRINT_DEC_BUFSZ) + buf[len++] = '0'; + } + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} + +#define PRINT_FLOAT_BUFSZ 64 /* xx.yy with 20.20 about the max */ +/** spool remainder after the decimal point to buffer, in reverse */ +static int +print_remainder(char* buf, int max, double r, int prec) { -#ifndef HAVE_STDARGS - char *str; - size_t count; - char *fmt; -#endif - VA_LOCAL_DECL; - - VA_START (fmt); - VA_SHIFT (str, char *); - VA_SHIFT (count, size_t ); - VA_SHIFT (fmt, char *); - (void) vsnprintf(str, count, fmt, ap); - VA_END; - return(strlen(str)); -} - -/* - * dopr(): poor man's version of doprintf - */ + unsigned long long cap = 1; + unsigned long long value; + int len, i; + if(prec > 19) prec = 19; /* max we can do */ + if(max < prec) return 0; + for(i=0; i<prec; i++) { + cap *= 10; + } + r *= (double)cap; + value = (unsigned long long)r; + /* see if we need to round up */ + if(((unsigned long long)((r - (double)value)*10.0)) >= 5) { + value++; + /* that might carry to numbers before the comma, if so, + * just ignore that rounding. failure because 64bitprintout */ + if(value >= cap) + value = cap-1; + } + len = print_dec_ll(buf, max, value); + while(len < prec) { /* pad with zeroes, e.g. if 0.0012 */ + buf[len++] = '0'; + } + if(len < max) + buf[len++] = '.'; + return len; +} -/* format read states */ -#define DP_S_DEFAULT 0 -#define DP_S_FLAGS 1 -#define DP_S_MIN 2 -#define DP_S_DOT 3 -#define DP_S_MAX 4 -#define DP_S_MOD 5 -#define DP_S_CONV 6 -#define DP_S_DONE 7 - -/* format flags - Bits */ -#define DP_F_MINUS 1 -#define DP_F_PLUS 2 -#define DP_F_SPACE 4 -#define DP_F_NUM 8 -#define DP_F_ZERO 16 -#define DP_F_UP 32 - -/* Conversion Flags */ -#define DP_C_SHORT 1 -#define DP_C_LONG 2 -#define DP_C_LDOUBLE 3 - -#define char_to_int(p) (p - '0') -#define MAX(p,q) ((p >= q) ? p : q) - -static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) -{ - char ch; - long value; - long double fvalue; - char *strvalue; - int min; - int max; - int state; - int flags; - int cflags; - size_t currlen; - - state = DP_S_DEFAULT; - currlen = flags = cflags = min = 0; - max = -1; - ch = *format++; - - while (state != DP_S_DONE) - { - if ((ch == '\0') || (currlen >= maxlen)) - state = DP_S_DONE; - - switch(state) - { - case DP_S_DEFAULT: - if (ch == '%') - state = DP_S_FLAGS; - else - dopr_outch (buffer, &currlen, maxlen, ch); - ch = *format++; - break; - case DP_S_FLAGS: - switch (ch) - { - case '-': - flags |= DP_F_MINUS; - ch = *format++; - break; - case '+': - flags |= DP_F_PLUS; - ch = *format++; - break; - case ' ': - flags |= DP_F_SPACE; - ch = *format++; - break; - case '#': - flags |= DP_F_NUM; - ch = *format++; - break; - case '0': - flags |= DP_F_ZERO; - ch = *format++; - break; - default: - state = DP_S_MIN; - break; - } - break; - case DP_S_MIN: - if (isdigit(ch)) - { - min = 10*min + char_to_int (ch); - ch = *format++; - } - else if (ch == '*') - { - min = va_arg (args, int); - ch = *format++; - state = DP_S_DOT; - } - else - state = DP_S_DOT; - break; - case DP_S_DOT: - if (ch == '.') - { - state = DP_S_MAX; - ch = *format++; - } - else - state = DP_S_MOD; - break; - case DP_S_MAX: - if (isdigit(ch)) - { - if (max < 0) - max = 0; - max = 10*max + char_to_int (ch); - ch = *format++; - } - else if (ch == '*') - { - max = va_arg (args, int); - ch = *format++; - state = DP_S_MOD; - } - else - state = DP_S_MOD; - break; - case DP_S_MOD: - /* Currently, we don't support Long Long, bummer */ - switch (ch) - { - case 'h': - cflags = DP_C_SHORT; - ch = *format++; - break; - case 'l': - cflags = DP_C_LONG; - ch = *format++; - break; - case 'L': - cflags = DP_C_LDOUBLE; - ch = *format++; - break; - default: - break; - } - state = DP_S_CONV; - break; - case DP_S_CONV: - switch (ch) - { - case 'd': - case 'i': - if (cflags == DP_C_SHORT) - value = va_arg (args, int); - else if (cflags == DP_C_LONG) - value = va_arg (args, long int); - else - value = va_arg (args, int); - fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); - break; - case 'o': - flags &= ~DP_F_PLUS; - if (cflags == DP_C_SHORT) - value = va_arg (args, unsigned int); - else if (cflags == DP_C_LONG) - value = va_arg (args, unsigned long int); - else - value = va_arg (args, unsigned int); - fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); - break; - case 'u': - flags &= ~DP_F_PLUS; - if (cflags == DP_C_SHORT) - value = va_arg (args, unsigned int); - else if (cflags == DP_C_LONG) - value = va_arg (args, unsigned long int); - else - value = va_arg (args, unsigned int); - fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); - break; - case 'X': - flags |= DP_F_UP; - case 'x': - flags &= ~DP_F_PLUS; - if (cflags == DP_C_SHORT) - value = va_arg (args, unsigned int); - else if (cflags == DP_C_LONG) - value = va_arg (args, unsigned long int); - else - value = va_arg (args, unsigned int); - fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); - break; - case 'f': - if (cflags == DP_C_LDOUBLE) - fvalue = va_arg (args, long double); - else - fvalue = va_arg (args, double); - /* um, floating point? */ - fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); - break; - case 'E': - flags |= DP_F_UP; - case 'e': - if (cflags == DP_C_LDOUBLE) - fvalue = va_arg (args, long double); - else - fvalue = va_arg (args, double); - break; - case 'G': - flags |= DP_F_UP; - case 'g': - if (cflags == DP_C_LDOUBLE) - fvalue = va_arg (args, long double); - else - fvalue = va_arg (args, double); - break; - case 'c': - dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); - break; - case 's': - strvalue = va_arg (args, char *); - if (max < 0) - max = maxlen; /* ie, no max */ - fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); - break; - case 'p': - strvalue = va_arg (args, void *); - fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); - break; - case 'n': - if (cflags == DP_C_SHORT) - { - short int *num; - num = va_arg (args, short int *); - *num = currlen; - } - else if (cflags == DP_C_LONG) - { - long int *num; - num = va_arg (args, long int *); - *num = currlen; - } - else - { - int *num; - num = va_arg (args, int *); - *num = currlen; - } - break; - case '%': - dopr_outch (buffer, &currlen, maxlen, ch); - break; - case 'w': - /* not supported yet, treat as next char */ - ch = *format++; - break; - default: - /* Unknown, skip */ - break; - } - ch = *format++; - state = DP_S_DEFAULT; - flags = cflags = min = 0; - max = -1; - break; - case DP_S_DONE: - break; - default: - /* hmm? */ - break; /* some picky compilers need this */ - } - } - if (currlen < maxlen - 1) - buffer[currlen] = '\0'; - else - buffer[maxlen - 1] = '\0'; -} - -static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, - char *value, int flags, int min, int max) -{ - int padlen, strln; /* amount to pad */ - int cnt = 0; - - if (value == 0) - { - value = "<NULL>"; - } - - for (strln = 0; value[strln]; ++strln); /* strlen */ - padlen = min - strln; - if (padlen < 0) - padlen = 0; - if (flags & DP_F_MINUS) - padlen = -padlen; /* Left Justify */ - - while ((padlen > 0) && (cnt < max)) - { - dopr_outch (buffer, currlen, maxlen, ' '); - --padlen; - ++cnt; - } - while (*value && (cnt < max)) - { - dopr_outch (buffer, currlen, maxlen, *value++); - ++cnt; - } - while ((padlen < 0) && (cnt < max)) - { - dopr_outch (buffer, currlen, maxlen, ' '); - ++padlen; - ++cnt; - } -} - -/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ - -static void fmtint (char *buffer, size_t *currlen, size_t maxlen, - long value, int base, int min, int max, int flags) -{ - int signvalue = 0; - unsigned long uvalue; - char convert[20]; - int place = 0; - int spadlen = 0; /* amount to space pad */ - int zpadlen = 0; /* amount to zero pad */ - int caps = 0; - - if (max < 0) - max = 0; - - uvalue = value; - if( value < 0 ) { - signvalue = '-'; - uvalue = -value; - } - else - if (flags & DP_F_PLUS) /* Do a sign (+/i) */ - signvalue = '+'; - else - if (flags & DP_F_SPACE) - signvalue = ' '; - - if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ - - do { - convert[place++] = - (caps? "0123456789ABCDEF":"0123456789abcdef") - [uvalue % (unsigned)base ]; - uvalue = (uvalue / (unsigned)base ); - } while(uvalue && (place < 20)); - if (place == 20) place--; - convert[place] = 0; - - zpadlen = max - place; - spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); - if (zpadlen < 0) zpadlen = 0; - if (spadlen < 0) spadlen = 0; - if (flags & DP_F_ZERO) - { - zpadlen = MAX(zpadlen, spadlen); - spadlen = 0; - } - if (flags & DP_F_MINUS) - spadlen = -spadlen; /* Left Justifty */ - -#ifdef DEBUG_SNPRINTF - dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", - zpadlen, spadlen, min, max, place)); -#endif +/** spool floating point to buffer */ +static int +print_float(char* buf, int max, double value, int prec) +{ + /* as xxx.xxx if prec==0, no '.', with prec decimals after . */ + /* no conversion for NAN and INF, because we do not want to require + linking with -lm. */ + /* Thus, the conversions use 64bit integers to convert the numbers, + * which makes 19 digits before and after the decimal point the max */ + unsigned long long whole = (unsigned long long)value; + double remain = value - (double)whole; + int len = 0; + if(prec != 0) + len = print_remainder(buf, max, remain, prec); + len += print_dec_ll(buf+len, max-len, whole); + return len; +} - /* Spaces */ - while (spadlen > 0) - { - dopr_outch (buffer, currlen, maxlen, ' '); - --spadlen; - } - - /* Sign */ - if (signvalue) - dopr_outch (buffer, currlen, maxlen, signvalue); - - /* Zeros */ - if (zpadlen > 0) - { - while (zpadlen > 0) - { - dopr_outch (buffer, currlen, maxlen, '0'); - --zpadlen; - } - } - - /* Digits */ - while (place > 0) - dopr_outch (buffer, currlen, maxlen, convert[--place]); - - /* Left Justified spaces */ - while (spadlen < 0) { - dopr_outch (buffer, currlen, maxlen, ' '); - ++spadlen; - } -} - -static long double abs_val (long double value) -{ - long double result = value; - - if (value < 0) - result = -value; - - return result; -} - -static long double pow10 (int exp) -{ - long double result = 1; - - while (exp) - { - result *= 10; - exp--; - } - - return result; -} - -static long round (long double value) -{ - long intpart; - - intpart = value; - value = value - intpart; - if (value >= 0.5) - intpart++; - - return intpart; -} - -static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, - long double fvalue, int min, int max, int flags) -{ - int signvalue = 0; - long double ufvalue; - char iconvert[20]; - char fconvert[20]; - int iplace = 0; - int fplace = 0; - int padlen = 0; /* amount to pad */ - int zpadlen = 0; - int caps = 0; - long intpart; - long fracpart; - - /* - * AIX manpage says the default is 0, but Solaris says the default - * is 6, and sprintf on AIX defaults to 6 - */ - if (max < 0) - max = 6; - - ufvalue = abs_val (fvalue); - - if (fvalue < 0) - signvalue = '-'; - else - if (flags & DP_F_PLUS) /* Do a sign (+/i) */ - signvalue = '+'; - else - if (flags & DP_F_SPACE) - signvalue = ' '; - -#if 0 - if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ -#endif +/** print %f */ +static void +print_num_f(char** at, size_t* left, int* ret, double value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_FLOAT_BUFSZ]; + int negative = (value < 0); + int zero = 0; + int len; + if(!prgiven) precision = 6; + len = print_float(buf, (int)sizeof(buf), negative?-value:value, + precision); + print_num(at, left, ret, minw, 1, 0, zeropad, minus, + plus, space, zero, negative, buf, len); +} - intpart = ufvalue; +/* rudimentary %g support */ +static int +print_float_g(char* buf, int max, double value, int prec) +{ + unsigned long long whole = (unsigned long long)value; + double remain = value - (double)whole; + int before = 0; + int len = 0; + + /* number of digits before the decimal point */ + while(whole > 0) { + before++; + whole /= 10; + } + whole = (unsigned long long)value; + + if(prec > before && remain != 0.0) { + /* see if the last decimals are zero, if so, skip them */ + len = print_remainder(buf, max, remain, prec-before); + while(len > 0 && buf[0]=='0') { + memmove(buf, buf+1, --len); + } + } + len += print_dec_ll(buf+len, max-len, whole); + return len; +} - /* - * Sorry, we only support 9 digits past the decimal because of our - * conversion method - */ - if (max > 9) - max = 9; - /* We "cheat" by converting the fractional part to integer by - * multiplying by a factor of 10 - */ - fracpart = round ((pow10 (max)) * (ufvalue - intpart)); +/** print %g */ +static void +print_num_g(char** at, size_t* left, int* ret, double value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_FLOAT_BUFSZ]; + int negative = (value < 0); + int zero = 0; + int len; + if(!prgiven) precision = 6; + if(precision == 0) precision = 1; + len = print_float_g(buf, (int)sizeof(buf), negative?-value:value, + precision); + print_num(at, left, ret, minw, 1, 0, zeropad, minus, + plus, space, zero, negative, buf, len); +} - if (fracpart >= pow10 (max)) - { - intpart++; - fracpart -= pow10 (max); - } -#ifdef DEBUG_SNPRINTF - dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart)); -#endif +/** strnlen (compat implementation) */ +static int +my_strnlen(const char* s, int max) +{ + int i; + for(i=0; i<max; i++) + if(s[i]==0) + return i; + return max; +} - /* Convert integer part */ - do { - iconvert[iplace++] = - (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10]; - intpart = (intpart / 10); - } while(intpart && (iplace < 20)); - if (iplace == 20) iplace--; - iconvert[iplace] = 0; - - /* Convert fractional part */ - do { - fconvert[fplace++] = - (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10]; - fracpart = (fracpart / 10); - } while(fracpart && (fplace < 20)); - if (fplace == 20) fplace--; - fconvert[fplace] = 0; - - /* -1 for decimal point, another -1 if we are printing a sign */ - padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); - zpadlen = max - fplace; - if (zpadlen < 0) - zpadlen = 0; - if (padlen < 0) - padlen = 0; - if (flags & DP_F_MINUS) - padlen = -padlen; /* Left Justifty */ - - if ((flags & DP_F_ZERO) && (padlen > 0)) - { - if (signvalue) - { - dopr_outch (buffer, currlen, maxlen, signvalue); - --padlen; - signvalue = 0; - } - while (padlen > 0) - { - dopr_outch (buffer, currlen, maxlen, '0'); - --padlen; - } - } - while (padlen > 0) - { - dopr_outch (buffer, currlen, maxlen, ' '); - --padlen; - } - if (signvalue) - dopr_outch (buffer, currlen, maxlen, signvalue); - - while (iplace > 0) - dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); - - /* - * Decimal point. This should probably use locale to find the correct - * char to print out. - */ - dopr_outch (buffer, currlen, maxlen, '.'); - - while (zpadlen > 0) - { - dopr_outch (buffer, currlen, maxlen, '0'); - --zpadlen; - } - - while (fplace > 0) - dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); - - while (padlen < 0) - { - dopr_outch (buffer, currlen, maxlen, ' '); - ++padlen; - } -} - -static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) -{ - if (*currlen < maxlen) - buffer[(*currlen)++] = c; -} - -#ifdef TEST_SNPRINTF -#ifndef LONG_STRING -#define LONG_STRING 1024 -#endif -int main (void) -{ - char buf1[LONG_STRING]; - char buf2[LONG_STRING]; - char *fp_fmt[] = { - "%-1.5f", - "%1.5f", - "%123.9f", - "%10.5f", - "% 10.5f", - "%+22.9f", - "%+4.9f", - "%01.3f", - "%4f", - "%3.1f", - "%3.2f", - NULL - }; - double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, - 0.9996, 1.996, 4.136, 0}; - char *int_fmt[] = { - "%-1.5d", - "%1.5d", - "%123.9d", - "%5.5d", - "%10.5d", - "% 10.5d", - "%+22.33d", - "%01.3d", - "%4d", - NULL - }; - long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; - int x, y; - int fail = 0; - int num = 0; - - printf ("Testing snprintf format codes against system sprintf...\n"); - - for (x = 0; fp_fmt[x] != NULL ; x++) - for (y = 0; fp_nums[y] != 0 ; y++) - { - snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]); - sprintf (buf2, fp_fmt[x], fp_nums[y]); - if (strcmp (buf1, buf2)) - { - printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", - fp_fmt[x], buf1, buf2); - fail++; - } - num++; - } - - for (x = 0; int_fmt[x] != NULL ; x++) - for (y = 0; int_nums[y] != 0 ; y++) - { - snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]); - sprintf (buf2, int_fmt[x], int_nums[y]); - if (strcmp (buf1, buf2)) - { - printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", - int_fmt[x], buf1, buf2); - fail++; - } - num++; - } - printf ("%d tests failed out of %d.\n", fail, num); +/** print %s */ +static void +print_str(char** at, size_t* left, int* ret, char* s, + int minw, int precision, int prgiven, int minus) +{ + int w; + /* with prec: no more than x characters from this string, stop at 0 */ + if(prgiven) + w = my_strnlen(s, precision); + else w = (int)strlen(s); /* up to the nul */ + if(w < minw && !minus) + print_pad(at, left, ret, ' ', minw - w); + spool_str(at, left, ret, s, w); + if(w < minw && minus) + print_pad(at, left, ret, ' ', minw - w); +} + +/** print %c */ +static void +print_char(char** at, size_t* left, int* ret, int c, + int minw, int minus) +{ + if(1 < minw && !minus) + print_pad(at, left, ret, ' ', minw - 1); + print_pad(at, left, ret, c, 1); + if(1 < minw && minus) + print_pad(at, left, ret, ' ', minw - 1); +} + + +/** + * Print to string. + * str: string buffer for result. result will be null terminated. + * size: size of the buffer. null is put inside buffer. + * format: printf format string. + * arg: '...' arguments to print. + * returns number of characters. a null is printed after this. + * return number of bytes that would have been written + * if the buffer had been large enough. + * + * supported format specifiers: + * %s, %u, %d, %x, %i, %f, %g, %c, %p, %n. + * length: l, ll (for d, u, x). + * precision: 6.6d (for d, u, x) + * %f, %g precisions, 0.3f + * %20s, '.*s' + * and %%. + */ +int vsnprintf(char* str, size_t size, const char* format, va_list arg) +{ + char* at = str; + size_t left = size; + int ret = 0; + const char* fmt = format; + int conv, minw, precision, prgiven, zeropad, minus, plus, space, length; + while(*fmt) { + /* copy string before % */ + while(*fmt && *fmt!='%') { + if(left > 1) { + *at++ = *fmt++; + left--; + } else fmt++; + ret++; + } + + /* see if we are at end */ + if(!*fmt) break; + + /* fetch next argument % designation from format string */ + fmt++; /* skip the '%' */ + + /********************************/ + /* get the argument designation */ + /********************************/ + /* we must do this vararg stuff inside this function for + * portability. Hence, get_designation, and print_designation + * are not their own functions. */ + + /* printout designation: + * conversion specifier: x, d, u, s, c, n, m, p + * flags: # not supported + * 0 zeropad (on the left) + * - left adjust (right by default) + * ' ' printspace for positive number (in - position). + * + alwayssign + * fieldwidth: [1-9][0-9]* minimum field width. + * if this is * then type int next argument specifies the minwidth. + * if this is negative, the - flag is set (with positive width). + * precision: period[digits]*, %.2x. + * if this is * then type int next argument specifies the precision. + * just '.' or negative value means precision=0. + * this is mindigits to print for d, i, u, x + * this is aftercomma digits for f + * this is max number significant digits for g + * maxnumber characters to be printed for s + * length: 0-none (int), 1-l (long), 2-ll (long long) + * notsupported: hh (char), h (short), L (long double), q, j, z, t + * Does not support %m$ and *m$ argument designation as array indices. + * Does not support %#x + * + */ + minw = 0; + precision = 1; + prgiven = 0; + zeropad = 0; + minus = 0; + plus = 0; + space = 0; + length = 0; + + /* get flags in any order */ + for(;;) { + if(*fmt == '0') + zeropad = 1; + else if(*fmt == '-') + minus = 1; + else if(*fmt == '+') + plus = 1; + else if(*fmt == ' ') + space = 1; + else break; + fmt++; + } + + /* field width */ + if(*fmt == '*') { + fmt++; /* skip char */ + minw = va_arg(arg, int); + if(minw < 0) { + minus = 1; + minw = -minw; + } + } else while(*fmt >= '0' && *fmt <= '9') { + minw = minw*10 + (*fmt++)-'0'; + } + + /* precision */ + if(*fmt == '.') { + fmt++; /* skip period */ + prgiven = 1; + precision = 0; + if(*fmt == '*') { + fmt++; /* skip char */ + precision = va_arg(arg, int); + if(precision < 0) + precision = 0; + } else while(*fmt >= '0' && *fmt <= '9') { + precision = precision*10 + (*fmt++)-'0'; + } + } + + /* length */ + if(*fmt == 'l') { + fmt++; /* skip char */ + length = 1; + if(*fmt == 'l') { + fmt++; /* skip char */ + length = 2; + } + } + + /* get the conversion */ + if(!*fmt) conv = 0; + else conv = *fmt++; + + /***********************************/ + /* print that argument designation */ + /***********************************/ + switch(conv) { + case 'i': + case 'd': + if(length == 0) + print_num_d(&at, &left, &ret, va_arg(arg, int), + minw, precision, prgiven, zeropad, minus, plus, space); + else if(length == 1) + print_num_ld(&at, &left, &ret, va_arg(arg, long), + minw, precision, prgiven, zeropad, minus, plus, space); + else if(length == 2) + print_num_lld(&at, &left, &ret, + va_arg(arg, long long), + minw, precision, prgiven, zeropad, minus, plus, space); + break; + case 'u': + if(length == 0) + print_num_u(&at, &left, &ret, + va_arg(arg, unsigned int), + minw, precision, prgiven, zeropad, minus, plus, space); + else if(length == 1) + print_num_lu(&at, &left, &ret, + va_arg(arg, unsigned long), + minw, precision, prgiven, zeropad, minus, plus, space); + else if(length == 2) + print_num_llu(&at, &left, &ret, + va_arg(arg, unsigned long long), + minw, precision, prgiven, zeropad, minus, plus, space); + break; + case 'x': + if(length == 0) + print_num_x(&at, &left, &ret, + va_arg(arg, unsigned int), + minw, precision, prgiven, zeropad, minus, plus, space); + else if(length == 1) + print_num_lx(&at, &left, &ret, + va_arg(arg, unsigned long), + minw, precision, prgiven, zeropad, minus, plus, space); + else if(length == 2) + print_num_llx(&at, &left, &ret, + va_arg(arg, unsigned long long), + minw, precision, prgiven, zeropad, minus, plus, space); + break; + case 's': + print_str(&at, &left, &ret, va_arg(arg, char*), + minw, precision, prgiven, minus); + break; + case 'c': + print_char(&at, &left, &ret, va_arg(arg, int), + minw, minus); + break; + case 'n': + *va_arg(arg, int*) = ret; + break; + case 'm': + print_str(&at, &left, &ret, strerror(errno), + minw, precision, prgiven, minus); + break; + case 'p': + print_num_llp(&at, &left, &ret, va_arg(arg, void*), + minw, precision, prgiven, zeropad, minus, plus, space); + break; + case '%': + print_pad(&at, &left, &ret, '%', 1); + break; + case 'f': + print_num_f(&at, &left, &ret, va_arg(arg, double), + minw, precision, prgiven, zeropad, minus, plus, space); + break; + case 'g': + print_num_g(&at, &left, &ret, va_arg(arg, double), + minw, precision, prgiven, zeropad, minus, plus, space); + break; + /* unknown */ + default: + case 0: break; + } + } + + /* zero terminate */ + if(left > 0) + *at = 0; + return ret; } -#endif /* SNPRINTF_TEST */ -#endif /* !HAVE_SNPRINTF */ +#ifdef SNPRINTF_TEST + +/** do tests */ +#undef snprintf +#define DOTEST(bufsz, result, retval, ...) do { \ + char buf[bufsz]; \ + printf("now test %s\n", #__VA_ARGS__); \ + int r=my_snprintf(buf, sizeof(buf), __VA_ARGS__); \ + if(r != retval || strcmp(buf, result) != 0) { \ + printf("error test(%s) was \"%s\":%d\n", \ + ""#bufsz", "#result", "#retval", "#__VA_ARGS__, \ + buf, r); \ + exit(1); \ + } \ + r=snprintf(buf, sizeof(buf), __VA_ARGS__); \ + if(r != retval || strcmp(buf, result) != 0) { \ + printf("error test(%s) differs with system, \"%s\":%d\n", \ + ""#bufsz", "#result", "#retval", "#__VA_ARGS__, \ + buf, r); \ + exit(1); \ + } \ + printf("test(\"%s\":%d) passed\n", buf, r); \ + } while(0); + +/** test program */ +int main(void) +{ + int x = 0; + + /* bufsize, expectedstring, expectedretval, snprintf arguments */ + DOTEST(1024, "hello", 5, "hello"); + DOTEST(1024, "h", 1, "h"); + /* warning from gcc for format string, but it does work + * DOTEST(1024, "", 0, ""); */ + + DOTEST(3, "he", 5, "hello"); + DOTEST(1, "", 7, "%d", 7823089); + + /* test positive numbers */ + DOTEST(1024, "0", 1, "%d", 0); + DOTEST(1024, "1", 1, "%d", 1); + DOTEST(1024, "9", 1, "%d", 9); + DOTEST(1024, "15", 2, "%d", 15); + DOTEST(1024, "ab15cd", 6, "ab%dcd", 15); + DOTEST(1024, "167", 3, "%d", 167); + DOTEST(1024, "7823089", 7, "%d", 7823089); + DOTEST(1024, " 12", 3, "%3d", 12); + DOTEST(1024, "012", 3, "%.3d", 12); + DOTEST(1024, "012", 3, "%3.3d", 12); + DOTEST(1024, "012", 3, "%03d", 12); + DOTEST(1024, " 012", 4, "%4.3d", 12); + DOTEST(1024, "", 0, "%.0d", 0); + + /* test negative numbers */ + DOTEST(1024, "-1", 2, "%d", -1); + DOTEST(1024, "-12", 3, "%3d", -12); + DOTEST(1024, " -2", 3, "%3d", -2); + DOTEST(1024, "-012", 4, "%.3d", -12); + DOTEST(1024, "-012", 4, "%3.3d", -12); + DOTEST(1024, "-012", 4, "%4.3d", -12); + DOTEST(1024, " -012", 5, "%5.3d", -12); + DOTEST(1024, "-12", 3, "%03d", -12); + DOTEST(1024, "-02", 3, "%03d", -2); + DOTEST(1024, "-15", 3, "%d", -15); + DOTEST(1024, "-7307", 5, "%d", -7307); + DOTEST(1024, "-12 ", 5, "%-5d", -12); + DOTEST(1024, "-00012", 6, "%-.5d", -12); + + /* test + and space flags */ + DOTEST(1024, "+12", 3, "%+d", 12); + DOTEST(1024, " 12", 3, "% d", 12); + + /* test %u */ + DOTEST(1024, "12", 2, "%u", 12); + DOTEST(1024, "0", 1, "%u", 0); + DOTEST(1024, "4294967295", 10, "%u", 0xffffffff); + + /* test %x */ + DOTEST(1024, "0", 1, "%x", 0); + DOTEST(1024, "c", 1, "%x", 12); + DOTEST(1024, "12ab34cd", 8, "%x", 0x12ab34cd); + + /* test %llu, %lld */ + DOTEST(1024, "18446744073709551615", 20, "%llu", + (long long)0xffffffffffffffff); + DOTEST(1024, "-9223372036854775808", 20, "%lld", + (long long)0x8000000000000000); + DOTEST(1024, "9223372036854775808", 19, "%llu", + (long long)0x8000000000000000); + + /* test %s */ + DOTEST(1024, "hello", 5, "%s", "hello"); + DOTEST(1024, " hello", 10, "%10s", "hello"); + DOTEST(1024, "hello ", 10, "%-10s", "hello"); + DOTEST(1024, "he", 2, "%.2s", "hello"); + DOTEST(1024, " he", 4, "%4.2s", "hello"); + DOTEST(1024, " h", 4, "%4.2s", "h"); + + /* test %c */ + DOTEST(1024, "a", 1, "%c", 'a'); + /* warning from gcc for format string, but it does work + DOTEST(1024, " a", 5, "%5c", 'a'); + DOTEST(1024, "a", 1, "%.0c", 'a'); */ + + /* test %n */ + DOTEST(1024, "hello", 5, "hello%n", &x); + if(x != 5) { printf("the %%n failed\n"); exit(1); } + + /* test %m */ + errno = 0; + DOTEST(1024, "Success", 7, "%m"); + + /* test %p */ + DOTEST(1024, "0x10", 4, "%p", (void*)0x10); + DOTEST(1024, "(nil)", 5, "%p", (void*)0x0); + + /* test %% */ + DOTEST(1024, "%", 1, "%%"); + + /* test %f */ + DOTEST(1024, "0.000000", 8, "%f", 0.0); + DOTEST(1024, "0.00", 4, "%.2f", 0.0); + /* differs, "-0.00" DOTEST(1024, "0.00", 4, "%.2f", -0.0); */ + DOTEST(1024, "234.00", 6, "%.2f", 234.005); + DOTEST(1024, "8973497.1246", 12, "%.4f", 8973497.12456); + DOTEST(1024, "-12.000000", 10, "%f", -12.0); + DOTEST(1024, "6", 1, "%.0f", 6.0); + + DOTEST(1024, "6", 1, "%g", 6.0); + DOTEST(1024, "6.1", 3, "%g", 6.1); + DOTEST(1024, "6.15", 4, "%g", 6.15); + + /* These format strings are from the code of NSD, Unbound, ldns */ + + DOTEST(1024, "abcdef", 6, "%s", "abcdef"); + DOTEST(1024, "005", 3, "%03u", 5); + DOTEST(1024, "12345", 5, "%03u", 12345); + DOTEST(1024, "5", 1, "%d", 5); + DOTEST(1024, "(nil)", 5, "%p", NULL); + DOTEST(1024, "12345", 5, "%ld", (long)12345); + DOTEST(1024, "12345", 5, "%lu", (long)12345); + DOTEST(1024, " 12345", 12, "%12u", (unsigned)12345); + DOTEST(1024, "12345", 5, "%u", (unsigned)12345); + DOTEST(1024, "12345", 5, "%llu", (unsigned long long)12345); + DOTEST(1024, "12345", 5, "%x", 0x12345); + DOTEST(1024, "12345", 5, "%llx", (long long)0x12345); + DOTEST(1024, "012345", 6, "%6.6d", 12345); + DOTEST(1024, "012345", 6, "%6.6u", 12345); + DOTEST(1024, "1234.54", 7, "%g", 1234.54); + DOTEST(1024, "123456789.54", 12, "%.12g", 123456789.54); + DOTEST(1024, "3456789123456.54", 16, "%.16g", 3456789123456.54); + /* %24g does not work with 24 digits, not enough accuracy, + * the first 16 digits are correct */ + DOTEST(1024, "12345", 5, "%3.3d", 12345); + DOTEST(1024, "000", 3, "%3.3d", 0); + DOTEST(1024, "001", 3, "%3.3d", 1); + DOTEST(1024, "012", 3, "%3.3d", 12); + DOTEST(1024, "-012", 4, "%3.3d", -12); + DOTEST(1024, "he", 2, "%.2s", "hello"); + DOTEST(1024, "helloworld", 10, "%s%s", "hello", "world"); + DOTEST(1024, "he", 2, "%.*s", 2, "hello"); + DOTEST(1024, " hello", 7, "%*s", 7, "hello"); + DOTEST(1024, "hello ", 7, "%*s", -7, "hello"); + DOTEST(1024, "0", 1, "%c", '0'); + DOTEST(1024, "A", 1, "%c", 'A'); + DOTEST(1024, "", 1, "%c", 0); + DOTEST(1024, "\010", 1, "%c", 8); + DOTEST(1024, "%", 1, "%%"); + DOTEST(1024, "0a", 2, "%02x", 0x0a); + DOTEST(1024, "bd", 2, "%02x", 0xbd); + DOTEST(1024, "12", 2, "%02ld", (long)12); + DOTEST(1024, "02", 2, "%02ld", (long)2); + DOTEST(1024, "02", 2, "%02u", (unsigned)2); + DOTEST(1024, "765432", 6, "%05u", (unsigned)765432); + DOTEST(1024, "10.234", 6, "%0.3f", 10.23421); + DOTEST(1024, "123456.234", 10, "%0.3f", 123456.23421); + DOTEST(1024, "123456789.234", 13, "%0.3f", 123456789.23421); + DOTEST(1024, "123456.23", 9, "%.2f", 123456.23421); + DOTEST(1024, "123456", 6, "%.0f", 123456.23421); + DOTEST(1024, "0123", 4, "%.4x", 0x0123); + DOTEST(1024, "00000123", 8, "%.8x", 0x0123); + DOTEST(1024, "ffeb0cde", 8, "%.8x", 0xffeb0cde); + DOTEST(1024, " 987654321", 10, "%10lu", (unsigned long)987654321); + DOTEST(1024, " 987654321", 12, "%12lu", (unsigned long)987654321); + DOTEST(1024, "987654321", 9, "%i", 987654321); + DOTEST(1024, "-87654321", 9, "%i", -87654321); + DOTEST(1024, "hello ", 16, "%-16s", "hello"); + DOTEST(1024, " ", 16, "%-16s", ""); + DOTEST(1024, "a ", 16, "%-16s", "a"); + DOTEST(1024, "foobarfoobar ", 16, "%-16s", "foobarfoobar"); + DOTEST(1024, "foobarfoobarfoobar", 18, "%-16s", "foobarfoobarfoobar"); + + /* combined expressions */ + DOTEST(1024, "foo 1.0 size 512 edns", 21, + "foo %s size %d %s%s", "1.0", 512, "", "edns"); + DOTEST(15, "foo 1.0 size 5", 21, + "foo %s size %d %s%s", "1.0", 512, "", "edns"); + DOTEST(1024, "packet 1203ceff id", 18, + "packet %2.2x%2.2x%2.2x%2.2x id", 0x12, 0x03, 0xce, 0xff); + DOTEST(1024, "/tmp/testbound_123abcd.tmp", 26, "/tmp/testbound_%u%s%s.tmp", 123, "ab", "cd"); + + return 0; +} +#endif /* SNPRINTF_TEST */ diff --git a/usr.sbin/nsd/configlexer.lex b/usr.sbin/nsd/configlexer.lex index d98a4ae0acc..55bf4cfe62a 100644 --- a/usr.sbin/nsd/configlexer.lex +++ b/usr.sbin/nsd/configlexer.lex @@ -99,6 +99,7 @@ ANY [^\"\n\r\\]|\\. server{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SERVER;} name{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NAME;} ip-address{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_ADDRESS;} +ip-transparent{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_TRANSPARENT;} debug-mode{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DEBUG_MODE;} hide-version{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_HIDE_VERSION;} ip4-only{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP4_ONLY;} @@ -140,6 +141,9 @@ AXFR { LEXOUT(("v(%s) ", yytext)); return VAR_AXFR;} UDP { LEXOUT(("v(%s) ", yytext)); return VAR_UDP;} rrl-size{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_SIZE;} rrl-ratelimit{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_RATELIMIT;} +rrl-slip{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_SLIP;} +rrl-ipv4-prefix-length{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV4_PREFIX_LENGTH;} +rrl-ipv6-prefix-length{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV6_PREFIX_LENGTH;} rrl-whitelist-ratelimit{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST_RATELIMIT;} rrl-whitelist{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST;} {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} diff --git a/usr.sbin/nsd/difffile.c b/usr.sbin/nsd/difffile.c index 3d87fce5201..2b6d721d878 100644 --- a/usr.sbin/nsd/difffile.c +++ b/usr.sbin/nsd/difffile.c @@ -536,6 +536,14 @@ add_RR(namedb_type* db, const dname_type* dname, /* ignore already existing RR: lenient accepting of messages */ return 1; } + if(domain == zone->apex) { + /* make sure we don't get multiple soa rrs */ + if (type == TYPE_SOA && rrset->rr_count > 0) { + log_msg(LOG_ERR, "diff: multiple soa records for %s", + dname_to_string(dname,0)); + return 0; + } + } /* re-alloc the rrs and add the new */ rrs_old = rrset->rrs; diff --git a/usr.sbin/nsd/ipc.c b/usr.sbin/nsd/ipc.c index a380815b50d..28e1cc5e7ec 100644 --- a/usr.sbin/nsd/ipc.c +++ b/usr.sbin/nsd/ipc.c @@ -134,6 +134,14 @@ child_handle_parent_command(netio_type *ATTR_UNUSED(netio), case NSD_QUIT: ipc_child_quit(data->nsd); break; + case NSD_QUIT_CHILD: + /* close our listening sockets and ack */ + server_close_all_sockets(data->nsd->udp, data->nsd->ifs); + server_close_all_sockets(data->nsd->tcp, data->nsd->ifs); + /* mode == NSD_QUIT_CHILD */ + (void)write(handler->fd, &mode, sizeof(mode)); + ipc_child_quit(data->nsd); + break; case NSD_ZONE_STATE: data->conn->is_reading = 1; data->conn->total_bytes = 0; @@ -200,7 +208,8 @@ parent_handle_xfrd_command(netio_type *ATTR_UNUSED(netio), } if (len == 0) { - DEBUG(DEBUG_IPC,1, (LOG_ERR, "handle_xfrd_command: xfrd closed channel.")); + /* xfrd closed, we must quit */ + DEBUG(DEBUG_IPC,1, (LOG_INFO, "handle_xfrd_command: xfrd closed channel.")); close(handler->fd); handler->fd = -1; return; @@ -208,6 +217,7 @@ parent_handle_xfrd_command(netio_type *ATTR_UNUSED(netio), switch (mode) { case NSD_RELOAD: + DEBUG(DEBUG_IPC,1, (LOG_INFO, "parent handle xfrd command RELOAD")); data->nsd->signal_hint_reload = 1; break; case NSD_QUIT: diff --git a/usr.sbin/nsd/namedb.c b/usr.sbin/nsd/namedb.c index 43a1b8e9bec..5ed3b31baf6 100644 --- a/usr.sbin/nsd/namedb.c +++ b/usr.sbin/nsd/namedb.c @@ -20,7 +20,6 @@ #include "namedb.h" - static domain_type * allocate_domain_info(domain_table_type *table, const dname_type *dname, diff --git a/usr.sbin/nsd/netio.c b/usr.sbin/nsd/netio.c index 135094a82eb..2c64b6d1f67 100644 --- a/usr.sbin/nsd/netio.c +++ b/usr.sbin/nsd/netio.c @@ -25,13 +25,6 @@ int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, #include <sys/select.h> #endif - -struct netio_handler_list -{ - netio_handler_list_type *next; - netio_handler_type *handler; -}; - netio_type * netio_create(region_type *region) { diff --git a/usr.sbin/nsd/netio.h b/usr.sbin/nsd/netio.h index 13035a0d3b2..c6686afc26f 100644 --- a/usr.sbin/nsd/netio.h +++ b/usr.sbin/nsd/netio.h @@ -137,6 +137,13 @@ struct netio_handler }; +struct netio_handler_list +{ + netio_handler_list_type *next; + netio_handler_type *handler; +}; + + /* * Create a new netio instance using the specified REGION. The netio * instance is cleaned up when the REGION is deallocated. diff --git a/usr.sbin/nsd/nsd.conf.sample.in b/usr.sbin/nsd/nsd.conf.sample.in index cf3e0b0c7bd..fe1a4874c5c 100644 --- a/usr.sbin/nsd/nsd.conf.sample.in +++ b/usr.sbin/nsd/nsd.conf.sample.in @@ -16,6 +16,9 @@ server: # ip-address: 1.2.3.4@5678 # ip-address: 12fe::8ef0 + # Allow binding to non local addresses. Default no. + # ip-transparent: no + # don't answer VERSION.BIND and VERSION.SERVER CHAOS class queries # hide-version: no @@ -108,6 +111,22 @@ server: # rrl-whitelist-ratelimit to 0 to disable ratelimit processing. # rrl-ratelimit: 200 + # Response Rate Limiting, number of packets to discard before + # sending a SLIP response (a truncated one, allowing an honest + # resolver to retry with TCP). Default is 2 (one half of the + # queries will receive a SLIP response, 0 disables SLIP (all + # packets are discarded), 1 means every request will get a + # SLIP response. + # rrl-slip: 2 + + # Response Rate Limiting, IPv4 prefix length. Addresses are + # grouped by netblock. + # rrl-ipv4-prefix-length: 24 + + # Response Rate Limiting, IPv6 prefix length. Addresses are + # grouped by netblock. + # rrl-ipv6-prefix-length: 64 + # Response Rate Limiting, maximum QPS allowed (from one query source) # for whitelisted types. Default 2000. # rrl-whitelist-ratelimit: 2000 diff --git a/usr.sbin/nsd/nsd.h b/usr.sbin/nsd/nsd.h index 1dce4d95477..2dd4676937e 100644 --- a/usr.sbin/nsd/nsd.h +++ b/usr.sbin/nsd/nsd.h @@ -57,6 +57,11 @@ struct nsd_options; * channel content during reload */ #define NSD_QUIT_SYNC 11 +/* + * QUIT_CHILD is sent at exit, to make sure the child has exited so that + * port53 is free when all of nsd's processes have exited at shutdown time + */ +#define NSD_QUIT_CHILD 12 #define NSD_SERVER_MAIN 0x0U #define NSD_SERVER_UDP 0x1U @@ -224,6 +229,7 @@ int server_prepare(struct nsd *nsd); void server_main(struct nsd *nsd); void server_child(struct nsd *nsd); void server_shutdown(struct nsd *nsd); +void server_close_all_sockets(struct nsd_socket sockets[], size_t n); /* extra domain numbers for temporary domains */ #define EXTRA_DOMAIN_NUMBERS 1024 diff --git a/usr.sbin/nsd/options.c b/usr.sbin/nsd/options.c index 721c763383e..39cfa610864 100644 --- a/usr.sbin/nsd/options.c +++ b/usr.sbin/nsd/options.c @@ -34,6 +34,7 @@ nsd_options_t* nsd_options_create(region_type* region) opt->keys = NULL; opt->numkeys = 0; opt->ip_addresses = NULL; + opt->ip_transparent = 0; opt->debug_mode = 0; opt->verbosity = 0; opt->hide_version = 0; @@ -67,6 +68,9 @@ nsd_options_t* nsd_options_create(region_type* region) #ifdef RATELIMIT opt->rrl_size = RRL_BUCKETS; opt->rrl_ratelimit = RRL_LIMIT/2; + opt->rrl_slip = RRL_SLIP; + opt->rrl_ipv4_prefix_length = RRL_IPV4_PREFIX_LENGTH; + opt->rrl_ipv6_prefix_length = RRL_IPV6_PREFIX_LENGTH; opt->rrl_whitelist_ratelimit = RRL_WLIST_LIMIT/2; #endif nsd_options = opt; @@ -647,6 +651,7 @@ acl_options_t* parse_acl_info(region_type* region, char* ip, const char* key) acl->use_axfr_only = 0; acl->allow_udp = 0; acl->ixfr_disabled = 0; + acl->bad_xfr_count = 0; acl->key_options = 0; acl->is_ipv6 = 0; acl->port = 0; diff --git a/usr.sbin/nsd/options.h b/usr.sbin/nsd/options.h index 5845b6eaa9f..cab7d5749cf 100644 --- a/usr.sbin/nsd/options.h +++ b/usr.sbin/nsd/options.h @@ -38,6 +38,7 @@ struct nsd_options { /* list of ip adresses to bind to (or NULL for all) */ ip_address_option_t* ip_addresses; + int ip_transparent; int debug_mode; int verbosity; int hide_version; @@ -69,6 +70,11 @@ struct nsd_options { size_t rrl_size; /** max qps for queries, 0 is nolimit */ size_t rrl_ratelimit; + /** ratio of slipped responses, 0 is noslip */ + size_t rrl_slip; + /** ip prefix length */ + size_t rrl_ipv4_prefix_length; + size_t rrl_ipv6_prefix_length; /** max qps for whitelisted queries, 0 is nolimit */ size_t rrl_whitelist_ratelimit; #endif @@ -122,6 +128,7 @@ struct acl_options { uint8_t use_axfr_only; uint8_t allow_udp; time_t ixfr_disabled; + int bad_xfr_count; /* ip address range */ const char* ip_address_spec; diff --git a/usr.sbin/nsd/rrl.c b/usr.sbin/nsd/rrl.c index 01da9d75ff1..65f3788ea59 100644 --- a/usr.sbin/nsd/rrl.c +++ b/usr.sbin/nsd/rrl.c @@ -7,6 +7,7 @@ #include "config.h" #include <errno.h> #include <ctype.h> +#include "dns.h" #include "rrl.h" #include "util.h" #include "lookup3.h" @@ -33,6 +34,8 @@ struct rrl_bucket { /* rate, in queries per second, which due to rate=r(t)+r(t-1)/2 is * equal to double the queries per second */ uint32_t rate; + /* the full hash */ + uint32_t hash; /* counter for queries arrived in this second */ uint32_t counter; /* timestamp, which time is the time of the counter, the rate is from @@ -46,6 +49,10 @@ struct rrl_bucket { static struct rrl_bucket* rrl_array = NULL; static size_t rrl_array_size = RRL_BUCKETS; static uint32_t rrl_ratelimit = RRL_LIMIT; /* 2x qps */ +static uint8_t rrl_slip_ratio = RRL_SLIP; +static uint8_t rrl_ipv4_prefixlen = RRL_IPV4_PREFIX_LENGTH; +static uint8_t rrl_ipv6_prefixlen = RRL_IPV6_PREFIX_LENGTH; +static uint64_t rrl_ipv6_mask; /* max prefixlen 64 */ static uint32_t rrl_whitelist_ratelimit = RRL_WLIST_LIMIT; /* 2x qps */ /* the array of mmaps for the children (saved between reloads) */ @@ -83,7 +90,8 @@ static char* wiredname2str(const uint8_t* dname) return buf; } -void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm) +void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm, size_t sm, + size_t plf, size_t pls) { #ifdef HAVE_MMAP size_t i; @@ -91,6 +99,15 @@ void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm) if(numbuck != 0) rrl_array_size = numbuck; rrl_ratelimit = lm*2; + rrl_slip_ratio = sm; + rrl_ipv4_prefixlen = plf; + rrl_ipv6_prefixlen = pls; + if (pls <= 32) { + rrl_ipv6_mask = ((uint64_t) htonl(0xffffffff << (32-pls))) << 32; + } else { + rrl_ipv6_mask = ((uint64_t) htonl(0xffffffff << (64-pls))) | + (((uint64_t)0xffffffff)<<32); + } rrl_whitelist_ratelimit = wlm*2; #ifdef HAVE_MMAP /* allocate the ratelimit hashtable in a memory map so it is @@ -99,7 +116,7 @@ void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm) rrl_maps = (void**)xalloc(sizeof(void*)*rrl_maps_num); for(i=0; i<rrl_maps_num; i++) { rrl_maps[i] = mmap(NULL, - sizeof(struct rrl_bucket)*rrl_array_size, + sizeof(struct rrl_bucket)*rrl_array_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); if(rrl_maps[i] == MAP_FAILED) { log_msg(LOG_ERR, "rrl: mmap failed: %s", @@ -116,12 +133,6 @@ void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm) #endif } -void rrl_set_limit(size_t lm, size_t wlm) -{ - rrl_ratelimit = lm*2; - rrl_whitelist_ratelimit = wlm*2; -} - void rrl_init(size_t ch) { if(!rrl_maps || ch >= rrl_maps_num) @@ -135,7 +146,6 @@ void rrl_init(size_t ch) * for genuine queries and the target for reflected packets */ static uint64_t rrl_get_source(query_type* query, uint16_t* c2) { - /* we take a /24 for IPv4 and /64 for IPv6 */ /* note there is an IPv6 subnet, that maps * to the same buckets as IPv4 space, but there is a flag in c2 * that makes the hash different */ @@ -143,17 +153,17 @@ static uint64_t rrl_get_source(query_type* query, uint16_t* c2) if( ((struct sockaddr_in*)&query->addr)->sin_family == AF_INET) { *c2 = 0; return ((struct sockaddr_in*)&query->addr)-> - sin_addr.s_addr & htonl(0xffffff00); + sin_addr.s_addr & htonl(0xffffffff << (32-rrl_ipv4_prefixlen)); } else { uint64_t s; *c2 = rrl_ip6; memmove(&s, &((struct sockaddr_in6*)&query->addr)->sin6_addr, sizeof(s)); - return s; + return s & rrl_ipv6_mask; } #else *c2 = 0; - return query->addr.sin_addr.s_addr & htonl(0xffffff00); + return query->addr.sin_addr.s_addr & htonl(0xffffffff << (32-rrl_ipv4_prefixlen)); #endif } @@ -170,7 +180,11 @@ static const char* rrlsource2str(uint64_t s, uint16_t c2) memmove(&a6, &s, sizeof(s)); if(!inet_ntop(AF_INET6, &a6, buf, sizeof(buf))) strlcpy(buf, "[ip6 ntop failed]", sizeof(buf)); - else strlcat(buf, "/64", sizeof(buf)); + else { + static char prefix[4]; + snprintf(prefix, sizeof(prefix), "/%d", rrl_ipv6_prefixlen); + strlcat(buf, &prefix[0], sizeof(buf)); + } return buf; } #endif @@ -178,7 +192,11 @@ static const char* rrlsource2str(uint64_t s, uint16_t c2) a4.s_addr = (uint32_t)s; if(!inet_ntop(AF_INET, &a4, buf, sizeof(buf))) strlcpy(buf, "[ip4 ntop failed]", sizeof(buf)); - else strlcat(buf, "/24", sizeof(buf)); + else { + static char prefix[4]; + snprintf(prefix, sizeof(prefix), "/%d", rrl_ipv4_prefixlen); + strlcat(buf, &prefix[0], sizeof(buf)); + } return buf; } @@ -216,7 +234,7 @@ const char* rrltype2str(enum rrl_type c) /** classify the query in a number of different types, each has separate * ratelimiting, so that positive queries are not impeded by others */ -static uint16_t rrl_classify(query_type* query, const uint8_t** d, +static uint16_t rrl_classify(query_type* query, const uint8_t** d, size_t* d_len) { if(RCODE(query->packet) == RCODE_NXDOMAIN) { @@ -292,7 +310,7 @@ static void examine_query(query_type* query, uint32_t* hash, uint64_t* source, *source = rrl_get_source(query, &c2); c = rrl_classify(query, &dname, &dname_len); - if(query->zone && query->zone->opts && + if(query->zone && query->zone->opts && (query->zone->opts->rrl_whitelist & c)) *lm = rrl_whitelist_ratelimit; if(*lm == 0) return; @@ -334,15 +352,21 @@ rrl_msg(query_type* query, const char* str) const uint8_t* d = NULL; size_t d_len; uint64_t s; + char address[128]; if(verbosity < 2) return; + if (addr2ip(query->addr, address, sizeof(address))) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "addr2ip failed")); + strlcpy(address, "[unknown]", sizeof(address)); + } s = rrl_get_source(query, &c2); c = rrl_classify(query, &d, &d_len) | c2; - if(query->zone && query->zone->opts && + if(query->zone && query->zone->opts && (query->zone->opts->rrl_whitelist & c)) wl = 1; - log_msg(LOG_INFO, "ratelimit %s %s type %s%s target %s", + log_msg(LOG_INFO, "ratelimit %s %s type %s%s target %s query %s %s", str, d?wiredname2str(d):"", rrltype2str(c), - wl?"(whitelisted)":"", rrlsource2str(s, c2)); + wl?"(whitelisted)":"", rrlsource2str(s, c2), + address, rrtype_to_string(query->qtype)); } /** true if the query used to be blocked by the ratelimit */ @@ -362,14 +386,23 @@ uint32_t rrl_update(query_type* query, uint32_t hash, uint64_t source, (long long unsigned)source, hash, b->rate, b->counter, b->stamp)); /* check if different source */ - if(b->source != source || b->flags != flags) { + if(b->source != source || b->flags != flags || b->hash != hash) { /* initialise */ /* potentially the wrong limit here, used lower nonwhitelim */ if(verbosity >=2 && - used_to_block(b->rate, b->counter, rrl_ratelimit)) - log_msg(LOG_INFO, "ratelimit unblock ~ type %s target %s", + used_to_block(b->rate, b->counter, rrl_ratelimit)) { + char address[128]; + if (addr2ip(query->addr, address, sizeof(address))) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "addr2ip failed")); + strlcpy(address, "[unknown]", sizeof(address)); + } + log_msg(LOG_INFO, "ratelimit unblock ~ type %s target %s query %s %s (%s collision)", rrltype2str(b->flags), - rrlsource2str(b->source, b->flags)); + rrlsource2str(b->source, b->flags), + address, rrtype_to_string(query->qtype), + (b->hash!=hash?"bucket":"hash")); + } + b->hash = hash; b->source = source; b->flags = flags; b->counter = 1; @@ -443,8 +476,8 @@ int rrl_process_query(query_type* query) query_state_type rrl_slip(query_type* query) { - /* discard half the packets, randomly */ - if((random() & 0x1)) { + /* discard number of packets, randomly */ + if((rrl_slip_ratio > 0) && ((rrl_slip_ratio == 1) || ((random() % rrl_slip_ratio) == 0))) { /* set TC on the rest */ TC_SET(query->packet); ANCOUNT_SET(query->packet, 0); diff --git a/usr.sbin/nsd/rrl.h b/usr.sbin/nsd/rrl.h index fae8fbf2343..48dbb53b8cb 100644 --- a/usr.sbin/nsd/rrl.h +++ b/usr.sbin/nsd/rrl.h @@ -30,14 +30,21 @@ enum rrl_type { #define RRL_BUCKETS 1000000 /** default rrl limit, in 2x qps , the default is 200 qps */ #define RRL_LIMIT 400 +/** default slip */ +#define RRL_SLIP 2 +/** default prefix lengths */ +#define RRL_IPV4_PREFIX_LENGTH 24 +#define RRL_IPV6_PREFIX_LENGTH 64 /** default whitelist rrl limit, in 2x qps, default is thus 2000 qps */ #define RRL_WLIST_LIMIT 4000 /** * Initialize for n children (optional, otherwise no mmaps used) * ratelimits lm and wlm are in qps (this routines x2s them for internal use). + * plf and pls are in prefix lengths. */ -void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm); +void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm, size_t sm, + size_t plf, size_t pls); /** * Initialize rate limiting (for this child server process) @@ -65,7 +72,5 @@ enum rrl_type rrlstr2type(const char* s); /** for unit test, update rrl bucket; return rate */ uint32_t rrl_update(query_type* query, uint32_t hash, uint64_t source, uint16_t flags, int32_t now, uint32_t lm); -/** set the rate limit counters, pass variables in qps */ -void rrl_set_limit(size_t lm, size_t wlm); #endif /* RRL_H */ diff --git a/usr.sbin/nsd/xfrd-disk.c b/usr.sbin/nsd/xfrd-disk.c index bb869ed9d40..6c052b08f76 100644 --- a/usr.sbin/nsd/xfrd-disk.c +++ b/usr.sbin/nsd/xfrd-disk.c @@ -297,7 +297,7 @@ xfrd_read_state(struct xfrd_state* xfrd) return; } - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: read %d zones from state file", numzones)); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: read %d zones from state file", (int)numzones)); fclose(in); region_destroy(tempregion); } @@ -308,23 +308,23 @@ neato_timeout(FILE* out, const char* str, uint32_t secs) { fprintf(out, "%s", str); if(secs <= 0) { - fprintf(out, " %ds", secs); + fprintf(out, " %ds", (int)secs); return; } if(secs >= 3600*24) { - fprintf(out, " %dd", secs/(3600*24)); + fprintf(out, " %dd", (int)secs/(3600*24)); secs = secs % (3600*24); } if(secs >= 3600) { - fprintf(out, " %dh", secs/3600); + fprintf(out, " %dh", (int)secs/3600); secs = secs%3600; } if(secs >= 60) { - fprintf(out, " %dm", secs/60); + fprintf(out, " %dm", (int)secs/60); secs = secs%60; } if(secs > 0) { - fprintf(out, " %ds", secs); + fprintf(out, " %ds", (int)secs); } } @@ -371,17 +371,17 @@ xfrd_write_state_soa(FILE* out, const char* id, fprintf(out, " ago\n"); fprintf(out, "\t%s: %u %u %u %u", id, - ntohs(soa->type), ntohs(soa->klass), - ntohl(soa->ttl), ntohs(soa->rdata_count)); + (unsigned)ntohs(soa->type), (unsigned)ntohs(soa->klass), + (unsigned)ntohl(soa->ttl), (unsigned)ntohs(soa->rdata_count)); fprintf(out, " "); xfrd_write_dname(out, soa->prim_ns); fprintf(out, " "); xfrd_write_dname(out, soa->email); - fprintf(out, " %u", ntohl(soa->serial)); - fprintf(out, " %u", ntohl(soa->refresh)); - fprintf(out, " %u", ntohl(soa->retry)); - fprintf(out, " %u", ntohl(soa->expire)); - fprintf(out, " %u\n", ntohl(soa->minimum)); + fprintf(out, " %u", (unsigned)ntohl(soa->serial)); + fprintf(out, " %u", (unsigned)ntohl(soa->refresh)); + fprintf(out, " %u", (unsigned)ntohl(soa->retry)); + fprintf(out, " %u", (unsigned)ntohl(soa->expire)); + fprintf(out, " %u\n", (unsigned)ntohl(soa->minimum)); fprintf(out, "\t#"); neato_timeout(out, " refresh =", ntohl(soa->refresh)); neato_timeout(out, " retry =", ntohl(soa->retry)); diff --git a/usr.sbin/nsd/xfrd.c b/usr.sbin/nsd/xfrd.c index ea24abad853..e217cd4ac19 100644 --- a/usr.sbin/nsd/xfrd.c +++ b/usr.sbin/nsd/xfrd.c @@ -664,15 +664,18 @@ xfrd_handle_incoming_soa(xfrd_zone_t* zone, xfrd_set_refresh_now(zone); return; } - if(zone->soa_nsd_acquired && soa->serial == zone->soa_nsd.serial) + if(zone->soa_nsd_acquired && soa->serial == zone->soa_nsd.serial) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s has already been updated " + "to serial %u (at time %u)", zone->apex_str, + ntohl(zone->soa_nsd.serial), (unsigned) zone->soa_nsd_acquired)); return; - + } if(zone->soa_disk_acquired && soa->serial == zone->soa_disk.serial) { /* soa in disk has been loaded in memory */ log_msg(LOG_INFO, "Zone %s serial %u is updated to %u.", - zone->apex_str, ntohl(zone->soa_nsd.serial), - ntohl(soa->serial)); + zone->apex_str, (unsigned)ntohl(zone->soa_nsd.serial), + (unsigned)ntohl(soa->serial)); zone->soa_nsd = zone->soa_disk; zone->soa_nsd_acquired = zone->soa_disk_acquired; if((uint32_t)xfrd_time() - zone->soa_disk_acquired @@ -716,7 +719,7 @@ xfrd_handle_incoming_soa(xfrd_zone_t* zone, /* user must have manually provided zone data */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s serial %u from unknown source. refreshing", - zone->apex_str, ntohl(soa->serial))); + zone->apex_str, (unsigned)ntohl(soa->serial))); zone->soa_nsd = *soa; zone->soa_disk = *soa; zone->soa_nsd_acquired = acquired; @@ -823,8 +826,19 @@ xfrd_udp_read(xfrd_zone_t* zone) xfrd_make_request(zone); break; case xfrd_packet_more: + case xfrd_packet_drop: + /* drop packet */ + xfrd_udp_release(zone); + /* query next server */ + xfrd_make_request(zone); + break; case xfrd_packet_bad: default: + zone->master->bad_xfr_count++; + if (zone->master->bad_xfr_count > 2) { + zone->master->ixfr_disabled = time(NULL); + zone->master->bad_xfr_count = 0; + } /* drop packet */ xfrd_udp_release(zone); /* query next server */ @@ -900,6 +914,7 @@ xfrd_bind_local_interface(int sockd, acl_options_t* ifc, acl_options_t* acl, #else struct sockaddr_in frm; #endif /* INET6 */ + int ret = 1; if (!ifc) /* no outgoing interface set */ return 1; @@ -913,7 +928,7 @@ xfrd_bind_local_interface(int sockd, acl_options_t* ifc, acl_options_t* acl, DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: bind() %s to %s socket", ifc->ip_address_spec, tcp? "tcp":"udp")); - + ret = 0; frm_len = xfrd_acl_sockaddr_frm(ifc, &frm); if (tcp) { @@ -954,13 +969,13 @@ xfrd_bind_local_interface(int sockd, acl_options_t* ifc, acl_options_t* acl, "failed: %s", ifc->ip_address_spec, tcp? "tcp":"udp", strerror(errno))); + + log_msg(LOG_WARNING, "xfrd: could not bind source address:port to " + "socket: %s", strerror(errno)); /* try another */ ifc = ifc->next; } - - log_msg(LOG_WARNING, "xfrd: could not bind source address:port to " - "socket: %s", strerror(errno)); - return 0; + return ret; } void @@ -1022,7 +1037,7 @@ xfrd_send_ixfr_request_udp(xfrd_zone_t* zone) DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd sent udp request for ixfr=%u for zone %s to %s", - ntohl(zone->soa_disk.serial), + (unsigned)ntohl(zone->soa_disk.serial), zone->apex_str, zone->master->ip_address_spec)); return fd; } @@ -1068,36 +1083,66 @@ xfrd_xfr_check_rrs(xfrd_zone_t* zone, buffer_type* packet, size_t count, int *done, xfrd_soa_t* soa) { /* first RR has already been checked */ + uint32_t tmp_serial = 0; uint16_t type, rrlen; size_t i, soapos; + for(i=0; i<count; ++i,++zone->msg_rr_count) { - if(!packet_skip_dname(packet)) + if (*done) { + /** + * We are done, but there are more RRs coming. Ignore + * trailing garbage. + */ + DEBUG(DEBUG_XFRD,1, (LOG_WARNING, "xfrd: zone %s xfr is " + "done, ignore trailing garbage", zone->apex_str)); + return 1; + } + if(!packet_skip_dname(packet)) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr unable " + "to skip owner name", zone->apex_str)); return 0; - if(!buffer_available(packet, 10)) + } + if(!buffer_available(packet, 10)) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr hdr " + "too small", zone->apex_str)); return 0; + } soapos = buffer_position(packet); type = buffer_read_u16(packet); (void)buffer_read_u16(packet); /* class */ (void)buffer_read_u32(packet); /* ttl */ rrlen = buffer_read_u16(packet); - if(!buffer_available(packet, rrlen)) + if(!buffer_available(packet, rrlen)) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr pkt " + "too small", zone->apex_str)); return 0; + } if(type == TYPE_SOA) { /* check the SOAs */ size_t mempos = buffer_position(packet); buffer_set_position(packet, soapos); - if(!xfrd_parse_soa_info(packet, soa)) + if(!xfrd_parse_soa_info(packet, soa)) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr " + "unable to parse soainfo", zone->apex_str)); return 0; + } if(zone->msg_rr_count == 1 && ntohl(soa->serial) != zone->msg_new_serial) { /* 2nd RR is SOA with lower serial, this is an IXFR */ zone->msg_is_ixfr = 1; - if(!zone->soa_disk_acquired) + if(!zone->soa_disk_acquired) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr " + "got ixfr but need axfr", zone->apex_str)); return 0; /* got IXFR but need AXFR */ - if(ntohl(soa->serial) != ntohl(zone->soa_disk.serial)) + } + if(ntohl(soa->serial) != ntohl(zone->soa_disk.serial)) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr " + "bad start serial", zone->apex_str)); return 0; /* bad start serial in IXFR */ + } zone->msg_old_serial = ntohl(soa->serial); + tmp_serial = ntohl(soa->serial); } else if(ntohl(soa->serial) == zone->msg_new_serial) { /* saw another SOA of new serial. */ @@ -1108,6 +1153,22 @@ xfrd_xfr_check_rrs(xfrd_zone_t* zone, buffer_type* packet, size_t count, *done = 1; } } + else if (zone->msg_is_ixfr) { + /* some additional checks */ + if(ntohl(soa->serial) > zone->msg_new_serial) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr " + "bad middle serial", zone->apex_str)); + return 0; /* bad middle serial in IXFR */ + } + if(ntohl(soa->serial) < tmp_serial) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr " + "serial decreasing not allowed", zone->apex_str)); + return 0; /* middle serial decreases in IXFR */ + } + /** serial ok, update tmp serial */ + tmp_serial = ntohl(soa->serial); + + } buffer_set_position(packet, mempos); } buffer_skip(packet, rrlen); @@ -1299,10 +1360,10 @@ xfrd_parse_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet, return xfrd_packet_newlease; } /* try next master */ - return xfrd_packet_bad; + return xfrd_packet_drop; } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "IXFR reply has ok serial (have \ -%u, reply %u).", ntohl(zone->soa_disk.serial), ntohl(soa->serial))); +%u, reply %u).", (unsigned)ntohl(zone->soa_disk.serial), (unsigned)ntohl(soa->serial))); /* serial is newer than soa_disk */ if(ancount == 1) { /* single record means it is like a notify */ @@ -1379,6 +1440,7 @@ xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet) return xfrd_packet_tcp; case xfrd_packet_notimpl: case xfrd_packet_bad: + case xfrd_packet_drop: default: { /* rollback */ @@ -1587,8 +1649,8 @@ xfrd_handle_incoming_notify(xfrd_zone_t* zone, xfrd_soa_t* soa) DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: ignored notify %s %u old serial, zone valid " "(soa disk serial %u)", zone->apex_str, - ntohl(soa->serial), - ntohl(zone->soa_disk.serial))); + (unsigned)ntohl(soa->serial), + (unsigned)ntohl(zone->soa_disk.serial))); return 0; /* ignore notify with old serial, we have a valid zone */ } if(soa == 0) { @@ -1649,9 +1711,15 @@ xfrd_check_failed_updates() soa time is before the time of the reload cmd. */ xfrd_soa_t dumped_soa = zone->soa_disk; log_msg(LOG_ERR, "xfrd: zone %s: soa serial %u " - "update failed, restarting " - "transfer (notified zone)", - zone->apex_str, ntohl(zone->soa_disk.serial)); + "update failed (acquired: %u), restarting " + "transfer (notified zone)", + zone->apex_str, ntohl(zone->soa_disk.serial), + (unsigned) zone->soa_disk_acquired); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: nsd has " + "soa serial %u (acquired: %u, reload cmd sent: " + "%u)", zone->apex_str, ntohl(zone->soa_nsd.serial), + (unsigned) zone->soa_nsd_acquired, + (unsigned) xfrd->reload_cmd_last_sent)); /* revert the soa; it has not been acquired properly */ zone->soa_disk_acquired = zone->soa_nsd_acquired; zone->soa_disk = zone->soa_nsd; @@ -1665,8 +1733,8 @@ xfrd_check_failed_updates() if(xfrd->need_to_send_reload == 0 && xfrd->reload_handler.timeout == NULL) { log_msg(LOG_ERR, "xfrd: zone %s: needs " - "to be loaded. reload lost? " - "try again", zone->apex_str); + "to be loaded. reload lost? " + "try again", zone->apex_str); xfrd_set_reload_timeout(); } } diff --git a/usr.sbin/nsd/xfrd.h b/usr.sbin/nsd/xfrd.h index 49337035277..e4d6a278259 100644 --- a/usr.sbin/nsd/xfrd.h +++ b/usr.sbin/nsd/xfrd.h @@ -178,6 +178,7 @@ struct xfrd_zone { enum xfrd_packet_result { xfrd_packet_bad, /* drop the packet/connection */ + xfrd_packet_drop, /* drop the connection, but not report bad */ xfrd_packet_more, /* more packets to follow on tcp */ xfrd_packet_notimpl, /* server responded with NOTIMPL or FORMATERR */ xfrd_packet_tcp, /* try tcp connection */ |