diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..544d21f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true + +[{*.c,*.h}] +indent_style = space +indent_size = 4 +tab_width = 8 diff --git a/.gitignore b/.gitignore index 7a98dc8..bb95251 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ -/MCONFIG -/aconfig.h -/aconfig.h.in +/config/MCONFIG +/config/config.h +/config/config.h.in +/autoconf/aclocal.m4 +/autoconf/clean.sh +/autoconf/helpers/ /autom4te.cache /config.log /config.status @@ -12,5 +15,7 @@ *.8 *.a *.o +*.i +*.s *~ \#* diff --git a/CHANGES b/CHANGES index 6df0d97..6b7b53c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +Changes in 5.2: + Fix breakage on newer Linux when a single interface has + multiple IP addresses. + + Changes in 5.1: Add -P option to write a PID file. Patch by Ferenc Wagner. diff --git a/Makefile b/Makefile index 9ff12d8..2e790fd 100644 --- a/Makefile +++ b/Makefile @@ -3,10 +3,10 @@ SUB = lib common tftp tftpd -%.build: MCONFIG aconfig.h version.h +%.build: config/MCONFIG config/config.h version.h $(MAKE) -C $(patsubst %.build, %, $@) -%.install: MCONFIG aconfig.h version.h +%.install: config/MCONFIG config/config.h version.h $(MAKE) -C $(patsubst %.install, %, $@) install %.clean: @@ -15,12 +15,12 @@ SUB = lib common tftp tftpd %.distclean: $(MAKE) -C $(patsubst %.distclean, %, $@) distclean -all: MCONFIG $(patsubst %, %.build, $(SUB)) +all: config/MCONFIG $(patsubst %, %.build, $(SUB)) tftp.build: lib.build common.build tftpd.build: lib.build common.build -install: MCONFIG $(patsubst %, %.install, $(SUB)) +install: config/MCONFIG $(patsubst %, %.install, $(SUB)) clean: localclean $(patsubst %, %.clean, $(SUB)) @@ -30,46 +30,40 @@ localclean: distclean: localdistclean $(patsubst %, %.distclean, $(SUB)) localdistclean: localclean - rm -f MCONFIG config.status config.log aconfig.h *~ \#* + rm -f config/config/MCONFIG config.status config.log config/config.h *~ \#* rm -rf *.cache find . -type f \( -name \*.orig -o -name \*.rej \) | xargs rm -f spotless: distclean - rm -f configure aconfig.h.in tftp.spec + rm -f configure config/config.h.in tftp.spec -autoconf: configure aconfig.h.in +autoconf: configure config/config.h.in -config: MCONFIG aconfig.h +config: config/MCONFIG config/config.h release: $(MAKE) autoconf $(MAKE) tftp.spec $(MAKE) distclean -MCONFIG: configure MCONFIG.in aconfig.h.in +config/MCONFIG: configure config/MCONFIG.in config/config.h.in if test -x config.status; then \ ./config.status --recheck && ./config.status ; \ else \ ./configure ; \ fi -aconfig.h: MCONFIG +config/config.h: config/MCONFIG : Generated by side effect -# Adding "configure" to the dependencies serializes this with running -# autoconf, because there are apparently race conditions between -# autoconf and autoheader. -aconfig.h.in: configure.in configure aclocal.m4 - rm -f aconfig.h.in aconfig.h - autoheader +configure: configure.ac + sh autogen.sh -configure: configure.in aclocal.m4 - rm -rf MCONFIG configure config.log aconfig.h *.cache - autoconf +config/config.h.in: configure + : Generated by side effect version.h: version echo \#define VERSION \"tftp-hpa `cat version`\" > version.h tftp.spec: tftp.spec.in version sed -e "s/@@VERSION@@/`cat version`/g" < $< > $@ || rm -f $@ - diff --git a/aclocal.m4 b/aclocal.m4 deleted file mode 100644 index c07702c..0000000 --- a/aclocal.m4 +++ /dev/null @@ -1,280 +0,0 @@ -dnl ----------------------------------------------------------------------- -dnl -dnl Copyright 1999-2008 H. Peter Anvin - All Rights Reserved -dnl -dnl This program is free software; you can redistribute it and/or modify -dnl it under the terms of the GNU General Public License as published by -dnl the Free Software Foundation, Inc., 53 Temple Place Ste 330, -dnl Bostom MA 02111-1307, USA; either version 2 of the License, or -dnl (at your option) any later version; incorporated herein by reference. -dnl -dnl ----------------------------------------------------------------------- - -dnl -------------------------------------------------------------------------- -dnl PA_ADD_CFLAGS() -dnl -dnl Attempt to add the given option to CFLAGS, if it doesn't break compilation -dnl -------------------------------------------------------------------------- -AC_DEFUN(PA_ADD_CFLAGS, -[AC_MSG_CHECKING([if $CC accepts $1]) - pa_add_cflags__old_cflags="$CFLAGS" - CFLAGS="$CFLAGS $1" - AC_TRY_LINK([#include ], - [printf("Hello, World!\n");], - AC_MSG_RESULT([yes]), - AC_MSG_RESULT([no]) - CFLAGS="$pa_add_cflags__old_cflags")]) - -dnl -------------------------------------------------------------------------- -dnl PA_SIGSETJMP -dnl -dnl Do we have sigsetjmp/siglongjmp? (AC_CHECK_FUNCS doesn't seem to work -dnl for these particular functions.) -dnl -------------------------------------------------------------------------- -AC_DEFUN(PA_SIGSETJMP, -[AC_MSG_CHECKING([for sigsetjmp]) - AC_TRY_LINK([ -#ifdef HAVE_SETJMP_H -#include -#endif], - [sigjmp_buf buf; - sigsetjmp(buf,1); - siglongjmp(buf,2);], - AC_MSG_RESULT([yes]) - $1, - AC_MSG_RESULT([no]) - $2)]) - -dnl -------------------------------------------------------------------------- -dnl PA_MSGHDR_MSG_CONTROL -dnl -dnl Does struct msghdr have the msg_control field? -dnl -------------------------------------------------------------------------- -AH_TEMPLATE([HAVE_MSGHDR_MSG_CONTROL], -[Define if struct msghdr has the msg_control field.]) - -AC_DEFUN(PA_MSGHDR_MSG_CONTROL, - [AC_CHECK_MEMBER(struct msghdr.msg_control, - [AC_DEFINE(HAVE_MSGHDR_MSG_CONTROL)], - [], - [ -#include -#include -#include - ])]) - -dnl ------------------------------------------------------------------------ -dnl PA_STRUCT_IN_PKTINFO -dnl -dnl Look for definition of struct in_pktinfo, which at least has an -dnl ipi_addr member. Some versions of glibc lack struct in_pktinfo; -dnl if so we need to include the definition ourselves -- but we only -dnl want to do that if absolutely necessary! -dnl ------------------------------------------------------------------------ -AH_TEMPLATE([HAVE_STRUCT_IN_PKTINFO], -[Define if struct in_pktinfo is defined.]) - -AC_DEFUN(PA_STRUCT_IN_PKTINFO, - [AC_CHECK_MEMBER(struct in_pktinfo.ipi_addr, - [AC_DEFINE(HAVE_STRUCT_IN_PKTINFO)], - [], - [ -#include -#include -#include -#include -#include -#include - ])]) - - -dnl ------------------------------------------------------------------------ -dnl PA_STRUCT_SOCKADDR_IN6 -dnl -dnl Look for definition of struct sockaddr_in6, which at least has an -dnl sin6_addr member -dnl -AH_TEMPLATE([HAVE_STRUCT_SOCKADDR_IN6], -[Define if struct sockaddr_in6 is defined.]) - -AC_DEFUN(PA_STRUCT_SOCKADDR_IN6, - [AC_CHECK_MEMBER(struct sockaddr_in6.sin6_addr, - [ - AC_DEFINE(HAVE_STRUCT_SOCKADDR_IN6) - HAVE_INET6=true; - ], - [ - HAVE_INET6=false; - ], - [ -#include -#include -#include -#include - ])]) - -dnl ------------------------------------------------------------------------ -dnl PA_STRUCT_ADDRINFO -dnl -dnl Look for definition of struct addrinfo, which at least has an -dnl ai_addr member -dnl -AH_TEMPLATE([HAVE_STRUCT_ADDRINFO], -[Define if struct addrinfo is defined.]) - -AC_DEFUN(PA_STRUCT_ADDRINFO, - [AC_CHECK_MEMBER(struct addrinfo.ai_addr, - [AC_DEFINE(HAVE_STRUCT_ADDRINFO)], - [], - [ -#include -#include -#include - ])]) - -dnl ------------------------------------------------------------------------ -dnl PA_STRUCT_IN6_PKTINFO -dnl -dnl Look for definition of struct in6_pktinfo, which at least has an -dnl ipi6_addr member -dnl -AH_TEMPLATE([HAVE_STRUCT_IN6_PKTINFO], -[Define if struct in6_pktinfo is defined.]) - -AC_DEFUN(PA_STRUCT_IN6_PKTINFO, - [AC_CHECK_MEMBER(struct in6_pktinfo.ipi6_addr, - [AC_DEFINE(HAVE_STRUCT_IN6_PKTINFO)], - [], - [ -#include -#include -#include -#include - ])]) - -dnl -------------------------------------------------------------------------- -dnl PA_HAVE_TCPWRAPPERS -dnl -dnl Do we have the tcpwrappers -lwrap? This can't be done using AC_CHECK_LIBS -dnl due to the need to provide "allow_severity" and "deny_severity" variables -dnl -------------------------------------------------------------------------- -AH_TEMPLATE([HAVE_TCPWRAPPERS], -[Define if we have tcpwrappers (-lwrap) and .]) - -AC_DEFUN(PA_HAVE_TCPWRAPPERS, -[AC_CHECK_LIB([wrap], [main]) - AC_MSG_CHECKING([for tcpwrappers]) - AC_TRY_LINK( -[ -#include -int allow_severity = 0; -int deny_severity = 0; -], -[ - hosts_ctl("sample_daemon", STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN); -], -[ - AC_DEFINE(HAVE_TCPWRAPPERS) - AC_MSG_RESULT([yes]) -], -[ - AC_MSG_RESULT([no]) -])]) - -dnl ------------------------------------------------------------------------ -dnl PA_CHECK_INTTYPES_H_SANE -dnl -dnl At least some versions of AIX 4 have macros which are -dnl completely broken. Try to detect those. -dnl -------------------------------------------------------------------------- -AH_TEMPLATE([INTTYPES_H_IS_SANE], -[Define if the macros in are usable]) - -AC_DEFUN(PA_CHECK_INTTYPES_H_SANE, -[AC_CHECK_HEADERS(inttypes.h, - [ - AC_MSG_CHECKING([if inttypes.h is sane]) - AC_TRY_LINK( - [ -#include -#include - ], - [uintmax_t max = UINTMAX_C(0); - printf("%"PRIuMAX"\n", max);], - AC_MSG_RESULT([yes]) - AC_DEFINE(INTTYPES_H_IS_SANE), - AC_MSG_RESULT([no (AIX, eh?)])) - ]) -]) - -dnl ------------------------------------------------------------------------ -dnl PA_WITH_BOOL -dnl -dnl PA_WITH_BOOL(option, default, help, enable, disable) -dnl -dnl Provides a more convenient way to specify --with-option and -dnl --without-option, with a default. default should be either 0 or 1. -dnl ------------------------------------------------------------------------ -AC_DEFUN(PA_WITH_BOOL, -[AC_ARG_WITH([$1], [$3], -if test ["$withval"] != no; then -[$4] -else -[$5] -fi, -if test [$2] -ne 0; then -[$4] -else -[$5] -fi)]) - -dnl -------------------------------------------------------------------------- -dnl PA_HEADER_DEFINES -dnl -dnl PA_HEADER_DEFINES(header, type, value) -dnl -------------------------------------------------------------------------- -AC_DEFUN(PA_HEADER_DEFINES, -[AC_MSG_CHECKING([if $1 defines $3]) - AH_TEMPLATE([HAVE_$3_DEFINITION], [Define if $1 defines $3]) - AC_TRY_COMPILE([ -#include <$1> -], -[ -int main() -{ - $2 dummy = $3; - return 0; -} -], -[ - pa_header_define=`echo HAVE_$3_DEFINITION | tr '[a-z]' '[A-Z]'` - AC_DEFINE_UNQUOTED($pa_header_define) - AC_MSG_RESULT(yes) -], -[ - AC_MSG_RESULT(no) -])]) - -dnl -------------------------------------------------------------------------- -dnl PA_SEARCH_LIBS_AND_ADD -dnl -dnl PA_SEARCH_LIBS_AND_ADD(function, libraries [,function to add]) -dnl -------------------------------------------------------------------------- - -AC_DEFUN(PA_SEARCH_LIBS_AND_ADD, - [ - AH_TEMPLATE(AS_TR_CPP(HAVE_$1), [Define if $1 function was found]) - AC_SEARCH_LIBS($1, $2, - [ - AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1)) - pa_add_$1=false; - ], - [ - XTRA=true; - if test $# -eq 3; then - AC_LIBOBJ($3) - else - AC_LIBOBJ($1) - fi - pa_add_$1=true; - ])]) diff --git a/autoconf/m4/pa_add_cflags.m4 b/autoconf/m4/pa_add_cflags.m4 new file mode 100644 index 0000000..26d55c4 --- /dev/null +++ b/autoconf/m4/pa_add_cflags.m4 @@ -0,0 +1,9 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_CFLAGS(variable, flag [,actual_flag [,success [,failure]]]]) +dnl +dnl Attempt to add the given option to xFLAGS, if it doesn't break +dnl compilation. If the option to be tested is different than the +dnl option that should actually be added, add the option to be +dnl actually added as a second argument. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ADD_CFLAGS], [PA_ADD_FLAGS(CFLAGS, [$1], [$2], [$3], [$4])]) diff --git a/autoconf/m4/pa_add_flags.m4 b/autoconf/m4/pa_add_flags.m4 new file mode 100644 index 0000000..23f96f1 --- /dev/null +++ b/autoconf/m4/pa_add_flags.m4 @@ -0,0 +1,39 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_FLAGS(flagvar, flags) +dnl +dnl Add [flags] to the variable [flagvar] if and only if it is accepted +dnl by all languages affected by [flagvar], if those languages have +dnl been previously seen in the script. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ADD_FLAGS], +[ + AS_VAR_PUSHDEF([old], [_$0_$1_orig]) + AS_VAR_PUSHDEF([ok], [_$0_$1_ok]) + AS_VAR_PUSHDEF([flags], [$1]) + + AS_VAR_COPY([old], [flags]) + AS_VAR_SET([flags], ["$flags $2"]) + AS_VAR_SET([ok], [yes]) + + PA_LANG_FOREACH(PA_FLAGS_LANGLIST($1), + [AS_VAR_IF([ok], [yes], + [AC_MSG_CHECKING([if $]_AC_CC[ accepts $2]) + PA_BUILD_IFELSE([], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AS_VAR_SET([ok], [no])])]) + ]) + + AS_VAR_IF([ok], [yes], + [m4_ifnblank([$3],[AS_VAR_SET([flags], ["$old $3"])]) + m4_foreach_w([_pa_add_flags_flag], [m4_ifblank([$3],[$2],[$3])], + [AC_DEFINE(PA_SYM([$1_]_pa_add_flags_flag), 1, + [Define to 1 if compiled with the ]_pa_add_flags_flag[ compiler flag])]) + $4], + [AS_VAR_SET([flags], ["$old"]) + $5]) + + AS_VAR_POPDEF([flags]) + AS_VAR_POPDEF([ok]) + AS_VAR_POPDEF([old]) +]) diff --git a/autoconf/m4/pa_add_headers.m4 b/autoconf/m4/pa_add_headers.m4 new file mode 100644 index 0000000..d3c478a --- /dev/null +++ b/autoconf/m4/pa_add_headers.m4 @@ -0,0 +1,13 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_HEADERS(headers...) +dnl +dnl Call AC_CHECK_HEADERS(), and add to ac_includes_default if found +dnl -------------------------------------------------------------------------- +AC_DEFUN([_PA_ADD_HEADER], +[AC_CHECK_HEADERS([$1],[ac_includes_default="$ac_includes_default +#include <$1>" +]) +]) + +AC_DEFUN([PA_ADD_HEADERS], +[m4_map_args_w([$1],[_PA_ADD_HEADER(],[)])]) diff --git a/autoconf/m4/pa_add_langflags.m4 b/autoconf/m4/pa_add_langflags.m4 new file mode 100644 index 0000000..05c3114 --- /dev/null +++ b/autoconf/m4/pa_add_langflags.m4 @@ -0,0 +1,27 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_LANGFLAGS(flag...) +dnl +dnl Attempt to add the option in the given list to each compiler flags +dnl (CFLAGS, CXXFLAGS, ...), if it doesn't break compilation. +dnl -------------------------------------------------------------------------- +m4_defun([_PA_LANGFLAG_VAR], +[m4_case([$1], + [C], [CFLAGS], + [C++], [CXXFLAGS], + [Fortran 77], [FFLAGS], + [Fortran], [FCFLAGS], + [Erlang], [ERLCFLAGS], + [Objective C], [OBJCFLAGS], + [Objective C++], [OBJCXXFLAGS], + [Go], [GOFLAGS], + [m4_fatal([PA_ADD_LANGFLAGS: Unknown language: $1])])]) + +AC_DEFUN([PA_ADD_LANGFLAGS], +[m4_pushdef([_pa_langflags],m4_dquote($1))dnl +m4_set_foreach(_PA_LANG_SEEN_SET,[_pa_lang],dnl +[_pa_flag_found=no + m4_foreach_w([_pa_flag], _pa_langflags, + [AS_IF([test $_pa_flag_found = no], + [PA_ADD_FLAGS(_PA_LANGFLAG_VAR(_pa_lang),_pa_flag,[],[_pa_flag_found=yes])]) + ])]) +m4_popdef([_pa_langflags])]) diff --git a/autoconf/m4/pa_arg_bool.m4 b/autoconf/m4/pa_arg_bool.m4 new file mode 100644 index 0000000..5289ed4 --- /dev/null +++ b/autoconf/m4/pa_arg_bool.m4 @@ -0,0 +1,19 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ARG_BOOL(option,helptext,default,enabled_action,disabled_action) +dnl +dnl The last three arguments are optional; default can be yes or no. +dnl +dnl Simpler-to-use versions of AC_ARG_ENABLED, that include the +dnl test for $enableval and the AS_HELP_STRING definition. This is only +dnl to be used for boolean options. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ARG_BOOL], +[m4_pushdef([pa_default],m4_default(m4_normalize([$3]),[no])) + m4_pushdef([pa_option],m4_case(pa_default,[yes],[disable],[enable])) + AC_ARG_ENABLE([$1], + [AS_HELP_STRING([--]m4_defn([pa_option])[-$1],[$2])], + [pa_arg_bool_enableval="$enableval"], + [pa_arg_bool_enableval="]m4_defn([pa_default])["]) + m4_popdef([pa_option], [pa_default]) + AS_IF([test x"$pa_arg_bool_enableval" != xno], [$4], [$5]) +]) diff --git a/autoconf/m4/pa_arg_disabled.m4 b/autoconf/m4/pa_arg_disabled.m4 new file mode 100644 index 0000000..42f4ce8 --- /dev/null +++ b/autoconf/m4/pa_arg_disabled.m4 @@ -0,0 +1,4 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ARG_DISABLED(option,helptext,disabled_action,enabled_action) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ARG_DISABLED],[PA_ARG_BOOL([$1],[$2],yes,[$4],[$3])]) diff --git a/autoconf/m4/pa_arg_enabled.m4 b/autoconf/m4/pa_arg_enabled.m4 new file mode 100644 index 0000000..7d66a21 --- /dev/null +++ b/autoconf/m4/pa_arg_enabled.m4 @@ -0,0 +1,4 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ARG_ENABLED(option,helptext,enabled_action,disabled_action) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ARG_ENABLED],[PA_ARG_BOOL([$1],[$2],no,[$3],[$4])]) diff --git a/autoconf/m4/pa_build_ifelse.m4 b/autoconf/m4/pa_build_ifelse.m4 new file mode 100644 index 0000000..1aacfcd --- /dev/null +++ b/autoconf/m4/pa_build_ifelse.m4 @@ -0,0 +1,16 @@ +dnl -------------------------------------------------------------------------- +dnl PA_BUILD_IFELSE(input [,success [,failure]]) +dnl +dnl Same as AC_LINK_IFELSE for languages where linking is applicable, +dnl otherwise AC_COMPILE_IFELSE. +dnl +dnl If the first argument is empty, use _AC_LANG_IO_PROGRAM. +dnl -------------------------------------------------------------------------- +m4_defun([_PA_BUILD_IFELSE], +[m4_case(_AC_LANG, + [Erlang], [AC_COMPILE_IFELSE($@)], + [AC_LINK_IFELSE($@)])]) + +AC_DEFUN([PA_BUILD_IFELSE], +[_PA_BUILD_IFELSE([m4_ifblank([$1],[AC_LANG_SOURCE(_AC_LANG_IO_PROGRAM)], + [$1])],[$2],[$3])]) diff --git a/autoconf/m4/pa_c_typeof.m4 b/autoconf/m4/pa_c_typeof.m4 new file mode 100644 index 0000000..909b171 --- /dev/null +++ b/autoconf/m4/pa_c_typeof.m4 @@ -0,0 +1,32 @@ +dnl -------------------------------------------------------------------------- +dnl PA_C_TYPEOF +dnl +dnl Find if typeof() exists, or an equivalent (__typeof__, decltype, +dnl __decltype__) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_C_TYPEOF], +[AC_CACHE_CHECK([if $CC supports typeof], [pa_cv_typeof], + [pa_cv_typeof=no + for pa_typeof_try in typeof __typeof __typeof__ decltype __decltype __decltype__ _Decltype + do + AS_IF([test $pa_cv_typeof = no], + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([ +AC_INCLUDES_DEFAULT +int testme(int x); +int testme(int x) +{ + $pa_typeof_try(x) y = x*x; + return y; +} +])], + [pa_cv_typeof=$pa_typeof_try])]) + done + ]) + AS_IF([test $pa_cv_typeof = no], + [], + [AC_DEFINE([HAVE_TYPEOF], 1, + [Define to 1 if you have some version of the typeof operator.]) + AS_IF([test $pa_cv_typeof = typeof], + [], + [AC_DEFINE_UNQUOTED([typeof], [$pa_cv_typeof], + [Define if your typeof operator is not named `typeof'.])])])]) diff --git a/autoconf/m4/pa_check_bad_stdc_inline.m4 b/autoconf/m4/pa_check_bad_stdc_inline.m4 new file mode 100644 index 0000000..3fbc53a --- /dev/null +++ b/autoconf/m4/pa_check_bad_stdc_inline.m4 @@ -0,0 +1,26 @@ +dnl -------------------------------------------------------------------------- +dnl PA_CHECK_BAD_STDC_INLINE +dnl +dnl Some versions of gcc seem to apply -Wmissing-prototypes to C99 +dnl inline functions, which means we need to use GNU inline syntax +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_CHECK_BAD_STDC_INLINE], +[AC_MSG_CHECKING([if $CC supports C99 external inlines]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([ +AC_INCLUDES_DEFAULT + +/* Don't mistake GNU inlines for c99 */ +#if defined(__GNUC__) && !defined(__GNUC_STDC_INLINE__) +# error "Using gnu inline standard" +#endif + +inline int foo(int x) +{ + return x+1; +} + ])], + [AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_STDC_INLINE], 1, + [Define to 1 if your compiler supports C99 extern inline])], + [AC_MSG_RESULT([no]) + PA_ADD_CFLAGS([-fgnu89-inline])])]) diff --git a/autoconf/m4/pa_check_inttypes_h_sane.m4 b/autoconf/m4/pa_check_inttypes_h_sane.m4 new file mode 100644 index 0000000..589e0a2 --- /dev/null +++ b/autoconf/m4/pa_check_inttypes_h_sane.m4 @@ -0,0 +1,17 @@ +dnl ------------------------------------------------------------------------ +dnl PA_CHECK_INTTYPES_H_SANE +dnl +dnl At least some versions of AIX 4 have macros which are +dnl completely broken. Try to detect those. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_CHECK_INTTYPES_H_SANE], +[AC_CHECK_HEADERS_ONCE(inttypes.h) +AS_IF([test "x$ac_cv_header_inttypes_h" = xyes],[ + AC_MSG_CHECKING([if inttypes.h is sane]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT], + [uintmax_t max = UINTMAX_C(0); + printf("%"PRIuMAX"\n", max);])], + [AC_MSG_RESULT([yes]) + AC_DEFINE(INTTYPES_H_IS_SANE, 1, + [Define if the macros in are usable])], + [AC_MSG_RESULT([no (AIX, eh?)])])])]) diff --git a/autoconf/m4/pa_cross_compile.m4 b/autoconf/m4/pa_cross_compile.m4 new file mode 100644 index 0000000..f082973 --- /dev/null +++ b/autoconf/m4/pa_cross_compile.m4 @@ -0,0 +1,41 @@ +dnl -------------------------------------------------------------------------- +dnl PA_CROSS_COMPILE +dnl +dnl Get the canonical name for the build and host (runtime) systems; +dnl then figure out if this is cross-compilation. Specifically, this +dnl disables invoking WINE on non-Windows systems which are configured +dnl to run WINE automatically. +dnl +dnl Use PA_CROSS_COMPILE_TOOL if the target system (output of a code- +dnl generation tool) is applicable. +dnl +dnl This doesn't explicitly print any messages as that is automatically +dnl done elsewhere. +dnl -------------------------------------------------------------------------- +AC_DEFUN_ONCE([PA_CROSS_COMPILE], +[ + AC_BEFORE([$0], [AC_LANG_COMPILER]) + AC_BEFORE([$0], [AC_LANG]) + AC_BEFORE([$0], [AC_PROG_CC]) + AC_BEFORE([$0], [AC_PROG_CPP]) + AC_BEFORE([$0], [AC_PROG_CXX]) + AC_BEFORE([$0], [AC_PROG_CXXCPP]) + AC_BEFORE([$0], [AC_PROG_OBJC]) + AC_BEFORE([$0], [AC_PROG_OBJCPP]) + AC_BEFORE([$0], [AC_PROG_OBJCXX]) + AC_BEFORE([$0], [AC_PROG_OBJCXXCPP]) + AC_BEFORE([$0], [AC_PROG_F77]) + AC_BEFORE([$0], [AC_PROG_FC]) + AC_BEFORE([$0], [AC_PROG_GO]) + + # Disable WINE + WINELOADER=/dev/null + export WINELOADER + WINESERVER=/dev/null + export WINESERVER + WINEPREFIX=/dev/null + export WINEPREFIX + + AC_CANONICAL_BUILD + AC_CANONICAL_HOST +]) diff --git a/autoconf/m4/pa_flags_langlist.m4 b/autoconf/m4/pa_flags_langlist.m4 new file mode 100644 index 0000000..2ef5ded --- /dev/null +++ b/autoconf/m4/pa_flags_langlist.m4 @@ -0,0 +1,19 @@ +dnl -------------------------------------------------------------------------- +dnl PA_FLAGS_LANGLIST(flagvar) +dnl +dnl Return a list of languages affected by the variable flagvar. +dnl If flagvar is unknown, assume it affects the current language. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_FLAGS_LANGLIST], +[m4_dquote(m4_case([$1], + [CPPFLAGS], [[C],[C++],[Objective C],[Objective C++]], + [CFLAGS], [[C]], + [CXXFLAGS], [[C++]], + [FFLAGS], [[Fortran 77]], + [FCFLAGS], [[Fortran]], + [ERLCFLAGS], [[Erlang]], + [OBJCFLAGS], [[Objective C]], + [OBJCXXFLAGS], [[Objective C++]], + [GOFLAGS], [[Go]], + [LDFLAGS], [[C],[C++],[Fortran 77],[Fortran],[Objective C],[Objective C++],[Go]], + m4_dquote(_AC_LANG)))]) diff --git a/autoconf/m4/pa_have_tcpwrappers.m4 b/autoconf/m4/pa_have_tcpwrappers.m4 new file mode 100644 index 0000000..fba87ce --- /dev/null +++ b/autoconf/m4/pa_have_tcpwrappers.m4 @@ -0,0 +1,26 @@ +dnl -------------------------------------------------------------------------- +dnl PA_HAVE_TCPWRAPPERS +dnl +dnl Do we have the tcpwrappers -lwrap? This can't be done using AC_CHECK_LIBS +dnl due to the need to provide "allow_severity" and "deny_severity" variables +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_HAVE_TCPWRAPPERS], +[AC_CHECK_LIB([wrap], [main]) + AC_MSG_CHECKING([for tcpwrappers]) + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[ +#include +int allow_severity = 0; +int deny_severity = 0; + ]], + [[ + hosts_ctl("sample_daemon", STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN); + ]])], + [ + AC_DEFINE(HAVE_TCPWRAPPERS, 1, + [Define if we have tcpwrappers (-lwrap) and .]) + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + ])]) diff --git a/autoconf/m4/pa_lang_foreach.m4 b/autoconf/m4/pa_lang_foreach.m4 new file mode 100644 index 0000000..650913d --- /dev/null +++ b/autoconf/m4/pa_lang_foreach.m4 @@ -0,0 +1,15 @@ +dnl -------------------------------------------------------------------------- +dnl PA_LANG_FOREACH(subset, body) +dnl +dnl Expand [body] for each language encountered in the configure script also +dnl present in [subset], or all if [subset] is empty +dnl -------------------------------------------------------------------------- +AC_DEFUN([_PA_LANG_DO],dnl +[AC_LANG([$2])dnl +$1]) + +AC_DEFUN([PA_LANG_FOREACH],dnl +[m4_pushdef([_pa_lang_foreach_current],[_AC_LANG])dnl +m4_map_args([m4_curry([_PA_LANG_DO],[$2])],m4_unquote(PA_LANG_SEEN_LIST($1)))dnl +AC_LANG(_pa_lang_foreach_current)dnl +m4_popdef([_pa_lang_foreach_current])]) diff --git a/autoconf/m4/pa_lang_seen_list.m4 b/autoconf/m4/pa_lang_seen_list.m4 new file mode 100644 index 0000000..d524013 --- /dev/null +++ b/autoconf/m4/pa_lang_seen_list.m4 @@ -0,0 +1,20 @@ +dnl -------------------------------------------------------------------------- +dnl PA_LANG_SEEN_LIST(subset) +dnl +dnl List of the language lang has been used in the configuration +dnl script so far, possibly subset by [subset]. +dnl +dnl This relies on overriding _AC_LANG_SET(from, to), +dnl the internal implementation of _AC_LANG. +dnl -------------------------------------------------------------------------- +m4_ifndef([_PA_LANG_SET], +[m4_rename([_AC_LANG_SET], [_PA_LANG_SET])dnl +m4_defun([_AC_LANG_SET], [m4_set_add([_PA_LANG_SEEN_SET],[$2])dnl +_PA_LANG_SET($@)])]) + +AC_DEFUN([PA_LANG_SEEN_LIST], +[m4_set_delete([_pa_lang_seen_subset])dnl +m4_pushdef([_pa_lang_seen_subset_list],m4_ifnblank([$1],[$1],m4_dquote(m4_set_list([_PA_LANG_SEEN_SET]))))dnl +m4_set_add_all([_pa_lang_seen_subset],_pa_lang_seen_subset_list)dnl +m4_cdr(m4_set_intersection([_pa_lang_seen_subset],[_PA_LANG_SEEN_SET]))dnl +m4_popdef([_pa_lang_seen_subset_list])]) diff --git a/autoconf/m4/pa_option_debug.m4 b/autoconf/m4/pa_option_debug.m4 new file mode 100644 index 0000000..ae7d9db --- /dev/null +++ b/autoconf/m4/pa_option_debug.m4 @@ -0,0 +1,13 @@ +dnl -------------------------------------------------------------------------- +dnl PA_OPTION_DEBUG(with_debug, without_debug) +dnl +dnl Set debug flags and optimization flags depending on if +dnl --enable-debug is set or not. Some flags are set regardless... +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_OPTION_DEBUG], +[PA_ARG_DISABLED([gdb], [disable gdb debug extensions], + [PA_ADD_LANGFLAGS([-g3])], [PA_ADD_LANGFLAGS([-ggdb3 -g3])]) + PA_ARG_ENABLED([debug], [optimize for debugging], + [PA_ADD_LANGFLAGS([-Og -O0]) + $1], + [$2])]) diff --git a/autoconf/m4/pa_option_profiling.m4 b/autoconf/m4/pa_option_profiling.m4 new file mode 100644 index 0000000..39a3f6c --- /dev/null +++ b/autoconf/m4/pa_option_profiling.m4 @@ -0,0 +1,8 @@ +dnl -------------------------------------------------------------------------- +dnl PA_OPTION_PROFILING(with_profiling, without_profiling) +dnl +dnl Try to enable profiling if --enable-profiling is set. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_OPTION_PROFILING], +[PA_ARG_ENABLED([profiling], [compile with profiling (-pg option)], +[PA_ADD_LANGFLAGS([-pg])])]) diff --git a/autoconf/m4/pa_prog_cc.m4 b/autoconf/m4/pa_prog_cc.m4 new file mode 100644 index 0000000..0debe5d --- /dev/null +++ b/autoconf/m4/pa_prog_cc.m4 @@ -0,0 +1,13 @@ +dnl -------------------------------------------------------------------------- +dnl PA_PROG_CC() +dnl +dnl Similar to AC_PROG_CC, but add a prototype for main() to +dnl AC_INCLUDES_DEFAULT to avoid -Werror from breaking compilation. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_PROG_CC], +[AC_PROG_CC + AS_IF([test x$ac_cv_prog != xno], + [ac_includes_default="$ac_includes_default +#ifndef __cplusplus +extern int main(void); +#endif"])]) diff --git a/autoconf/m4/pa_search_libs_and_add.m4 b/autoconf/m4/pa_search_libs_and_add.m4 new file mode 100644 index 0000000..b006128 --- /dev/null +++ b/autoconf/m4/pa_search_libs_and_add.m4 @@ -0,0 +1,22 @@ +dnl -------------------------------------------------------------------------- +dnl PA_SEARCH_LIBS_AND_ADD +dnl +dnl PA_SEARCH_LIBS_AND_ADD(function, libraries [,function to add]) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_SEARCH_LIBS_AND_ADD], + [ + AH_TEMPLATE(AS_TR_CPP(HAVE_$1), [Define if $1 function was found]) + AC_SEARCH_LIBS($1, $2, + [ + AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1)) + pa_add_$1=false; + ], + [ + XTRA=true; + if test $# -eq 3; then + AC_LIBOBJ($3) + else + AC_LIBOBJ($1) + fi + pa_add_$1=true; + ])]) diff --git a/autoconf/m4/pa_sigsetjmp.m4 b/autoconf/m4/pa_sigsetjmp.m4 new file mode 100644 index 0000000..898b562 --- /dev/null +++ b/autoconf/m4/pa_sigsetjmp.m4 @@ -0,0 +1,25 @@ +dnl -------------------------------------------------------------------------- +dnl PA_SIGSETJMP +dnl +dnl Do we have sigsetjmp/siglongjmp? (AC_CHECK_FUNCS doesn't seem to work +dnl for these particular functions.) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_SIGSETJMP], +[AC_MSG_CHECKING([for sigsetjmp]) + AC_LINK_IFELSE([AC_LANG_SOURCE( +[ +AC_INCLUDES_DEFAULT +#include + +int main(void) { + sigjmp_buf buf; + if (sigsetjmp(buf,1)) + return 0; + siglongjmp(buf,2); + return 1; + } +])], + [AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_SIGSETJMP], 1, + [Define to 1 if your system has sigsetjmp/siglongjmp])], + [AC_MSG_RESULT([no])])]) diff --git a/autoconf/m4/pa_sym.m4 b/autoconf/m4/pa_sym.m4 new file mode 100644 index 0000000..d3a8965 --- /dev/null +++ b/autoconf/m4/pa_sym.m4 @@ -0,0 +1,11 @@ +dnl -------------------------------------------------------------------------- +dnl PA_SYM(prefix, string) +dnl +dnl Convert a (semi-) arbitrary string to a CPP symbol +dnl Compact underscores and convert non-C characters to underscore, +dnl except + which is converted to X (so C++ -> CXX). +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_SYM], +[m4_bpatsubsts(m4_quote(m4_toupper([$*])), + [,],[],[\+],[X],[[^ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]+],[_],dnl +[^._?\(.*\)_.$],[[\1]])]) diff --git a/autogen.sh b/autogen.sh index 728a381..0c892cc 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,2 +1,83 @@ -#!/bin/sh -make autoconf +#!/bin/sh -x +# +# Run this script to regenerate autoconf files +# +recheck=false +for arg; do + case x"$arg" in + x--recheck) + recheck=true + config=$(sh config.status --config 2>/dev/null) + ;; + x--clearenv) + unset AUTOCONF AUTOMAKE ACLOCAL AUTOHEADER ACLOCAL_PATH + ;; + *) + echo "$0: unknown option: $arg" 1>&2 + ;; + esac +done + +# This allows for overriding the default autoconf programs +AUTOCONF="${AUTOCONF:-${AUTOTOOLS_PREFIX}autoconf}" +AUTOMAKE="${AUTOMAKE:-${AUTOTOOLS_PREFIX}automake}" +ACLOCAL="${ACLOCAL:-${AUTOTOOLS_PREFIX}aclocal}" +AUTOHEADER="${AUTOHEADER:-${AUTOTOOLS_PREFIX}autoheader}" + +mkdir -p autoconf autoconf/helpers config +autolib="`"$AUTOMAKE" --print-libdir`" +if test ! x"$autolib" = x; then + for prg in install-sh compile config.guess config.sub; do + # Update autoconf helpers if and only if newer ones are available + if test -f "$autolib"/"$prg" && \ + ( set -e ; \ + test -f autoconf/helpers/"$prg" && sed -n \ + -e 's/^scriptver=/scriptversion=/' \ + -e 's/^timestamp=/scriptversion=/' \ + -e 's/^scriptversion=['\''"]?\([^'\''"]*\).*$/\1/p' \ + "$autolib"/"$prg" autoconf/helpers/"$prg" | \ + sort -c 2>/dev/null ; \ + test $? -ne 0 ) + then + cp -f "$autolib"/"$prg" autoconf/helpers + fi + done +fi +mv -f autoconf/aclocal.m4 autoconf/aclocal.m4.old +mkdir -p autoconf/m4.old autoconf/m4 +mv -f autoconf/m4/*.m4 autoconf/m4.old/ 2>/dev/null || true +ACLOCAL_PATH="${ACLOCAL_PATH}${ACLOCAL_PATH:+:}`pwd`/autoconf/m4.old" +export ACLOCAL_PATH +"$ACLOCAL" --install --output=autoconf/aclocal.m4 -I autoconf/m4 +if test ! -f autoconf/aclocal.m4; then + # aclocal failed, revert to previous files + mv -f autoconf/m4.old/*.m4 autoconf/m4/ + mv -f autoconf/aclocal.m4.old autoconf/aclocal.m4 + exit 1 +fi +rm -rf autoconf/*m4.old +"$AUTOHEADER" -B autoconf +"$AUTOCONF" -B autoconf +( + echo '#!/bin/sh' + "$AUTOCONF" -B autoconf \ + -t AC_CONFIG_HEADERS:'rm -f $*' \ + -t AC_CONFIG_FILES:'rm -f $*' + echo 'rm -f config.log config.status' + echo 'rm -rf autom4te.cache' +) > autoconf/clean.sh +chmod +x autoconf/clean.sh +sh autoconf/clean.sh + +rm -f configure~ || true + +# Try to regenerate unconfig.h if Perl is available and unconfig.pl +# is present in the autoconf directory. +if [ -n "$(which perl)" -a -f autoconf/unconfig.pl ]; then + perl autoconf/unconfig.pl . config/config.h.in config/unconfig.h +fi + +if $recheck; then + # This bizarre statement has to do with how config.status quotes its output + echo exec sh configure $config | sh - +fi diff --git a/common/Makefile b/common/Makefile index a825213..89e351d 100644 --- a/common/Makefile +++ b/common/Makefile @@ -1,10 +1,10 @@ SRCROOT = .. VERSION = $(shell cat ../version) --include ../MCONFIG +-include ../config/MCONFIG include ../MRULES -OBJS = tftpsubs.$(O) +OBJS = tftpsubs.$(O) signal.$(O) LIB = libcommon.a all: $(LIB) diff --git a/common/signal.c b/common/signal.c new file mode 100644 index 0000000..ccd4af8 --- /dev/null +++ b/common/signal.c @@ -0,0 +1,19 @@ +/* + * signal.c + * + * User-friendly wrapper around sigaction(). + */ + +#include "config.h" + +int tftp_signal(int signum, sighandler_t handler, int flags) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof sa); + sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = flags; + + return sigaction(signum, &sa, NULL); +} diff --git a/config.h b/config.h index 0e35438..a8c55ac 100644 --- a/config.h +++ b/config.h @@ -1,6 +1,6 @@ /* -*- c -*- ------------------------------------------------------------- * * - * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2024 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -17,12 +17,23 @@ #ifndef CONFIG_H #define CONFIG_H 1 +/* Feature enables for specific environments */ +#ifdef __APPLE__ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1070 +#define __APPLE_USE_RFC_3542 1 +#endif +#endif + /* Must be included before we include any system headers! */ -#include "aconfig.h" /* autogenerated configuration header */ +#include "config/config.h" /* autogenerated configuration header */ /* Standard includes */ #include +#include +#include +#include +#include #ifdef HAVE_SYS_TYPES_H #include @@ -32,21 +43,6 @@ #include #endif -#ifdef STDC_HEADERS -#include -#include -#else -#ifdef HAVE_STDLIB_H -#include -#endif -#endif - -#ifdef HAVE_MEMORY_H -#ifndef STDC_HEADERS -#include -#endif -#endif - #ifdef HAVE_STRING_H #include #endif @@ -73,15 +69,8 @@ #include #endif -#ifdef TIME_WITH_SYS_TIME +#ifdef HAVE_SYS_TIME_H #include -#include -#else -#if HAVE_SYS_TIME_H -#include -#else -#include -#endif #endif #ifdef HAVE_GRP_H @@ -92,9 +81,6 @@ #include #endif -#include -#include - #ifdef HAVE_SYS_SOCKET_H #include #else @@ -129,10 +115,10 @@ /* Some broken systems care about text versus binary, but real Unix systems don't... */ -#ifndef HAVE_O_TEXT_DEFINITION +#if !HAVE_DECL_O_TEXT #define O_TEXT 0 #endif -#ifndef HAVE_O_BINARY_DEFINITION +#if !HAVE_DECL_O_BINARY #define O_BINARY 0 #endif @@ -267,11 +253,9 @@ typedef int socklen_t; #include -#ifndef HAVE_IPPORT_TFTP_DEFINITION -#ifndef IPPORT_TFTP +#if !HAVE_DECL_IPPORT_TFTP && !defined(IPPORT_TFTP) #define IPPORT_TFTP 69 #endif -#endif /* arpa/{inet,tftp}.h, and possible missing pieces */ @@ -293,9 +277,11 @@ typedef int socklen_t; void *xmalloc(size_t); char *xstrdup(const char *); -#ifndef HAVE_BSD_SIGNAL -void (*bsd_signal(int, void (*)(int))) (int); +#ifndef HAVE_SIGHANDLER_T +typedef void (*sighandler_t)(int); #endif +int tftp_signal(int, sighandler_t, int); + #ifndef HAVE_DUP2 int dup2(int, int); #endif diff --git a/MCONFIG.in b/config/MCONFIG.in similarity index 100% rename from MCONFIG.in rename to config/MCONFIG.in diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..deaa4fc --- /dev/null +++ b/configure.ac @@ -0,0 +1,283 @@ +dnl Process this file with autoconf 2.71 or later to produce +dnl a configure script. +AC_PREREQ([2.71]) +AC_INIT +AC_CONFIG_SRCDIR([MRULES]) +AC_PREFIX_DEFAULT([/usr]) +AC_CONFIG_AUX_DIR([autoconf/helpers]) + +dnl This prevents us from running Wine and thinking we are not +dnl cross-compiling when in fact we are; running Wine here is at +dnl the best very slow and doesn't buy us a single thing at all. +PA_CROSS_COMPILE + +dnl Enable any available C extensions +PA_PROG_CC +AC_USE_SYSTEM_EXTENSIONS + +dnl Options for debugging and profiling +PA_OPTION_DEBUG +PA_OPTION_PROFILING + +dnl LLVM doesn't error out on invalid -W options unless this option is +dnl specified first. Enable this so this script can actually discover +dnl which -W options are possible for this compiler. +PA_ADD_CFLAGS([-Werror=unknown-warning-option]) + +dnl Force gcc and gcc-compatible compilers treat signed integers +dnl as 2's complement +PA_ADD_CFLAGS([-fwrapv]) + +dnl Force clang to behave in a predictable manner, in order to make bugs +dnl possible to track down. gcc appears to have this behavior by default. +PA_ADD_CFLAGS([-ftrivial-auto-var-init=zero]) + +dnl Some environments abuse __STRICT_ANSI__ to disable some +dnl function declarations +PA_ADD_CFLAGS([-U__STRICT_ANSI__]) + +dnl Don't put things in common if we can avoid it. We don't want to +dnl assume all compilers support common, and this will help find those +dnl problems. This also works around an OSX linker problem. +PA_ADD_CFLAGS([-fno-common]) + +dnl Tests which may trigger warnings on some compilers +AC_C_CONST +AC_C_INLINE +AC_C_RESTRICT + +dnl Checks for header files. +AC_CHECK_INCLUDES_DEFAULT + +dnl See if we need extra libraries +XTRA=false + +AC_SEARCH_LIBS([strerror],[cposix]) + +AC_CHECK_HEADERS_ONCE(inttypes.h) +AC_CHECK_HEADERS_ONCE(stdint.h) +AC_CHECK_HEADERS_ONCE(grp.h) +AC_CHECK_HEADERS_ONCE(libgen.h) +AC_CHECK_HEADERS_ONCE(setjmp.h) +AC_CHECK_HEADERS_ONCE(strings.h) +AC_CHECK_HEADERS_ONCE(sysexits.h) +AC_CHECK_HEADERS_ONCE(unistd.h) +AC_CHECK_HEADERS_ONCE(sys/filio.h) +AC_CHECK_HEADERS_ONCE(sys/stat.h) +AC_CHECK_HEADERS_ONCE(sys/time.h) +PA_CHECK_INTTYPES_H_SANE + +dnl This is needed on some versions of FreeBSD... +AC_CHECK_HEADERS_ONCE(machine/param.h) + +dnl Windows... +PA_ADD_HEADERS(windows.h) +PA_ADD_HEADERS(winsock2.h) +AS_IF([test "x$ac_cv_header_winsock2_h" != xyes], + [PA_ADD_HEADERS(winsock.h)]) + +PA_ADD_HEADERS(fcntl.h) +PA_ADD_HEADERS(sys/types.h) +PA_ADD_HEADERS(arpa/inet.h) +PA_ADD_HEADERS(sys/socket.h) +PA_ADD_HEADERS(sys/file.h) +PA_ADD_HEADERS(netinet/in.h) +PA_ADD_HEADERS(sys/uio.h) +PA_ADD_HEADERS(netdb.h) + +AC_TYPE_OFF_T +AC_TYPE_PID_T +AC_TYPE_MODE_T +AC_TYPE_SIZE_T +AC_CHECK_TYPES(intmax_t) +AC_CHECK_TYPES(long long) +AC_CHECK_TYPES(uint16_t) +AC_CHECK_TYPES(uint32_t) +AC_CHECK_TYPES(u_short) +AC_CHECK_TYPES(u_long) + +AC_CHECK_TYPES(socklen_t) + +AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , + [AC_MSG_ERROR(socket library not found)]) + +AC_CHECK_FUNCS(fcntl) +AC_CHECK_FUNCS(flock) +AC_CHECK_FUNCS(setsid) +AC_CHECK_FUNCS(recvmsg) +AC_CHECK_FUNCS(ftruncate) +AC_CHECK_FUNCS(setresuid) +AC_CHECK_FUNCS(setreuid) +AC_CHECK_FUNCS(setresgid) +AC_CHECK_FUNCS(setregid) +AC_CHECK_FUNCS(initgroups) +AC_CHECK_FUNCS(setgroups) +AC_CHECK_TYPES(sighandler_t) + +dnl Solaris 8 has [u]intmax_t but not strtoumax(). How utterly braindamaged. +AC_CHECK_FUNCS(strtoumax) +AC_CHECK_FUNCS(strtoull) + +AC_CHECK_MEMBERS(struct msghdr.msg_control) +AC_CHECK_MEMBERS(struct in_pktinfo.ipi_addr) +AC_CHECK_MEMBERS(struct addrinfo.ai_addr) + +AC_CHECK_DECLS([O_NONBLOCK, O_BINARY, O_TEXT]) +AC_CHECK_DECLS([F_SETLK]) +AC_CHECK_DECLS([LOCK_SH, LOCK_EX]) + +PA_SIGSETJMP + +dnl +dnl Get common paths +dnl +SRCROOT=`cd $srcdir && pwd` +OBJROOT=`pwd` + +PA_SEARCH_LIBS_AND_ADD(xmalloc, iberty) +PA_SEARCH_LIBS_AND_ADD(xstrdup, iberty) +PA_SEARCH_LIBS_AND_ADD(getopt_long, getopt, getopt_long) +PA_SEARCH_LIBS_AND_ADD(getaddrinfo, [nsl resolv]) +AS_IF([$pa_add_getaddrinfo], +[AC_SEARCH_LIBS(gethostbyname, [nsl resolv], + [AC_SEARCH_LIBS(herror, [nsl resolv], , + [AC_MSG_ERROR(herror not found)])], + [AC_MSG_ERROR(gethostbyname not found)])], +[AC_SEARCH_LIBS(freeaddrinfo, [nsl resolv], , + [AC_MSG_ERROR(getaddrinfo but not freeaddrinfo found)]) + AC_SEARCH_LIBS(gai_strerror, [nsl resolv], , + [AC_MSG_ERROR(getaddrinfo but not gai_strerror found)])]) + +PA_SEARCH_LIBS_AND_ADD(inet_ntop, [nsl resolv]) +AS_IF([$pa_add_inet_ntop], + [AC_SEARCH_LIBS(inet_ntoa, [nsl resolv], , + [AC_MSG_ERROR(inet_ntoa not found)])]) + +AC_SEARCH_LIBS(inet_aton, [nsl resolv], ,[AC_MSG_ERROR(inet_aton not found)]) + +PA_SEARCH_LIBS_AND_ADD(daemon) +PA_SEARCH_LIBS_AND_ADD(dup2) + +AS_IF([$XTRA], [XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS"]) + +dnl +dnl These libraries apply to the server only +dnl + +common_libs="$LIBS" + +AC_CHECK_DECLS(IPPORT_TFTP) + +PA_ARG_DISABLED([tcpwrappers], + [disable tcpwrapper permissions checking], [], + [ + AC_SEARCH_LIBS(yp_get_default_domain, [nsl resolv]) + PA_HAVE_TCPWRAPPERS + ]) + +AC_CHECK_HEADERS_ONCE([regex.h]) +PA_ARG_DISABLED([remap], + [disable regex-based filename remapping], [], + [AS_IF([test x"$ac_cv_header_regex_h" = xyes], + [AC_SEARCH_LIBS(regcomp, [regex rx], + [AC_DEFINE([WITH_REGEX], 1, + [Define if we are compiling with regex filename remapping.]) + TFTPDOBJS="remap.\$(O) $TFTPOBJS"])])]) + +TFTPD_LIBS="$LIBS $XTRALIBS" +LIBS="$common_libs" + +dnl +dnl These libraries apply to the client only +dnl + +AH_TEMPLATE([WITH_READLINE], +[Define if we are compiling with readline/editline command-line editing.]) + +PA_ARG_DISABLED([readline], + [disable the use of readline command-line editing], [], + [ + AC_CHECK_HEADER([readline/readline.h], + [ + dnl readline may need libtermcap or somesuch... + AC_SEARCH_LIBS(tputs, [termcap terminfo]) + + AC_SEARCH_LIBS(readline, [readline history], + [AC_DEFINE(WITH_READLINE)]) + AC_CHECK_HEADERS(readline/history.h) + ], + [AC_CHECK_HEADER([editline/readline.h], + [ + dnl editline may need libtermcap or somesuch... + AC_SEARCH_LIBS(tputs, [termcap terminfo]) + + AC_SEARCH_LIBS(editline, [edit], + [AC_DEFINE(WITH_READLINE)]) + ])]) +],:) + +TFTP_LIBS="$LIBS $XTRALIBS" +LIBS="$common_libs" + +dnl +dnl Check for IPV6 and disable-ipv6 +dnl + +AC_CHECK_MEMBERS(struct sockaddr_in6.sin6_addr) +AC_MSG_CHECKING([for IPv6 support]) +PA_ARG_DISABLED([ipv6], + [disable support for IPv6], + [AC_MSG_RESULT(disabled)], + [AS_IF([test x"$ac_cv_member_struct_sockaddr_in6_sin6_addr$ac_cv_member_struct_addrinfo_ai_addr" = xyesyes], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IPV6, 1, [define if IPv6 support is enabled])], + [AC_MSG_RESULT(no) + AC_MSG_WARN([*** we do not have required IPv6 structs - IPv6 will be disabled])])]) + +AC_SUBST(SRCROOT) +AC_SUBST(OBJROOT) + +AC_SUBST(TFTP_LIBS) +AC_SUBST(TFTPD_LIBS) +AC_SUBST(TFTPDOBJS) + +AC_PROG_LN_S +AC_PROG_RANLIB + +dnl +dnl Make sure the install program has an absolute path if it +dnl has a path at all. autoconf doesn't do this "in order +dnl to not pollute the cache." Sigh. +dnl Note: the $ needs to be double-quoted for reasons unknown. +dnl +AC_PROG_INSTALL +[if echo "$INSTALL" | grep '^[^/].*/' > /dev/null 2>&1; then + INSTALL='\${SRCROOT}'/"$INSTALL" +fi] + +PA_ADD_CFLAGS(-W) +PA_ADD_CFLAGS(-Wall) +PA_ADD_CFLAGS(-Wpointer-arith) +PA_ADD_CFLAGS(-Wbad-function-cast) +PA_ADD_CFLAGS(-Wcast-equal) +PA_ADD_CFLAGS(-Wstrict-prototypes) +PA_ADD_CFLAGS(-Wmissing-prototypes) +PA_ADD_CFLAGS(-Wmissing-declarations) +PA_ADD_CFLAGS(-Wnested-externs) +PA_ADD_CFLAGS(-Winline) +PA_ADD_CFLAGS(-Wwrite-strings) +PA_ADD_CFLAGS(-Wundef) +PA_ADD_CFLAGS(-Wshadow) +PA_ADD_CFLAGS(-Wsign-compare) +PA_ADD_CFLAGS(-fno-strict-aliasing) + +dnl +dnl Test compiler features. On some compilers, this can be affected +dnl by -Werror options, so run this *after* those options are added. +dnl +PA_CHECK_BAD_STDC_INLINE +PA_C_TYPEOF + +AC_CONFIG_HEADERS([config/config.h]) +AC_CONFIG_FILES([config/MCONFIG]) +AC_OUTPUT diff --git a/configure.in b/configure.in deleted file mode 100644 index 7ab7c5a..0000000 --- a/configure.in +++ /dev/null @@ -1,303 +0,0 @@ -dnl -dnl autoconf input file to generate MCONFIG -dnl - -AC_PREREQ(2.61) -AC_INIT(MCONFIG.in) -AC_PREFIX_DEFAULT(/usr) - -AC_USE_SYSTEM_EXTENSIONS -AC_ISC_POSIX -AC_PROG_CC - -AC_C_CONST -AC_C_INLINE - -PA_ADD_CFLAGS(-W) -PA_ADD_CFLAGS(-Wall) -PA_ADD_CFLAGS(-Wpointer-arith) -PA_ADD_CFLAGS(-Wbad-function-cast) -PA_ADD_CFLAGS(-Wcast-equal) -PA_ADD_CFLAGS(-Wstrict-prototypes) -PA_ADD_CFLAGS(-Wmissing-prototypes) -PA_ADD_CFLAGS(-Wmissing-declarations) -PA_ADD_CFLAGS(-Wnested-externs) -PA_ADD_CFLAGS(-Winline) -PA_ADD_CFLAGS(-Wwrite-strings) -PA_ADD_CFLAGS(-Wundef) -PA_ADD_CFLAGS(-Wshadow) -PA_ADD_CFLAGS(-Wsign-compare) -PA_ADD_CFLAGS(-pipe) -PA_ADD_CFLAGS(-fno-strict-aliasing) - -AC_HEADER_STDC -AC_CHECK_HEADERS(inttypes.h) -AC_CHECK_HEADERS(stdint.h) -PA_CHECK_INTTYPES_H_SANE -AC_CHECK_HEADERS(fcntl.h) -AC_CHECK_HEADERS(grp.h) -AC_CHECK_HEADERS(libgen.h) -AC_CHECK_HEADERS(memory.h) -AC_CHECK_HEADERS(setjmp.h) -AC_CHECK_HEADERS(stddef.h) -AC_CHECK_HEADERS(stdlib.h) -AC_CHECK_HEADERS(string.h) -AC_CHECK_HEADERS(strings.h) -AC_CHECK_HEADERS(sysexits.h) -AC_CHECK_HEADERS(time.h) -AC_CHECK_HEADERS(unistd.h) -AC_CHECK_HEADERS(sys/file.h) -AC_CHECK_HEADERS(sys/filio.h) -AC_CHECK_HEADERS(sys/stat.h) -AC_CHECK_HEADERS(sys/time.h) -AC_CHECK_HEADERS(sys/types.h) -AC_CHECK_HEADERS(arpa/inet.h) -AC_CHECK_HEADERS(netdb.h) -AC_HEADER_TIME -dnl This is needed on some versions of FreeBSD... -AC_CHECK_HEADERS(machine/param.h) -AC_CHECK_HEADERS(sys/socket.h) -AC_CHECK_HEADERS(winsock2.h) -AC_CHECK_HEADERS(winsock.h) - -AC_SYS_LARGEFILE - -AC_TYPE_OFF_T -AC_TYPE_PID_T -AC_TYPE_MODE_T -AC_TYPE_SIZE_T -AC_CHECK_TYPES(intmax_t) -AC_CHECK_TYPES(long long) -AC_CHECK_TYPES(uint16_t) -AC_CHECK_TYPES(uint32_t) -AC_CHECK_TYPES(u_short) -AC_CHECK_TYPES(u_long) - -dnl -dnl isn't among the list of standard headers that autoconf checks, -dnl but POSIX requires for socklen_t to be defined. -dnl -AC_CHECK_TYPES(socklen_t,,, -[ -#include -#if HAVE_SYS_TYPES_H -# include -#endif -#if HAVE_SYS_STAT_H -# include -#endif -#if STDC_HEADERS -# include -# include -#else -# if HAVE_STDLIB_H -# include -# endif -#endif -#if HAVE_STRING_H -# if !STDC_HEADERS && HAVE_MEMORY_H -# include -# endif -# include -#endif -#if HAVE_STRINGS_H -# include -#endif -#if HAVE_INTTYPES_H -# include -#else -# if HAVE_STDINT_H -# include -# endif -#endif -#if HAVE_UNISTD_H -# include -#endif -#ifdef HAVE_SYS_SOCKET_H -# include -#endif -]) - -AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , [AC_MSG_ERROR(socket library not found)]) - -AC_CHECK_FUNCS(fcntl) -AC_CHECK_FUNCS(setsid) -AC_CHECK_FUNCS(recvmsg) -AC_CHECK_FUNCS(ftruncate) -AC_CHECK_FUNCS(setreuid) -AC_CHECK_FUNCS(setregid) -AC_CHECK_FUNCS(initgroups) -AC_CHECK_FUNCS(setgroups) - -dnl Solaris 8 has [u]intmax_t but not strtoumax(). How utterly braindamaged. -AC_CHECK_FUNCS(strtoumax) -AC_CHECK_FUNCS(strtoull) - -PA_MSGHDR_MSG_CONTROL -PA_STRUCT_IN_PKTINFO -PA_STRUCT_ADDRINFO - -PA_HEADER_DEFINES(fcntl.h, int, O_NONBLOCK) -PA_HEADER_DEFINES(fcntl.h, int, O_BINARY) -PA_HEADER_DEFINES(fcntl.h, int, O_TEXT) - -PA_HEADER_DEFINES(fcntl.h, int, F_SETLK) - -PA_HEADER_DEFINES(sys/file.h, int, LOCK_SH) -PA_HEADER_DEFINES(sys/file.h, int, LOCK_EX) - -AH_TEMPLATE([HAVE_SIGSETJMP], -[Define if we have sigsetjmp, siglongjmp and sigjmp_buf.]) -PA_SIGSETJMP([AC_DEFINE(HAVE_SIGSETJMP)]) - -dnl -dnl Get common paths -dnl -SRCROOT=`cd $srcdir && pwd` -OBJROOT=`pwd` - -XTRA=false -PA_SEARCH_LIBS_AND_ADD(xmalloc, iberty) -PA_SEARCH_LIBS_AND_ADD(xstrdup, iberty) -PA_SEARCH_LIBS_AND_ADD(bsd_signal, bsd, bsdsignal) -PA_SEARCH_LIBS_AND_ADD(getopt_long, getopt, getopt_long) -PA_SEARCH_LIBS_AND_ADD(getaddrinfo, [nsl resolv]) -if $pa_add_getaddrinfo -then - AC_SEARCH_LIBS(gethostbyname, [nsl resolv], - [AC_SEARCH_LIBS(herror, [nsl resolv], , - [AC_MSG_ERROR(herror not found)])], - [AC_MSG_ERROR(gethostbyname not found)]) -else - AC_SEARCH_LIBS(freeaddrinfo, [nsl resolv], , - [AC_MSG_ERROR(getaddrinfo but not freeaddrinfo found)]) - AC_SEARCH_LIBS(gai_strerror, [nsl resolv], , - [AC_MSG_ERROR(getaddrinfo but not gai_strerror found)]) -fi - -PA_SEARCH_LIBS_AND_ADD(inet_ntop, [nsl resolv]) -if $pa_add_inet_ntop -then - AC_SEARCH_LIBS(inet_ntoa, [nsl resolv], , - [AC_MSG_ERROR(inet_ntoa not found)]) -fi -AC_SEARCH_LIBS(inet_aton, [nsl resolv], ,[AC_MSG_ERROR(inet_aton not found)]) - -AC_CHECK_FUNCS(daemon, , [XTRA=true; AC_LIBOBJ(daemon)]) -AC_CHECK_FUNCS(dup2, , [XTRA=true; AC_LIBOBJ(dup2)]) -if $XTRA -then - XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS" -fi - -dnl -dnl These libraries apply to the server only -dnl - -common_libs="$LIBS" - -PA_HEADER_DEFINES(netinet/in.h, int, IPPORT_TFTP) - -PA_WITH_BOOL(tcpwrappers, 1, -[ --without-tcpwrappers disable tcpwrapper permissions checking], -[ - AC_SEARCH_LIBS(yp_get_default_domain, [nsl resolv]) - PA_HAVE_TCPWRAPPERS -],:) - - -AH_TEMPLATE([WITH_REGEX], -[Define if we are compiling with regex filename remapping.]) - -PA_WITH_BOOL(remap, 1, -[ --without-remap disable regex-based filename remapping], -[ - AC_CHECK_HEADER(regex.h, - [ - AC_SEARCH_LIBS(regcomp, [regex rx], - [ - AC_DEFINE(WITH_REGEX) - TFTPDOBJS="remap.${OBJEXT} $TFTPDOBJS" - ]) - ]) -],:) - -TFTPD_LIBS="$LIBS $XTRALIBS" -LIBS="$common_libs" - -dnl -dnl These libraries apply to the client only -dnl - -AH_TEMPLATE([WITH_READLINE], -[Define if we are compiling with readline/editline command-line editing.]) - -PA_WITH_BOOL(readline, 1, -[ --without-readline disable the use of readline command-line editing], -[ - AC_CHECK_HEADER(readline/readline.h, - [ - dnl readline may need libtermcap or somesuch... - AC_SEARCH_LIBS(tputs, [termcap terminfo]) - - AC_SEARCH_LIBS(readline, [readline history], - [AC_DEFINE(WITH_READLINE)]) - AC_CHECK_HEADERS(readline/history.h) - ], - [AC_CHECK_HEADER(editline/readline.h, - [ - dnl editline may need libtermcap or somesuch... - AC_SEARCH_LIBS(tputs, [termcap terminfo]) - - AC_SEARCH_LIBS(editline, [edit], - [AC_DEFINE(WITH_READLINE)]) - ])]) -],:) - -TFTP_LIBS="$LIBS $XTRALIBS" -LIBS="$common_libs" - -dnl -dnl Check for IPV6 and disable-ipv6 -dnl -PA_STRUCT_SOCKADDR_IN6 -AC_MSG_CHECKING([for IPv6 support]) -PA_WITH_BOOL(ipv6, 1, -[ --without-ipv6 disable the support for IPv6], -[ - if $HAVE_INET6 - then - AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_IPV6, 1, [Define if IPv6 support is enabled.]) - PA_STRUCT_IN6_PKTINFO - else - AC_MSG_RESULT(no) - AC_MSG_WARN([*** we do not have required IPv6 structs - IPv6 will be disabled]) - fi -], -[AC_MSG_RESULT(disabled)]) - - -AC_SUBST(SRCROOT) -AC_SUBST(OBJROOT) - -AC_SUBST(TFTP_LIBS) -AC_SUBST(TFTPD_LIBS) -AC_SUBST(TFTPDOBJS) - -AC_PROG_LN_S -AC_PROG_RANLIB - -dnl -dnl Make sure the install program has an absolute path if it -dnl has a path at all. autoconf doesn't do this "in order -dnl to not pollute the cache." Sigh. -dnl Note: the $ needs to be double-quoted for reasons unknown. -dnl -AC_PROG_INSTALL -[if echo "$INSTALL" | grep '^[^/].*/' > /dev/null 2>&1; then - INSTALL='\${SRCROOT}'/"$INSTALL" -fi] - -AC_CONFIG_HEADERS(aconfig.h) -AC_OUTPUT(MCONFIG) diff --git a/lib/Makefile b/lib/Makefile index a43ce19..a7fd057 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -4,7 +4,7 @@ SRCROOT = .. --include ../MCONFIG +-include ../config/MCONFIG include ../MRULES ifeq ($(LIBOBJS),) @@ -25,5 +25,3 @@ libxtra.a: $(LIBOBJS) -rm -f libxtra.a $(AR) libxtra.a $(LIBOBJS) $(RANLIB) libxtra.a - - diff --git a/lib/bsdsignal.c b/lib/bsdsignal.c deleted file mode 100644 index 0aae136..0000000 --- a/lib/bsdsignal.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * bsdsignal.c - * - * Use sigaction() to simulate BSD signal() - */ - -#include "config.h" - -void (*bsd_signal(int signum, void (*handler) (int))) (int) { - struct sigaction action, oldaction; - - memset(&action, 0, sizeof action); - action.sa_handler = handler; - sigemptyset(&action.sa_mask); - sigaddset(&action.sa_mask, signum); - action.sa_flags = SA_RESTART; - - if (sigaction(signum, &action, &oldaction) == -1) { -#ifdef SIG_ERR - return SIG_ERR; -#else - return NULL; -#endif - } - - return oldaction.sa_handler; -} diff --git a/tftp/Makefile b/tftp/Makefile index 20f4c18..9faa62f 100644 --- a/tftp/Makefile +++ b/tftp/Makefile @@ -1,7 +1,7 @@ SRCROOT = .. VERSION = $(shell cat ../version) --include ../MCONFIG +-include ../config/MCONFIG include ../MRULES OBJS = tftp.$(O) main.$(O) diff --git a/tftp/extern.h b/tftp/extern.h index 78474fc..9c578e6 100644 --- a/tftp/extern.h +++ b/tftp/extern.h @@ -31,10 +31,13 @@ * SUCH DAMAGE. */ -#ifndef RECVFILE_H -#define RECVFILE_H +#ifndef EXTERN_H +#define EXTERN_H + +#include "config.h" void tftp_recvfile(int, const char *, const char *); void tftp_sendfile(int, const char *, const char *); +extern sigjmp_buf toplevel; #endif diff --git a/tftp/main.c b/tftp/main.c index 1b8a881..ac06330 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -188,7 +188,7 @@ char *xstrdup(const char *); const char *program; -static inline void usage(int errcode) +static void usage(int errcode) { fprintf(stderr, #ifdef HAVE_IPV6 @@ -305,7 +305,7 @@ int main(int argc, char *argv[]) sp->s_proto = (char *)"udp"; } - bsd_signal(SIGINT, intr); + tftp_signal(SIGINT, intr, 0); if (peerargc) { /* Set peer */ @@ -768,8 +768,8 @@ void intr(int sig) { (void)sig; /* Quiet unused warning */ - bsd_signal(SIGALRM, SIG_IGN); alarm(0); + tftp_signal(SIGALRM, SIG_DFL, 0); siglongjmp(toplevel, -1); } diff --git a/tftp/tftp.c b/tftp/tftp.c index d15da22..33a4175 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -48,8 +48,7 @@ extern int maxtimeout; #define PKTSIZE SEGSIZE+4 char ackbuf[PKTSIZE]; int timeout; -sigjmp_buf toplevel; -sigjmp_buf timeoutbuf; +static sigjmp_buf timeoutbuf; static void nak(int, const char *); static int makerequest(int, const char *, struct tftphdr *, const char *); @@ -85,7 +84,7 @@ void tftp_sendfile(int fd, const char *name, const char *mode) is_request = 1; /* First packet is the actual WRQ */ amount = 0; - bsd_signal(SIGALRM, timer); + tftp_signal(SIGALRM, timer, 0); do { if (is_request) { size = makerequest(WRQ, name, dp, mode) - 4; @@ -191,7 +190,7 @@ void tftp_recvfile(int fd, const char *name, const char *mode) firsttrip = 1; amount = 0; - bsd_signal(SIGALRM, timer); + tftp_signal(SIGALRM, timer, 0); do { if (firsttrip) { size = makerequest(RRQ, name, ap, mode); diff --git a/tftpd/Makefile b/tftpd/Makefile index a05335d..5963581 100644 --- a/tftpd/Makefile +++ b/tftpd/Makefile @@ -1,7 +1,7 @@ SRCROOT = .. VERSION = $(shell cat ../version) --include ../MCONFIG +-include ../config/MCONFIG include ../MRULES OBJS = tftpd.$(O) recvfrom.$(O) misc.$(O) $(TFTPDOBJS) diff --git a/tftpd/misc.c b/tftpd/misc.c index 07684dd..9070f96 100644 --- a/tftpd/misc.c +++ b/tftpd/misc.c @@ -19,19 +19,11 @@ #include "tftpd.h" /* - * Set the signal handler and flags. Basically a user-friendly - * wrapper around sigaction(). + * Set the signal handler and flags, and error out on failure. */ -void set_signal(int signum, void (*handler) (int), int flags) +void set_signal(int signum, sighandler_t handler, int flags) { - struct sigaction sa; - - memset(&sa, 0, sizeof sa); - sa.sa_handler = handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = flags; - - if (sigaction(signum, &sa, NULL)) { + if (tftp_signal(signum, handler, flags)) { syslog(LOG_ERR, "sigaction: %m"); exit(EX_OSERR); } diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 3ee5642..320678d 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -26,10 +26,12 @@ #if defined(HAVE_RECVMSG) && defined(HAVE_MSGHDR_MSG_CONTROL) -#include +#ifdef HAVE_SYS_UIO_H +# include +#endif #ifdef IP_PKTINFO -# ifndef HAVE_STRUCT_IN_PKTINFO +# ifndef HAVE_STRUCT_IN_PKTINFO_IPI_ADDR # ifdef __linux__ /* Assume this version of glibc simply lacks the definition */ struct in_pktinfo { @@ -51,52 +53,59 @@ struct in_pktinfo { #endif /* - * Check to see if this is a valid local address. If so, we should - * end up having the same local and remote address when trying to - * bind to it. + * Check to see if this is a valid local address, meaning that we can + * legally bind to it. */ static int address_is_local(const union sock_addr *addr) { - union sock_addr sa; + union sock_addr sa1, sa2; int sockfd = -1; int e; int rv = 0; socklen_t addrlen; + memcpy(&sa1, addr, sizeof sa1); + /* Multicast or universal broadcast address? */ - if (addr->sa.sa_family == AF_INET) { - if (ntohl(addr->si.sin_addr.s_addr) >= (224UL << 24)) + if (sa1.sa.sa_family == AF_INET) { + if (ntohl(sa1.si.sin_addr.s_addr) >= (224UL << 24)) return 0; + sa1.si.sin_port = 0; /* Any port */ } #ifdef HAVE_IPV6 - else if (addr->sa.sa_family == AF_INET6) { - if (IN6_IS_ADDR_MULTICAST(&addr->s6.sin6_addr)) + else if (sa1.sa.sa_family == AF_INET6) { + if (IN6_IS_ADDR_MULTICAST(&sa1.s6.sin6_addr)) return 0; + sa1.s6.sin6_port = 0; /* Any port */ } #endif else return 0; - sockfd = socket(addr->sa.sa_family, SOCK_DGRAM, 0); + sockfd = socket(sa1.sa.sa_family, SOCK_DGRAM, 0); if (sockfd < 0) goto err; - if (connect(sockfd, &addr->sa, SOCKLEN(addr))) + if (bind(sockfd, &sa1.sa, SOCKLEN(&sa1))) goto err; addrlen = SOCKLEN(addr); - if (getsockname(sockfd, (struct sockaddr *)&sa, &addrlen)) + if (getsockname(sockfd, (struct sockaddr *)&sa2, &addrlen)) goto err; - if (addr->sa.sa_family == AF_INET) - rv = sa.si.sin_addr.s_addr == addr->si.sin_addr.s_addr; + if (sa1.sa.sa_family != sa2.sa.sa_family) + goto err; + + if (sa2.sa.sa_family == AF_INET) + rv = sa1.si.sin_addr.s_addr == sa2.si.sin_addr.s_addr; #ifdef HAVE_IPV6 - else if (addr->sa.sa_family == AF_INET6) - rv = IN6_ARE_ADDR_EQUAL(&sa.s6.sin6_addr, &addr->s6.sin6_addr); + else if (sa2.sa.sa_family == AF_INET6) + rv = IN6_ARE_ADDR_EQUAL(&sa1.s6.sin6_addr, &sa2.s6.sin6_addr); #endif else rv = 0; - err: + +err: e = errno; if (sockfd >= 0) @@ -106,10 +115,31 @@ static int address_is_local(const union sock_addr *addr) return rv; } +static void normalize_ip6_compat(union sock_addr *myaddr) +{ +#ifdef HAVE_IPV6 + static const uint8_t ip6_compat_prefix[12] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + struct sockaddr_in in; + + if (myaddr->sa.sa_family == AF_INET6 && + !memcmp(&myaddr->s6.sin6_addr, ip6_compat_prefix, + sizeof ip6_compat_prefix)) { + bzero(&in, sizeof in); + in.sin_family = AF_INET; + in.sin_port = myaddr->s6.sin6_port; + memcpy(&in.sin_addr, (const char *)&myaddr->s6.sin6_addr + + sizeof ip6_compat_prefix, sizeof in.sin_addr); + memcpy(&myaddr->si, &in, sizeof in); + } +#else + (void)myaddr; +#endif +} + int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t * fromlen, - union sock_addr *myaddr) + union sock_addr *from, union sock_addr *myaddr) { struct msghdr msg; struct iovec iov; @@ -142,16 +172,16 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, /* Try to enable getting the return address */ #ifdef IP_RECVDSTADDR - if (from->sa_family == AF_INET) + if (from->sa.sa_family == AF_INET) setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); #endif #ifdef IP_PKTINFO - if (from->sa_family == AF_INET) + if (from->sa.sa_family == AF_INET) setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); #endif #ifdef HAVE_IPV6 #ifdef IPV6_RECVPKTINFO - if (from->sa_family == AF_INET6) + if (from->sa.sa_family == AF_INET6) setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); #endif #endif @@ -160,8 +190,8 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, msg.msg_controllen = sizeof(control_un); msg.msg_flags = 0; - msg.msg_name = from; - msg.msg_namelen = *fromlen; + msg.msg_name = &from->sa; + msg.msg_namelen = sizeof(*from); iov.iov_base = buf; iov.iov_len = len; msg.msg_iov = &iov; @@ -170,11 +200,9 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, if ((n = recvmsg(s, &msg, flags)) < 0) return n; /* Error */ - *fromlen = msg.msg_namelen; - if (myaddr) { bzero(myaddr, sizeof(*myaddr)); - myaddr->sa.sa_family = from->sa_family; + myaddr->sa.sa_family = from->sa.sa_family; if (msg.msg_controllen < sizeof(struct cmsghdr) || (msg.msg_flags & MSG_CTRUNC)) @@ -183,7 +211,7 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; cmptr = CMSG_NXTHDR(&msg, cmptr)) { - if (from->sa_family == AF_INET) { + if (from->sa.sa_family == AF_INET) { myaddr->sa.sa_family = AF_INET; #ifdef IP_RECVDSTADDR if (cmptr->cmsg_level == IPPROTO_IP && @@ -204,7 +232,7 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, #endif } #ifdef HAVE_IPV6 - else if (from->sa_family == AF_INET6) { + else if (from->sa.sa_family == AF_INET6) { myaddr->sa.sa_family = AF_INET6; #ifdef IP6_RECVDSTADDR if (cmptr->cmsg_level == IPPROTO_IPV6 && @@ -215,17 +243,23 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, #ifdef HAVE_STRUCT_IN6_PKTINFO if (cmptr->cmsg_level == IPPROTO_IPV6 && - (cmptr->cmsg_type == IPV6_RECVPKTINFO || + ( +#ifdef IPV6_RECVPKTINFO + cmptr->cmsg_type == IPV6_RECVPKTINFO || +#endif cmptr->cmsg_type == IPV6_PKTINFO)) { - memcpy(&pktinfo6, CMSG_DATA(cmptr), - sizeof(struct in6_pktinfo)); - memcpy(&myaddr->s6.sin6_addr, &pktinfo6.ipi6_addr, - sizeof(struct in6_addr)); + memcpy(&pktinfo6, CMSG_DATA(cmptr), + sizeof(struct in6_pktinfo)); + memcpy(&myaddr->s6.sin6_addr, &pktinfo6.ipi6_addr, + sizeof(struct in6_addr)); } #endif } #endif } + + normalize_ip6_compat(myaddr); + /* If the address is not a valid local address, * then bind to any address... */ @@ -238,6 +272,9 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, #endif } } + + normalize_ip6_compat(from); + return n; } @@ -245,16 +282,16 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t * fromlen, - union sock_addr *myaddr) + union sock_addr *from, union sock_addr *myaddr) { /* There is no way we can get the local address, fudge it */ + socklen_t fromlen = sizeof(*from); bzero(myaddr, sizeof(*myaddr)); - myaddr->sa.sa_family = from->sa_family; + myaddr->sa.sa_family = from->sa.sa_family; sa_set_port(myaddr, htons(IPPORT_TFTP)); - return recvfrom(s, buf, len, flags, from, fromlen); + return recvfrom(s, buf, len, flags, &from->sa, &fromlen); } #endif diff --git a/tftpd/recvfrom.h b/tftpd/recvfrom.h index e3c4055..7773a0d 100644 --- a/tftpd/recvfrom.h +++ b/tftpd/recvfrom.h @@ -19,5 +19,4 @@ int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t *fromlen, - union sock_addr *myaddr); + union sock_addr *from, union sock_addr *myaddr); diff --git a/tftpd/remap.c b/tftpd/remap.c index 1e7abe7..413d117 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- * * - * Copyright 2001-2007 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2024 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -22,7 +22,7 @@ #include "tftpd.h" #include "remap.h" -#define DEADMAN_MAX_STEPS 1024 /* Timeout after this many steps */ +#define DEADMAN_MAX_STEPS 4096 /* Timeout after this many steps */ #define MAXLINE 16384 /* Truncate a line at this many bytes */ #define RULE_REWRITE 0x01 /* This is a rewrite rule */ @@ -31,12 +31,20 @@ #define RULE_RESTART 0x08 /* Restart at the top after matching this rule */ #define RULE_ABORT 0x10 /* Terminate processing with an error */ #define RULE_INVERSE 0x20 /* Execute if regex *doesn't* match */ +#define RULE_IPV4 0x40 /* IPv4 only */ +#define RULE_IPV6 0x80 /* IPv6 only */ + +#define RULE_HASFILE 0x100 /* Valid if rule results in a valid filename */ +#define RULE_RRQ 0x200 /* Get (read) only */ +#define RULE_WRQ 0x400 /* Put (write) only */ +#define RULE_SEDG 0x800 /* sed-style global */ + +int deadman_max_steps = DEADMAN_MAX_STEPS; struct rule { struct rule *next; int nrule; - int rule_flags; - char rule_mode; + unsigned int rule_flags; regex_t rx; const char *pattern; }; @@ -56,22 +64,30 @@ static int xform_tolower(int c) return tolower(c); } -/* Do \-substitution. Call with string == NULL to get length only. */ -static int genmatchstring(char *string, const char *pattern, - const char *input, const regmatch_t * pmatch, - match_pattern_callback macrosub) +/* + * Do \-substitution. Call with string == NULL to get length only. + * "start" indicates an offset into the input buffer where the pattern + * match was started. + */ +static int do_genmatchstring(char *string, const char *pattern, + const char *ibuf, + const regmatch_t * pmatch, + match_pattern_callback macrosub, + int start, int *nextp) { int (*xform) (int) = xform_null; int len = 0; int n, mlen, sublen; int endbytes; + const char *input = ibuf + start; /* Get section before match; note pmatch[0] is the whole match */ endbytes = strlen(input) - pmatch[0].rm_eo; - len = pmatch[0].rm_so + endbytes; + len = start + pmatch[0].rm_so; if (string) { - memcpy(string, input, pmatch[0].rm_so); - string += pmatch[0].rm_so; + /* Copy the prefix before "start" as well! */ + memcpy(string, ibuf, start + pmatch[0].rm_so); + string += start + pmatch[0].rm_so; } /* Transform matched section */ @@ -97,7 +113,7 @@ static int genmatchstring(char *string, const char *pattern, mlen = pmatch[n].rm_eo - pmatch[n].rm_so; len += mlen; if (string) { - const char *p = input + pmatch[n].rm_so; + const char *p = input + start + pmatch[n].rm_so; while (mlen--) *string++ = xform(*p++); } @@ -140,7 +156,12 @@ static int genmatchstring(char *string, const char *pattern, } } + /* Pointer to post-substitution tail */ + if (nextp) + *nextp = len; + /* Copy section after match */ + len += endbytes; if (string) { memcpy(string, input + pmatch[0].rm_eo, endbytes); string[endbytes] = '\0'; @@ -149,6 +170,26 @@ static int genmatchstring(char *string, const char *pattern, return len; } +/* + * Ditto, but allocate the string in a new buffer + */ + +static int genmatchstring(char **string, const char *pattern, + const char *ibuf, + const regmatch_t * pmatch, + match_pattern_callback macrosub, + int start, int *nextp) +{ + int len; + char *buf; + + len = do_genmatchstring(NULL, pattern, ibuf, pmatch, + macrosub, start, NULL); + *string = buf = tfmalloc(len + 1); + return do_genmatchstring(buf, pattern, ibuf, pmatch, + macrosub, start, nextp); +} + /* * Extract a string terminated by non-escaped whitespace; ignoring * leading whitespace. Consider an unescaped # to be a comment marker, @@ -206,11 +247,17 @@ static int parseline(char *line, struct rule *r, int lineno) r->rule_flags |= RULE_REWRITE; break; case 'g': - r->rule_flags |= RULE_GLOBAL; + if (r->rule_flags & RULE_GLOBAL) + r->rule_flags |= RULE_SEDG; + else + r->rule_flags |= RULE_GLOBAL; break; case 'e': r->rule_flags |= RULE_EXIT; break; + case 'E': + r->rule_flags |= RULE_HASFILE; + break; case 's': r->rule_flags |= RULE_RESTART; break; @@ -223,9 +270,17 @@ static int parseline(char *line, struct rule *r, int lineno) case '~': r->rule_flags |= RULE_INVERSE; break; - case 'G': - case 'P': - r->rule_mode = *p; + case '4': + r->rule_flags |= RULE_IPV4; + break; + case '6': + r->rule_flags |= RULE_IPV6; + break; + case 'G': + r->rule_flags |= RULE_RRQ; + break; + case 'P': + r->rule_flags |= RULE_WRQ; break; default: syslog(LOG_ERR, @@ -236,15 +291,21 @@ static int parseline(char *line, struct rule *r, int lineno) } } - /* RULE_GLOBAL only applies when RULE_REWRITE specified */ - if (!(r->rule_flags & RULE_REWRITE)) - r->rule_flags &= ~RULE_GLOBAL; - - if ((r->rule_flags & (RULE_INVERSE | RULE_REWRITE)) == - (RULE_INVERSE | RULE_REWRITE)) { - syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", - lineno, line); - return -1; /* Error */ + if (r->rule_flags & RULE_REWRITE) { + if (r->rule_flags & RULE_INVERSE) { + syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", + lineno, line); + return -1; /* Error */ + } + if ((r->rule_flags & (RULE_GLOBAL|RULE_SEDG|RULE_HASFILE)) + == (RULE_GLOBAL|RULE_HASFILE)) { + syslog(LOG_ERR, "E rules cannot be combined with g (but gg is OK), line %d: %s\n", + lineno, line); + return -1; /* Error */ + } + } else { + /* RULE_GLOBAL and RULE_SEDG are meaningless without RULE_REWRITE */ + r->rule_flags &= ~(RULE_GLOBAL|RULE_SEDG); } /* Read and compile the regex */ @@ -325,17 +386,24 @@ void freerules(struct rule *r) } /* Execute a rule set on a string; returns a malloc'd new string. */ -char *rewrite_string(const char *input, const struct rule *rules, - char mode, match_pattern_callback macrosub, +char *rewrite_string(const struct formats *pf, + const char *input, const struct rule *rules, + int mode, int af, match_pattern_callback macrosub, const char **errmsg) { char *current = tfstrdup(input); - char *newstr; + char *newstr, *newerstr; + const char *accerr; const struct rule *ruleptr = rules; regmatch_t pmatch[10]; + int i; int len; int was_match = 0; - int deadman = DEADMAN_MAX_STEPS; + int deadman = deadman_max_steps; + int matchsense; + int pmatches; + unsigned int bad_flags; + int ggoffset; /* Default error */ *errmsg = "Remap table failure"; @@ -344,86 +412,121 @@ char *rewrite_string(const char *input, const struct rule *rules, syslog(LOG_INFO, "remap: input: %s", current); } - for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) { - if (ruleptr->rule_mode && ruleptr->rule_mode != mode) - continue; /* Rule not applicable, try next */ + bad_flags = 0; + if (mode != RRQ) bad_flags |= RULE_RRQ; + if (mode != WRQ) bad_flags |= RULE_WRQ; + if (af != AF_INET) bad_flags |= RULE_IPV4; + if (af != AF_INET6) bad_flags |= RULE_IPV6; - if (!deadman--) { - syslog(LOG_WARNING, - "remap: Breaking loop, input = %s, last = %s", input, - current); - free(current); - return NULL; /* Did not terminate! */ - } + for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) { + if (ruleptr->rule_flags & bad_flags) + continue; /* This rule is excluded by flags */ + + matchsense = ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0; + pmatches = ruleptr->rule_flags & RULE_INVERSE ? 0 : 10; + + /* Clear the pmatch[] array */ + for (i = 0; i < 10; i++) + pmatch[i].rm_so = pmatch[i].rm_eo = -1; + + was_match = 0; do { - if (regexec(&ruleptr->rx, current, 10, pmatch, 0) == - (ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0)) { - /* Match on this rule */ - was_match = 1; + if (!deadman--) + goto dead; - if (ruleptr->rule_flags & RULE_INVERSE) { - /* No actual match, so clear out the pmatch array */ - int i; - for (i = 0; i < 10; i++) - pmatch[i].rm_so = pmatch[i].rm_eo = -1; - } + if (regexec(&ruleptr->rx, current, pmatches, pmatch, 0) + != matchsense) + break; /* No match, break out of do loop */ - if (ruleptr->rule_flags & RULE_ABORT) { - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: abort: %s", - ruleptr->nrule, current); - } - if (ruleptr->pattern[0]) { - /* Custom error message */ - len = - genmatchstring(NULL, ruleptr->pattern, current, - pmatch, macrosub); - newstr = tfmalloc(len + 1); - genmatchstring(newstr, ruleptr->pattern, current, - pmatch, macrosub); - *errmsg = newstr; - } else { - *errmsg = NULL; - } - free(current); - return (NULL); - } + /* Match on this rule */ + was_match = 1; - if (ruleptr->rule_flags & RULE_REWRITE) { - len = genmatchstring(NULL, ruleptr->pattern, current, - pmatch, macrosub); - newstr = tfmalloc(len + 1); - genmatchstring(newstr, ruleptr->pattern, current, - pmatch, macrosub); - free(current); - current = newstr; - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: rewrite: %s", - ruleptr->nrule, current); - } + if (ruleptr->rule_flags & RULE_ABORT) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: abort: %s", + ruleptr->nrule, current); } - } else { - break; /* No match, terminate unconditionally */ + if (ruleptr->pattern[0]) { + /* Custom error message */ + genmatchstring(&newstr, ruleptr->pattern, current, + pmatch, macrosub, 0, NULL); + *errmsg = newstr; + } else { + *errmsg = NULL; + } + free(current); + return (NULL); } - /* If the rule is global, keep going until no match */ - } while (ruleptr->rule_flags & RULE_GLOBAL); - if (was_match) { - was_match = 0; + if (ruleptr->rule_flags & RULE_REWRITE) { + len = genmatchstring(&newstr, ruleptr->pattern, current, + pmatch, macrosub, 0, &ggoffset); - if (ruleptr->rule_flags & RULE_EXIT) { - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: exit", - ruleptr->nrule); + if (ruleptr->rule_flags & RULE_SEDG) { + /* sed-style partial-matching global */ + while (ggoffset < len && + regexec(&ruleptr->rx, newstr + ggoffset, + pmatches, pmatch, + ggoffset ? REG_NOTBOL : 0) + == matchsense) { + if (!deadman--) { + free(current); + current = newstr; + goto dead; + } + len = genmatchstring(&newerstr, ruleptr->pattern, + newstr, pmatch, macrosub, + ggoffset, &ggoffset); + free(newstr); + newstr = newerstr; + } } - return current; /* Exit here, we're done */ - } else if (ruleptr->rule_flags & RULE_RESTART) { - ruleptr = rules; /* Start from the top */ - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: restart", - ruleptr->nrule); + + if ((ruleptr->rule_flags & RULE_HASFILE) && + pf->f_validate(newstr, mode, pf, &accerr)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", + ruleptr->nrule, accerr, newstr); + } + free(newstr); + was_match = 0; + break; } + + free(current); + current = newstr; + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: rewrite: %s", + ruleptr->nrule, current); + } + } else if (ruleptr->rule_flags & RULE_HASFILE) { + if (pf->f_validate(current, mode, pf, &accerr)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n", + ruleptr->nrule, accerr); + } + was_match = 0; + break; + } + } + /* If the rule is (old-style) global, keep going until no match */ + } while ((ruleptr->rule_flags & (RULE_GLOBAL|RULE_SEDG)) == RULE_GLOBAL); + + if (!was_match) + continue; /* Next rule */ + + if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: exit", + ruleptr->nrule); + } + return current; /* Exit here, we're done */ + } else if (ruleptr->rule_flags & RULE_RESTART) { + ruleptr = rules; /* Start from the top */ + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: restart", + ruleptr->nrule); } } } @@ -432,4 +535,11 @@ char *rewrite_string(const char *input, const struct rule *rules, syslog(LOG_INFO, "remap: done"); } return current; + +dead: /* Deadman expired */ + syslog(LOG_ERR, + "remap: Breaking loop after %d steps, input = %s, last = %s", + deadman_max_steps, input, current); + free(current); + return NULL; /* Did not terminate! */ } diff --git a/tftpd/remap.h b/tftpd/remap.h index 69ca08d..c8d76ff 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -35,8 +35,13 @@ struct rule *parserulefile(FILE *); void freerules(struct rule *); /* Execute a rule set on a string; returns a malloc'd new string. */ -char *rewrite_string(const char *, const struct rule *, char, +struct formats; +char *rewrite_string(const struct formats *, const char *, + const struct rule *, int, int, match_pattern_callback, const char **); +/* Remapping deadman counter */ +extern int deadman_max_steps; + #endif /* WITH_REGEX */ #endif /* TFTPD_REMAP_H */ diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 78b4cfb..3d43325 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -1,5 +1,5 @@ .\" -*- nroff -*- --------------------------------------------------------- * -.\" +.\" .\" Copyright (c) 1990, 1993, 1994 .\" The Regents of the University of California. All rights reserved. .\" @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTPD 8 "14 September 2009" "tftp-hpa @@VERSION@@" "System Manager's Manual" +.TH TFTPD 8 "7 June 2014" "tftp-hpa @@VERSION@@" "System Manager's Manual" .SH NAME .B tftpd \- Trivial File Transfer Protocol server @@ -155,7 +155,7 @@ or .B utimeout option is negotiated. The default is 1000000 (1 second.) .TP -\fB\-\-mapfile\fP \fIremap-file\fP, \fB\-m\fP \fIremap-file\fP +\fB\-\-map-file\fP \fIremap-file\fP, \fB\-m\fP \fIremap-file\fP Specify the use of filename remapping. The .I remap-file is a file containing the remapping rules. See the section on filename @@ -163,6 +163,10 @@ remapping below. This option may not be compiled in, see the output of .B "in.tftpd \-V" to verify whether or not it is available. .TP +.\fB\-\-map-steps\fP \fIsteps\fP +Specify the number of remapping rules that may be executed before the +filename mapping fails. The default is 4096. +.TP \fB\-\-verbose\fP, \fB\-v\fP Increase the logging verbosity of .BR tftpd . @@ -238,7 +242,7 @@ option, but crash with an error if they actually get the option accepted by the server. .SH "FILENAME REMAPPING" The -.B \-\-mapfile +.B \-\-map-file option specifies a file which contains filename remapping rules. Each non-comment line (comments begin with hash marks, .BR # ) @@ -270,7 +274,18 @@ by the The replacement pattern may contain escape sequences; see below. .TP .B g -Repeat this rule until it no longer matches. This is always used with +Repeat this rule until it no longer matches. This is always used with +.BR r . +.TP +.B gg +Repeat this rule until it no longer matches, but only on the portion +of the string that has not yet been matched, similar to how the +.B s +command with the +.B g +option works in +.BR sed (1). +This is always used with .BR r . .TP .B i @@ -281,6 +296,17 @@ case-insensitively. By default it is case sensitive. .B e If this rule matches, end rule processing after executing the rule. .TP +.B E +If this rule matches, +\fIand the result matches a filename that can be transferred\fP, +end rule processing after executing the rule. If this is combined with +.BR r , +then if the substitution does \fInot\fP result in a valid filename, +the substitution is undone. This cannot be combined with +.BR g , +but \fIcan\fP be combined with +.BR gg . +.TP .B s If this rule matches, start rule processing over from the very first rule after executing this rule. @@ -295,16 +321,22 @@ This rule applies to GET (RRQ) requests only. .B P This rule applies to PUT (WRQ) requests only. .TP +.B 4 +This rule applies to IPv4 sessions only. +.TP +.B 6 +This rule applies to IPv6 sessions only. +.TP .B ~ Inverse the sense of this rule, i.e. execute the .I operation only if the .I regex .I doesn't -match. Cannot used together with +match. Cannot used together with .BR r . .PP -The following escape sequences are recognized as part of the +The following escape sequences are recognized as part of a .IR "replacement pattern" : .TP \fB\\0\fP @@ -318,12 +350,14 @@ subexpressions, \\( ... \\), of the pattern. .TP \fB\\i\fP -The IP address of the requesting host, in dotted-quad notation -(e.g. 192.0.2.169). +The IP address of the requesting host, in dotted-quad notation for +IPv4 (e.g. 192.0.2.169) or conventional colon form for IPv6 +(e.g. 2001:db8::1). .TP \fB\\x\fP -The IP address of the requesting host, in hexadecimal notation -(e.g. C00002A9). +The IP address of the requesting host, in expanded hexadecimal +notation (e.g. C00002A9 for IPv4, or 20010DB8000000000000000000000001 +for IPv6). .TP \fB\\\\\fP Literal backslash. @@ -383,14 +417,14 @@ Access to files can, and should, be restricted by invoking .B tftpd with a list of directories by including pathnames as server program arguments on the command line. In this case access is restricted to -files whole names are prefixed by one of the given directories. If +files whose names are prefixed by one of the given directories. If possible, it is recommended that the .B \-\-secure flag is used to set up a chroot() environment for the server to run in once a connection has been set up. .PP Finally, the filename remapping -.RB ( \-\-mapfile +.RB ( \-\-map-file flag) support can be used to provide a limited amount of additional access control. .SH "CONFORMING TO" diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 1873e70..e0041dc 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Regents of the University of California. * Copyright (c) 1999-2009 H. Peter Anvin - * Copyright (c) 2011 Intel Corporation; author: H. Peter Anvin + * Copyright (c) 2011-2014 Intel Corporation; author: H. Peter Anvin * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -76,7 +76,7 @@ static int ai_fam = AF_INET; #define TRIES 6 /* Number of attempts to send each packet */ #define TIMEOUT_LIMIT ((1 << TRIES)-1) -const char *__progname; +const char *tftpd_progname; static int peer; static unsigned long timeout = TIMEOUT; /* Current timeout value */ static unsigned long rexmtval = TIMEOUT; /* Basic timeout value */ @@ -93,7 +93,6 @@ static unsigned int max_blksize = MAX_SEGSIZE; static char tmpbuf[INET6_ADDRSTRLEN], *tmp_p; static union sock_addr from; -static socklen_t fromlen; static off_t tsize; static int tsize_ok; @@ -107,11 +106,12 @@ int portrange = 0; unsigned int portrange_from, portrange_to; int verbosity = 0; -struct formats; #ifdef WITH_REGEX static struct rule *rewrite_rules = NULL; #endif +static FILE *file; + int tftp(struct tftphdr *, int); static void nak(int, const char *); static void timer(int); @@ -153,7 +153,7 @@ static void handle_exit(int sig) } /* Handle timeout signal or timeout event */ -void timer(int sig) +static void timer(int sig) { (void)sig; /* Suppress unused warning */ timeout <<= 1; @@ -163,14 +163,14 @@ void timer(int sig) } #ifdef WITH_REGEX -static struct rule *read_remap_rules(const char *file) +static struct rule *read_remap_rules(const char *rulefile) { FILE *f; struct rule *rulep; - f = fopen(file, "rt"); + f = fopen(rulefile, "rt"); if (!f) { - syslog(LOG_ERR, "Cannot open map file: %s: %m", file); + syslog(LOG_ERR, "Cannot open map file: %s: %m", rulefile); exit(EX_NOINPUT); } rulep = parserulefile(f); @@ -185,7 +185,8 @@ static struct rule *read_remap_rules(const char *file) */ static int lock_file(int fd, int lock_write) { -#if defined(HAVE_FCNTL) && defined(HAVE_F_SETLK_DEFINITION) + (void)lock_write; +#if defined(HAVE_FCNTL) && HAVE_DECL_F_SETLK struct flock fl; fl.l_type = lock_write ? F_WRLCK : F_RDLCK; @@ -193,7 +194,7 @@ static int lock_file(int fd, int lock_write) fl.l_start = 0; fl.l_len = 0; /* Whole file */ return fcntl(fd, F_SETLK, &fl); -#elif defined(HAVE_LOCK_SH_DEFINITION) +#elif defined(HAVE_FLOCK) && HAVE_DECL_LOCK_SH && HAVE_DECL_LOCK_EX return flock(fd, lock_write ? LOCK_EX|LOCK_NB : LOCK_SH|LOCK_NB); #else return 0; /* Hope & pray... */ @@ -324,8 +325,9 @@ static int split_port(char **ap, char **pp) enum long_only_options { OPT_VERBOSITY = 256, + OPT_MAP_STEPS }; - + static struct option long_options[] = { { "ipv4", 0, NULL, '4' }, { "ipv6", 0, NULL, '6' }, @@ -346,6 +348,7 @@ static struct option long_options[] = { { "retransmit", 1, NULL, 'T' }, { "port-range", 1, NULL, 'R' }, { "map-file", 1, NULL, 'm' }, + { "map-steps", 1, NULL, OPT_MAP_STEPS }, { "pidfile", 1, NULL, 'P' }, { NULL, 0, NULL, 0 } }; @@ -375,6 +378,7 @@ int main(int argc, char **argv) int spec_umask = 0; int c; int setrv; + int die; int waittime = 900; /* Default time to wait for a connect */ const char *user = "nobody"; /* Default user */ char *p, *ep; @@ -387,9 +391,9 @@ int main(int argc, char **argv) /* basename() is way too much of a pain from a portability standpoint */ p = strrchr(argv[0], '/'); - __progname = (p && p[1]) ? p + 1 : argv[0]; + tftpd_progname = (p && p[1]) ? p + 1 : argv[0]; - openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + openlog(tftpd_progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); srand(time(NULL) ^ getpid()); @@ -493,6 +497,17 @@ int main(int argc, char **argv) } rewrite_file = optarg; break; + case OPT_MAP_STEPS: + { + unsigned long steps = strtoul(optarg, &ep, 0); + if (*optarg && *ep && steps > 0 && steps <= INT_MAX) { + deadman_max_steps = steps; + } else { + syslog(LOG_ERR, "Bad --map-steps option: %s", optarg); + exit(EX_USAGE); + } + break; + } #endif case 'v': verbosity++; @@ -874,9 +889,7 @@ int main(int argc, char **argv) set_socket_nonblock(fd, 0); #endif - fromlen = sizeof(from); - n = myrecvfrom(fd, buf, sizeof(buf), 0, - (struct sockaddr *)&from, &fromlen, &myaddr); + n = myrecvfrom(fd, buf, sizeof(buf), 0, &from, &myaddr); if (n < 0) { if (E_WOULD_BLOCK(errno) || errno == EINTR) { @@ -938,14 +951,14 @@ int main(int argc, char **argv) syslog daemon gets restarted by the time we get here. */ if (secure && standalone) { closelog(); - openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + openlog(tftpd_progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); } #ifdef HAVE_TCPWRAPPERS /* Verify if this was a legal request for us. This has to be done before the chroot, while /etc is still accessible. */ request_init(&wrap_request, - RQ_DAEMON, __progname, + RQ_DAEMON, tftpd_progname, RQ_FILE, fd, RQ_CLIENT_SIN, &from, RQ_SERVER_SIN, &myaddr, 0); sock_methods(&wrap_request); @@ -977,21 +990,29 @@ int main(int argc, char **argv) exit(EX_IOERR); } - /* Set up the supplementary group access list if possible */ - /* /etc/group still need to be accessible at this point */ + /* Set up the supplementary group access list if possible + /etc/group still need to be accessible at this point. + If we get EPERM, this is already a restricted process, e.g. + using user namespaces on Linux. */ + die = 0; +#ifdef HAVE_SETGROUPS + setrv = setgroups(0, NULL); + if (setrv && errno != EPERM) { + syslog(LOG_ERR, "cannot clear group list"); + die = EX_OSERR; + } +#endif #ifdef HAVE_INITGROUPS setrv = initgroups(user, pw->pw_gid); - if (setrv) { + if (!setrv) { + die = 0; + } else if (errno != EPERM) { syslog(LOG_ERR, "cannot set groups for user %s", user); - exit(EX_OSERR); - } -#else -#ifdef HAVE_SETGROUPS - if (setgroups(0, NULL)) { - syslog(LOG_ERR, "cannot clear group list"); + die = EX_OSERR; } #endif -#endif + if (die) + exit(die); /* Chroot and drop privileges */ if (secure) { @@ -1003,19 +1024,30 @@ int main(int argc, char **argv) chdir("/"); /* Cygwin chroot() bug workaround */ #endif } -#ifdef HAVE_SETREGID + +#ifdef HAVE_SETRESGID + setrv = setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid); +#elif defined(HAVE_SETREGID) setrv = setregid(pw->pw_gid, pw->pw_gid); #else setrv = setegid(pw->pw_gid) || setgid(pw->pw_gid); #endif + if (setrv && errno == EPERM) { + setrv = 0; /* Assume already restricted by system policy */ + } -#ifdef HAVE_SETREUID +#ifdef HAVE_SETRESUID + setrv = setrv || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid); +#elif defined(HAVE_SETREUID) setrv = setrv || setreuid(pw->pw_uid, pw->pw_uid); #else /* Important: setuid() must come first */ setrv = setrv || setuid(pw->pw_uid) || (geteuid() != pw->pw_uid && seteuid(pw->pw_uid)); #endif + if (setrv && errno == EPERM) { + setrv = 0; /* Assume already restricted by system policy */ + } if (setrv) { syslog(LOG_ERR, "cannot drop privileges: %m"); @@ -1039,23 +1071,16 @@ int main(int argc, char **argv) tp = (struct tftphdr *)buf; tp_opcode = ntohs(tp->th_opcode); if (tp_opcode == RRQ || tp_opcode == WRQ) - tftp(tp, n); + tftp(tp, n); exit(0); } -static char *rewrite_access(char *, int, const char **); +static char *rewrite_access(const struct formats *, + char *, int, int, const char **); static int validate_access(char *, int, const struct formats *, const char **); static void tftp_sendfile(const struct formats *, struct tftphdr *, int); static void tftp_recvfile(const struct formats *, struct tftphdr *, int); -struct formats { - const char *f_mode; - char *(*f_rewrite) (char *, int, const char **); - int (*f_validate) (char *, int, const struct formats *, const char **); - void (*f_send) (const struct formats *, struct tftphdr *, int); - void (*f_recv) (const struct formats *, struct tftphdr *, int); - int f_convert; -}; static const struct formats formats[] = { { "netascii", rewrite_access, validate_access, tftp_sendfile, @@ -1112,9 +1137,9 @@ int tftp(struct tftphdr *tp, int size) nak(EBADOP, "Unknown mode"); exit(0); } - if (!(filename = - (*pf->f_rewrite) (origfilename, tp_opcode, - &errmsgptr))) { + file = NULL; + if (!(filename = (*pf->f_rewrite) + (pf, origfilename, tp_opcode, from.sa.sa_family, &errmsgptr))) { nak(EACCESS, errmsgptr); /* File denied by mapping rule */ exit(0); } @@ -1137,12 +1162,18 @@ int tftp(struct tftphdr *tp, int size) tmp_p, origfilename, filename); } - ecode = - (*pf->f_validate) (filename, tp_opcode, pf, &errmsgptr); - if (ecode) { - nak(ecode, errmsgptr); - exit(0); - } + /* + * If "file" is already set, then a file was already validated + * and opened during remap processing. + */ + if (!file) { + ecode = + (*pf->f_validate) (filename, tp_opcode, pf, &errmsgptr); + if (ecode) { + nak(ecode, errmsgptr); + exit(0); + } + } opt = ++cp; } else if (argn & 1) { val = ++cp; @@ -1229,7 +1260,7 @@ static int set_blksize2(uintmax_t *vp) static int set_rollover(uintmax_t *vp) { uintmax_t ro = *vp; - + if (ro > 65535) return 0; @@ -1328,7 +1359,7 @@ static void do_opt(const char *opt, const char *val, char **ap) nak(EOPTNEG, "Insufficient space for options"); exit(0); } - + memcpy(p, opt, optlen+1); p += optlen+1; memcpy(p, retbuf, retlen+1); @@ -1398,12 +1429,12 @@ static int rewrite_macros(char macro, char *output) /* * Modify the filename, if applicable. If it returns NULL, deny the access. */ -static char *rewrite_access(char *filename, int mode, const char **msg) +static char *rewrite_access(const struct formats *pf, char *filename, + int mode, int af, const char **msg) { if (rewrite_rules) { char *newname = - rewrite_string(filename, rewrite_rules, - mode != RRQ ? 'P' : 'G', + rewrite_string(pf, filename, rewrite_rules, mode, af, rewrite_macros, msg); filename = newname; } @@ -1411,15 +1442,17 @@ static char *rewrite_access(char *filename, int mode, const char **msg) } #else -static char *rewrite_access(char *filename, int mode, const char **msg) +static char *rewrite_access(const struct formats *pf, char *filename, + int mode, int af, const char **msg) { + (void)pf; (void)mode; /* Avoid warning */ (void)msg; + (void)af; return filename; } #endif -static FILE *file; /* * Validate file access. Since we * have no uid or gid, for now require @@ -1644,17 +1677,21 @@ static void tftp_sendfile(const struct formats *pf, struct tftphdr *oap, int oac /* * Receive a file. */ -static void tftp_recvfile(const struct formats *pf, struct tftphdr *oap, int oacklen) +static void tftp_recvfile(const struct formats *pf, + struct tftphdr *oack, int oacklen) { struct tftphdr *dp; int n, size; /* These are "static" to avoid longjmp funnies */ + static struct tftphdr *oap; static struct tftphdr *ap; /* ack buffer */ static u_short block = 0; static int acksize; u_short dp_opcode, dp_block; unsigned long r_timeout; + oap = oack; + dp = w_init(); do { timeout = rexmtval; diff --git a/tftpd/tftpd.h b/tftpd/tftpd.h index e1d8bf0..277e5d2 100644 --- a/tftpd/tftpd.h +++ b/tftpd/tftpd.h @@ -23,4 +23,13 @@ char *tfstrdup(const char *); extern int verbosity; +struct formats { + const char *f_mode; + char *(*f_rewrite) (const struct formats *, char *, int, int, const char **); + int (*f_validate) (char *, int, const struct formats *, const char **); + void (*f_send) (const struct formats *, struct tftphdr *, int); + void (*f_recv) (const struct formats *, struct tftphdr *, int); + int f_convert; +}; + #endif diff --git a/version b/version index a75b92f..d346e2a 100644 --- a/version +++ b/version @@ -1 +1 @@ -5.1 +5.3