diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 544d21f..0000000 --- a/.editorconfig +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index bb95251..0000000 --- a/.gitignore +++ /dev/null @@ -1,21 +0,0 @@ -/config/MCONFIG -/config/config.h -/config/config.h.in -/autoconf/aclocal.m4 -/autoconf/clean.sh -/autoconf/helpers/ -/autom4te.cache -/config.log -/config.status -/configure -/version.h -/tftp/tftp -/tftpd/tftpd -*.1 -*.8 -*.a -*.o -*.i -*.s -*~ -\#* diff --git a/CHANGES b/CHANGES index 6b7b53c..2ae9114 100644 --- a/CHANGES +++ b/CHANGES @@ -1,255 +1,4 @@ -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. - - Bounce the syslog socket in standalone mode, in case the - syslog daemon has been restarted. Patch by Ferenc Wagner. - - Build fixes. - - Fix handling of block number wraparound after a successful - options negotiation. - - Fix a buffer overflow in option parsing. - - -Changes in 5.0: - Try to on platforms with getaddrinfo() without AI_ADDRCONFIG or - AI_CANONNAME. - - Implement the "rollover" option, for clients which want block - number to rollover to anything other than zero. - - Correctly disable PMTU in standalone mode. Patch by Florian - Lohoff. - - -Changes in 0.49: - Add IPv6 support. Patch by Karsten Keil. - - Support systems with editline instead of readline. - - Support long options in the server. - - -Changes in 0.48: - Unbreak -l -s in the server, which was broken in 0.47. - - -Changes in 0.47: - Add -L option to the server to run standalone without - detaching from the shell. - - Parallel make fix. - - -Changes in 0.46: - Minor portability improvements. - - -Changes in 0.45: - Add -l (literal) option to the client, to override the special - treatment of the colon (:) character as a hostname separator. - - -Changes in 0.44: - Allow the client to specify a range of local port numbers, - just like the server can. - - Fix sending SIGHUP to update the regular expression table. - - -Changes in 0.43: - Fix double-free error on ^c in client. - - Try to deal with clients that send TFTP requests to broadcasts - (apparently some recent Sun boxes do this instead of using the - address told by DHCP. Bad Sun! Bad Sun!) - - Portability fixes. - - -Changes in 0.42: - Try to disable path MTU discovery for TFTP connections (it's - useless anyway.) - - Add a hack to allow the admin to specify a range of local port - numbers to use. - - Fix local IP number handling on systems which present - IP_RECVDSTADDR in recvmsg(). - - -Changes in 0.41: - Fix bug by which patterns of the form \U\1 weren't converted - correctly. - - -Changes in 0.40.1: - Solaris build fix. - - -Changes in 0.40: - Fix bug which would cause "r" remapping rules to be - incorrectly rejected. - - -Changes in 0.39: - Support Perl-style \U...\E and \L...\E, as well as allow - matching rules to be inverted (execute if rule *doesn't* - match.) - - Fix a timeout bug. - - Add an RPM spec file. - - -Changes in 0.38: - Portability fixes. - - -Changes in 0.37: - Fix a pathology where a client sending ACKs for the wrong - packet can prevent proper retransmission. - - -Changes in 0.36: - Portability fixes. - - -Changes in 0.35: - Add an option to control the maximum value of blksize - negotiated. - - Removed workaround for obsolete Cygwin problem. - - Don't use getopt() -- the -c option doesn't work correctly - since it depends on the ordering of arguments and options. It - is now possible to do: - - tftp -m binary hostname -c get filename - - This was previous possible by doing: - - tftp -m binary -c get hostname:filename - - ... but it seemed that was counterintuitive to people. - - Somewhat improved configure scripts. - - -Changes in 0.34: - Additional Solaris gcc compiler bug workarounds; these - actually make the code somewhat cleaner. - - -Changes in 0.33: - Even better error messages. - - Work around a suspect Solaris gcc bug. - - Configuration fix: readline needs termcap. - - Support running the tftp client from the command line. For - example: - - tftp -m binary -c get hostname:file - - -Changes in 0.32: - Better error messages; including the capability to send a - custom error message to the client when hitting an "a" rule in - a remapping table. - - -Changes in 0.31: - Put in a check to make sure xinetd (in particular) doesn't - pass us an IPv6 socket. - - Fix some problems related to timeout negotiation. - - Allow the user to set the default timeout speed. - - -Changes in 0.30: - (Hopefully) better timeout algorithm. - - Add a "utimeout" option; like "timeout" but in microseconds. - - Change the log level of client-side errors to LOG_WARNING. - - autoconf portability improvements. - - Minor bugfixes. - - -Changes in 0.29: - Posixly correctness. - - Now compiles and runs on Win32 systems using Cygwin - (http://www.cygwin.com/). - (). - - Fixed a bug which could cause a standalone server to exit with - a "recvfrom: Interrupted system call" log message if signals - arrive at a particularly inopportune moment. - - Fix a macro substitution bug (thanks to Richard Nyberg.) - - -Changes in 0.28: - Fix stupid one-liner bug which broke standalone mode (-l). - - -Changes in 0.27: - Make the Digital Unix 4.0F platform work again. Thanks to - Alan Sundell for helping out with this platform! - - Make the AIX 4.3 platform work again. Thanks to Josef Siemes - for helping out with this platform! - - Allow replacement patterns to include the IP address of the - requesting host (\i). - - Allow relying on Unix permissions rather than o+r magic if the - -p option is specified. As part of this, set all groups if - initgroups() is specified on the platform. - - Clean up race conditions inherited from the BSD source base. - - -Changes in 0.26: - Fix the configuration process so tftpd doesn't end up - depending on readline, which apparently could happen on some - platforms before. - - Make parallel builds (make -j) work correctly. - - Improve parsing of the "connect" command in the tftp client. - - Add a -V option to both tftp and tftpd to print the version - number on stdout and immediately exit. - - Add a -v option to tftp to start out in verbose mode. - - Rewrite the man pages using standard "man" troff macros. - - Enable the (limited) use of readline on systems which don't - have readline/history.h. - - Support compiling under MacOS X with fink (see - ). Thanks for Justin Hallett - and Eric Eslinger for their help in getting this working! - - -Changes in 0.25: - Fixed Sorcerer's Apprentice bug in both the client and the - server. These bugs were inherited from the original BSD code. - +$Id$ Changes in 0.24: Fix bugs in both client and server dealing with block number diff --git a/INSTALL.tftp b/INSTALL.tftp index 914094f..4bd87a5 100644 --- a/INSTALL.tftp +++ b/INSTALL.tftp @@ -1,3 +1,5 @@ +$Id$ + Specific installation instructions ================================== diff --git a/config/MCONFIG.in b/MCONFIG.in similarity index 73% rename from config/MCONFIG.in rename to MCONFIG.in index 2660f7e..d9d5718 100644 --- a/config/MCONFIG.in +++ b/MCONFIG.in @@ -1,12 +1,13 @@ ## -*- makefile -*- ------------------------------------------------------ ## -## Copyright 2001-2007 H. Peter Anvin - All Rights Reserved +## Copyright 2001 H. Peter Anvin - All Rights Reserved ## ## This program is free software available under the same license ## as the "OpenBSD" operating system, distributed at ## http://www.openbsd.org/. ## ## ----------------------------------------------------------------------- +## $Id$ ## ## MCONFIG.in @@ -14,10 +15,6 @@ ## Basic Makefile definitions ## -# Source and object root -SRCROOT = @SRCROOT@ -OBJROOT = @OBJROOT@ - # Prefixes prefix = @prefix@ exec_prefix = @exec_prefix@ @@ -31,19 +28,9 @@ MANDIR = @mandir@ # System binaries SBINDIR = @sbindir@ -# Data root directory -datarootdir = @datarootdir@ - -# Binary suffixes -O = @OBJEXT@ -X = @EXEEXT@ - # Install into alternate root area, e.g. for package generation INSTALLROOT = -# Link -LN_S = @LN_S@ - # Install program INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -56,9 +43,8 @@ CFLAGS = @CFLAGS@ -I$(SRCROOT) # Link flags LDFLAGS = @LDFLAGS@ -# Libraries (client and server) -TFTP_LIBS = ../common/libcommon.a @TFTP_LIBS@ -TFTPD_LIBS = ../common/libcommon.a @TFTPD_LIBS@ +# Libraries +LIBS = @LIBS@ # Additional library we need to build LIBOBJS = @LIBOBJS@ diff --git a/Makefile b/Makefile index 2e790fd..4c2369e 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,12 @@ # You can do "make SUB=blah" to make only a few, or edit here, or both # You can also run make directly in the subdirs you want. -SUB = lib common tftp tftpd +SUB = lib tftp tftpd -%.build: config/MCONFIG config/config.h version.h +%.build: $(MAKE) -C $(patsubst %.build, %, $@) -%.install: config/MCONFIG config/config.h version.h +%.install: $(MAKE) -C $(patsubst %.install, %, $@) install %.clean: @@ -15,55 +15,39 @@ SUB = lib common tftp tftpd %.distclean: $(MAKE) -C $(patsubst %.distclean, %, $@) distclean -all: config/MCONFIG $(patsubst %, %.build, $(SUB)) +all: 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: $(patsubst %, %.clean, $(SUB)) -clean: localclean $(patsubst %, %.clean, $(SUB)) - -localclean: - rm -f version.h - -distclean: localdistclean $(patsubst %, %.distclean, $(SUB)) - -localdistclean: localclean - rm -f config/config/MCONFIG config.status config.log config/config.h *~ \#* +distclean: $(patsubst %, %.distclean, $(SUB)) + rm -f MCONFIG config.status config.log acconfig.h *~ \#* rm -rf *.cache - find . -type f \( -name \*.orig -o -name \*.rej \) | xargs rm -f + find . -type f \( -name \*.orig -o -name \*.rej \) | xargs -r rm -f spotless: distclean - rm -f configure config/config.h.in tftp.spec + rm -f configure acconfig.h.in -autoconf: configure config/config.h.in +autoconf: configure acconfig.h.in -config: config/MCONFIG config/config.h +config: MCONFIG acconfig.h release: $(MAKE) autoconf - $(MAKE) tftp.spec $(MAKE) distclean -config/MCONFIG: configure config/MCONFIG.in config/config.h.in - if test -x config.status; then \ - ./config.status --recheck && ./config.status ; \ - else \ - ./configure ; \ - fi +MCONFIG: configure MCONFIG.in acconfig.h.in + ./configure -config/config.h: config/MCONFIG +acconfig.h: MCONFIG : Generated by side effect -configure: configure.ac - sh autogen.sh +acconfig.h.in: configure.in aclocal.m4 + autoheader -f + touch -c acconfig.h.in + rm -f acconfig.h -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 $@ +configure: configure.in aclocal.m4 + autoconf + rm -f MCONFIG config.cache config.log acconfig.h diff --git a/README b/README index 91afcb3..fc25395 100644 --- a/README +++ b/README @@ -21,7 +21,5 @@ installation instructions. This software can be discussed on the SYSLINUX mailing list. To -subscribe, go to the list subscription page at: - - http://www.zytor.com/mailman/listinfo/syslinux - +subscribe, send a message containing the word "subscribe" in the body +to . diff --git a/README.security b/README.security index 644babb..7db6f2e 100644 --- a/README.security +++ b/README.security @@ -1,26 +1,3 @@ -Starting in version 0.27, tftp-hpa has the option of a "use Unix -permissions" mode. In this mode, tftpd can access any file accessible -by the tftpd effective user, specified via the -u option. This means -that files no longer need to be set to o+r or o+w. - -If file creation is enabled (via the -c option), the -p option also -changes the default umask from 0 (anyone can read or write) to -"unchanged" (inherited from the calling process.) The -U option can -be used to override the default umask; this is recommended. - -The sanest setup, from a security standpoint, for tftpd to run in is -probably the following: - -1. Create a separate "tftpd" user and group only used for tftpd; -2. Have all your boot files in a single directory tree (usually called - /tftpboot). -3. Specify "-p -u tftpd -s /tftpboot" on the tftpd command line; if - you want clients to be able to create files use - "-p -c -U 002 -u tftpd -s /tftpboot" (replace 002 with whatever - umask is appropriate for your setup.) - - ======================================= - Starting in version 0.17, tftp-hpa operates in genuine "wait" mode, which means that an in.tftpd process hangs around for some time after the last service request has arrived. This speeds up servicing a diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..310d5b0 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,172 @@ +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_COMPILE([#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( + [#include ], + [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_MSG_CHECKING([for msg_control in struct msghdr]) + AC_TRY_COMPILE( +[ +#define _XPG4_2 /* Needed on Solaris */ +#include +#include +#include +], +[ + struct msghdr msg; + void *p = (void *) &msg.msg_control; +], +[ + AC_DEFINE(HAVE_MSGHDR_MSG_CONTROL) + AC_MSG_RESULT([yes]) +], +[ + AC_MSG_RESULT([no]) +])]) + +dnl ------------------------------------------------------------------------ +dnl PA_STRUCT_IN_PKTINFO +dnl +dnl Look for definition of struct in_pktinfo. Some versions of glibc +dnl lack struct in_pktinfo; if so we need to include the definition +dnl ourselves -- but we only 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_MSG_CHECKING([for definition of struct in_pktinfo]) + AC_TRY_COMPILE( +[ +#include +#include +#include +#include +#include +#include +], +[ + struct in_pktinfo pktinfo; + int foo = sizeof(struct in_pktinfo); + void *quux = (void *)(&pktinfo.ipi_addr); +], +[ + AC_DEFINE(HAVE_STRUCT_IN_PKTINFO) + AC_MSG_RESULT([yes]) +], +[ + AC_MSG_RESULT([no]) +])]) + +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_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]) + 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) +])]) + diff --git a/autoconf/m4/pa_add_cflags.m4 b/autoconf/m4/pa_add_cflags.m4 deleted file mode 100644 index 26d55c4..0000000 --- a/autoconf/m4/pa_add_cflags.m4 +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 23f96f1..0000000 --- a/autoconf/m4/pa_add_flags.m4 +++ /dev/null @@ -1,39 +0,0 @@ -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 deleted file mode 100644 index d3c478a..0000000 --- a/autoconf/m4/pa_add_headers.m4 +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 05c3114..0000000 --- a/autoconf/m4/pa_add_langflags.m4 +++ /dev/null @@ -1,27 +0,0 @@ -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 deleted file mode 100644 index 5289ed4..0000000 --- a/autoconf/m4/pa_arg_bool.m4 +++ /dev/null @@ -1,19 +0,0 @@ -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 deleted file mode 100644 index 42f4ce8..0000000 --- a/autoconf/m4/pa_arg_disabled.m4 +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index 7d66a21..0000000 --- a/autoconf/m4/pa_arg_enabled.m4 +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index 1aacfcd..0000000 --- a/autoconf/m4/pa_build_ifelse.m4 +++ /dev/null @@ -1,16 +0,0 @@ -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 deleted file mode 100644 index 909b171..0000000 --- a/autoconf/m4/pa_c_typeof.m4 +++ /dev/null @@ -1,32 +0,0 @@ -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 deleted file mode 100644 index 3fbc53a..0000000 --- a/autoconf/m4/pa_check_bad_stdc_inline.m4 +++ /dev/null @@ -1,26 +0,0 @@ -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 deleted file mode 100644 index 589e0a2..0000000 --- a/autoconf/m4/pa_check_inttypes_h_sane.m4 +++ /dev/null @@ -1,17 +0,0 @@ -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 deleted file mode 100644 index f082973..0000000 --- a/autoconf/m4/pa_cross_compile.m4 +++ /dev/null @@ -1,41 +0,0 @@ -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 deleted file mode 100644 index 2ef5ded..0000000 --- a/autoconf/m4/pa_flags_langlist.m4 +++ /dev/null @@ -1,19 +0,0 @@ -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 deleted file mode 100644 index fba87ce..0000000 --- a/autoconf/m4/pa_have_tcpwrappers.m4 +++ /dev/null @@ -1,26 +0,0 @@ -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 deleted file mode 100644 index 650913d..0000000 --- a/autoconf/m4/pa_lang_foreach.m4 +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index d524013..0000000 --- a/autoconf/m4/pa_lang_seen_list.m4 +++ /dev/null @@ -1,20 +0,0 @@ -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 deleted file mode 100644 index ae7d9db..0000000 --- a/autoconf/m4/pa_option_debug.m4 +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 39a3f6c..0000000 --- a/autoconf/m4/pa_option_profiling.m4 +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 0debe5d..0000000 --- a/autoconf/m4/pa_prog_cc.m4 +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index b006128..0000000 --- a/autoconf/m4/pa_search_libs_and_add.m4 +++ /dev/null @@ -1,22 +0,0 @@ -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 deleted file mode 100644 index 898b562..0000000 --- a/autoconf/m4/pa_sigsetjmp.m4 +++ /dev/null @@ -1,25 +0,0 @@ -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 deleted file mode 100644 index d3a8965..0000000 --- a/autoconf/m4/pa_sym.m4 +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100755 index 0c892cc..0000000 --- a/autogen.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/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 deleted file mode 100644 index 89e351d..0000000 --- a/common/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -SRCROOT = .. -VERSION = $(shell cat ../version) - --include ../config/MCONFIG -include ../MRULES - -OBJS = tftpsubs.$(O) signal.$(O) -LIB = libcommon.a - -all: $(LIB) - -$(LIB): $(OBJS) - -rm -f $(LIB) - $(AR) $(LIB) $(OBJS) - $(RANLIB) $(LIB) - -$(OBJS): tftpsubs.h - -install: - -clean: - rm -f *.o *.obj *.exe $(LIB) - -distclean: clean - rm -f *~ diff --git a/common/signal.c b/common/signal.c deleted file mode 100644 index ccd4af8..0000000 --- a/common/signal.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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/common/tftpsubs.c b/common/tftpsubs.c deleted file mode 100644 index 8c999f6..0000000 --- a/common/tftpsubs.c +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright (c) 1983, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "tftpsubs.h" - -/* Simple minded read-ahead/write-behind subroutines for tftp user and - server. Written originally with multiple buffers in mind, but current - implementation has two buffer logic wired in. - - Todo: add some sort of final error check so when the write-buffer - is finally flushed, the caller can detect if the disk filled up - (or had an i/o error) and return a nak to the other side. - - Jim Guyton 10/85 - */ - -#include - -#define PKTSIZE MAX_SEGSIZE+4 /* should be moved to tftp.h */ - -int segsize = SEGSIZE; /* Default segsize */ - -struct bf { - int counter; /* size of data in buffer, or flag */ - char buf[PKTSIZE]; /* room for data packet */ -} bfs[2]; - - /* Values for bf.counter */ -#define BF_ALLOC -3 /* alloc'd but not yet filled */ -#define BF_FREE -2 /* free */ -/* [-1 .. segsize] = size of data in the data buffer */ - -static int nextone; /* index of next buffer to use */ -static int current; /* index of buffer in use */ - - /* control flags for crlf conversions */ -int newline = 0; /* fillbuf: in middle of newline expansion */ -int prevchar = -1; /* putbuf: previous char (cr check) */ - -static struct tftphdr *rw_init(int); - -struct tftphdr *w_init() -{ - return rw_init(0); -} /* write-behind */ - -struct tftphdr *r_init() -{ - return rw_init(1); -} /* read-ahead */ - -/* init for either read-ahead or write-behind */ -/* x == zero for write-behind, one for read-head */ -static struct tftphdr *rw_init(int x) -{ - newline = 0; /* init crlf flag */ - prevchar = -1; - bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ - current = 0; - bfs[1].counter = BF_FREE; - nextone = x; /* ahead or behind? */ - return (struct tftphdr *)bfs[0].buf; -} - -/* Have emptied current buffer by sending to net and getting ack. - Free it and return next buffer filled with data. - */ -int readit(FILE * file, struct tftphdr **dpp, int convert) -{ - struct bf *b; - - bfs[current].counter = BF_FREE; /* free old one */ - current = !current; /* "incr" current */ - - b = &bfs[current]; /* look at new buffer */ - if (b->counter == BF_FREE) /* if it's empty */ - read_ahead(file, convert); /* fill it */ - /* assert(b->counter != BF_FREE);*//* check */ - *dpp = (struct tftphdr *)b->buf; /* set caller's ptr */ - return b->counter; -} - -/* - * fill the input buffer, doing ascii conversions if requested - * conversions are lf -> cr,lf and cr -> cr, nul - */ -void read_ahead(FILE * file, int convert) -{ - int i; - char *p; - int c; - struct bf *b; - struct tftphdr *dp; - - b = &bfs[nextone]; /* look at "next" buffer */ - if (b->counter != BF_FREE) /* nop if not free */ - return; - nextone = !nextone; /* "incr" next buffer ptr */ - - dp = (struct tftphdr *)b->buf; - - if (convert == 0) { - b->counter = read(fileno(file), dp->th_data, segsize); - return; - } - - p = dp->th_data; - for (i = 0; i < segsize; i++) { - if (newline) { - if (prevchar == '\n') - c = '\n'; /* lf to cr,lf */ - else - c = '\0'; /* cr to cr,nul */ - newline = 0; - } else { - c = getc(file); - if (c == EOF) - break; - if (c == '\n' || c == '\r') { - prevchar = c; - c = '\r'; - newline = 1; - } - } - *p++ = c; - } - b->counter = (int)(p - dp->th_data); -} - -/* Update count associated with the buffer, get new buffer - from the queue. Calls write_behind only if next buffer not - available. - */ -int writeit(FILE * file, struct tftphdr **dpp, int ct, int convert) -{ - bfs[current].counter = ct; /* set size of data to write */ - current = !current; /* switch to other buffer */ - if (bfs[current].counter != BF_FREE) /* if not free */ - (void)write_behind(file, convert); /* flush it */ - bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ - *dpp = (struct tftphdr *)bfs[current].buf; - return ct; /* this is a lie of course */ -} - -/* - * Output a buffer to a file, converting from netascii if requested. - * CR,NUL -> CR and CR,LF => LF. - * Note spec is undefined if we get CR as last byte of file or a - * CR followed by anything else. In this case we leave it alone. - */ -int write_behind(FILE * file, int convert) -{ - char *buf; - int count; - int ct; - char *p; - int c; /* current character */ - struct bf *b; - struct tftphdr *dp; - - b = &bfs[nextone]; - if (b->counter < -1) /* anything to flush? */ - return 0; /* just nop if nothing to do */ - - count = b->counter; /* remember byte count */ - b->counter = BF_FREE; /* reset flag */ - dp = (struct tftphdr *)b->buf; - nextone = !nextone; /* incr for next time */ - buf = dp->th_data; - - if (count <= 0) - return -1; /* nak logic? */ - - if (convert == 0) - return write(fileno(file), buf, count); - - p = buf; - ct = count; - while (ct--) { /* loop over the buffer */ - c = *p++; /* pick up a character */ - if (prevchar == '\r') { /* if prev char was cr */ - if (c == '\n') /* if have cr,lf then just */ - fseek(file, -1, 1); /* smash lf on top of the cr */ - else if (c == '\0') /* if have cr,nul then */ - goto skipit; /* just skip over the putc */ - /* else just fall through and allow it */ - } - putc(c, file); - skipit: - prevchar = c; - } - return count; -} - -/* When an error has occurred, it is possible that the two sides - * are out of synch. Ie: that what I think is the other side's - * response to packet N is really their response to packet N-1. - * - * So, to try to prevent that, we flush all the input queued up - * for us on the network connection on our host. - * - * We return the number of packets we flushed (mostly for reporting - * when trace is active). - */ - -int synchnet(int f) -{ /* socket to flush */ - int pktcount = 0; - char rbuf[PKTSIZE]; - union sock_addr from; - socklen_t fromlen; - fd_set socketset; - struct timeval notime; - - while (1) { - notime.tv_sec = notime.tv_usec = 0; - - FD_ZERO(&socketset); - FD_SET(f, &socketset); - - if (select(f, &socketset, NULL, NULL, ¬ime) <= 0) - break; /* Nothing to read */ - - /* Otherwise drain the packet */ - pktcount++; - fromlen = sizeof(from); - (void)recvfrom(f, rbuf, sizeof(rbuf), 0, - &from.sa, &fromlen); - } - - return pktcount; /* Return packets drained */ -} - -int pick_port_bind(int sockfd, union sock_addr *myaddr, - unsigned int port_range_from, - unsigned int port_range_to) -{ - unsigned int port, firstport; - int port_range = 0; - - if (port_range_from != 0 && port_range_to != 0) { - port_range = 1; - } - - firstport = port_range - ? port_range_from + rand() % (port_range_to - port_range_from + 1) - : 0; - - port = firstport; - - do { - sa_set_port(myaddr, htons(port)); - if (bind(sockfd, &myaddr->sa, SOCKLEN(myaddr)) < 0) { - /* Some versions of Linux return EINVAL instead of EADDRINUSE */ - if (!(port_range && (errno == EINVAL || errno == EADDRINUSE))) - return -1; - - /* Normally, we shouldn't have to loop, but some situations involving - aborted transfers make it possible. */ - } else { - return 0; - } - - port++; - if (port > port_range_to) - port = port_range_from; - } while (port != firstport); - - return -1; -} - -int -set_sock_addr(char *host,union sock_addr *s, char **name) -{ - struct addrinfo *addrResult; - struct addrinfo hints; - int err; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = s->sa.sa_family; - hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - err = getaddrinfo(strip_address(host), NULL, &hints, &addrResult); - if (err) - return err; - if (addrResult == NULL) - return EAI_NONAME; - memcpy(s, addrResult->ai_addr, addrResult->ai_addrlen); - if (name) { - if (addrResult->ai_canonname) - *name = xstrdup(addrResult->ai_canonname); - else - *name = xstrdup(host); - } - freeaddrinfo(addrResult); - return 0; -} - -#ifdef HAVE_IPV6 -int is_numeric_ipv6(const char *p) -{ - /* A numeric IPv6 address consist at least of 2 ':' and - * it may have sequences of hex-digits and maybe contain - * a '.' from a IPv4 mapped address and maybe is enclosed in [] - * we do not check here, if it is a valid IPv6 address - * only if is something like a numeric IPv6 address or something else - */ - int colon = 0; - int dot = 0; - int bracket = 0; - char c; - - if (!p) - return 0; - - if (*p == '[') { - bracket = 1; - p++; - } - - while ((c = *p++) && c != ']') { - switch (c) { - case ':': - colon++; - break; - case '.': - dot++; - break; - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - break; - default: - return 0; /* Invalid character */ - } - } - - if (colon < 2 || colon > 7) - return 0; - - if (dot) { - /* An IPv4-mapped address in dot-quad form will have 3 dots */ - if (dot != 3) - return 0; - /* The IPv4-mapped address takes the space of one colon */ - if (colon > 6) - return 0; - } - - /* If bracketed, must be closed, and vice versa */ - if (bracket ^ (c == ']')) - return 0; - - /* Otherwise, assume we're okay */ - return 1; -} - -/* strip [] from numeric IPv6 addreses */ - -char *strip_address(char *addr) -{ - char *p; - - if (is_numeric_ipv6(addr) && (*addr == '[')) { - p = addr + strlen(addr); - p--; - if (*p == ']') { - *p = 0; - addr++; - } - } - return addr; -} -#endif diff --git a/config.h b/config.h index a8c55ac..7585381 100644 --- a/config.h +++ b/config.h @@ -1,12 +1,13 @@ /* -*- c -*- ------------------------------------------------------------- * - * - * Copyright 2001-2024 H. Peter Anvin - All Rights Reserved + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at * http://www.openbsd.org/. * * ----------------------------------------------------------------------- */ +/* $Id$ */ /* * config.h @@ -17,23 +18,21 @@ #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 "config/config.h" /* autogenerated configuration header */ +#include "acconfig.h" + +/* This is necessary on Solaris with gcc */ +#define _XPG4_2 +#define _XOPEN_SOURCE +#define __EXTENSIONS__ + +/* This is necessary on glibc systems */ +#define _BSD_SOURCE +#define _ISO9X_SOURCE /* Standard includes */ #include -#include -#include -#include -#include #ifdef HAVE_SYS_TYPES_H #include @@ -43,18 +42,31 @@ #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 #ifdef HAVE_STRINGS_H #include -#endif +#endif #ifdef HAVE_INTTYPES_H -#ifdef INTTYPES_H_IS_SANE #include -#endif #else #ifdef HAVE_STDINT_H #include @@ -65,62 +77,9 @@ #include #endif -#ifdef HAVE_SETJMP_H #include -#endif - -#ifdef HAVE_SYS_TIME_H -#include -#endif - -#ifdef HAVE_GRP_H -#include -#endif - -#ifdef HAVE_FCNTL_H -#include -#endif - -#ifdef HAVE_SYS_SOCKET_H +#include #include -#else -#ifdef HAVE_WINSOCK2_H -#include -#else -#ifdef HAVE_WINSOCK_H -#include -#endif -#endif -#endif -#ifdef HAVE_NETDB_H -#include -#endif - -#ifdef HAVE_GETOPT_LONG -#include -#else -#include "lib/getopt.h" -#endif - -/* Test for EAGAIN/EWOULDBLOCK */ -#ifdef EAGAIN -#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) -#define E_WOULD_BLOCK(x) ((x) == EAGAIN || (x) == EWOULDBLOCK) -#else -#define E_WOULD_BLOCK(x) ((x) == EAGAIN) -#endif -#else -#define E_WOULD_BLOCK(x) ((x) == EWOULDBLOCK) -#endif - -/* Some broken systems care about text versus binary, but - real Unix systems don't... */ -#if !HAVE_DECL_O_TEXT -#define O_TEXT 0 -#endif -#if !HAVE_DECL_O_BINARY -#define O_BINARY 0 -#endif /* If we don't have intmax_t, try creating it */ @@ -128,9 +87,9 @@ #ifdef HAVE_LONG_LONG typedef long long intmax_t; typedef unsigned long long uintmax_t; -#define PRIdMAX "lld" -#define PRIuMAX "llu" -#define PRIxMAX "llx" +#define PRIdMAX "Ld" +#define PRIuMAX "Lu" +#define PRIxMAX "Lx" #define INTMAX_C(x) (x##LL) #define UINTMAX_C(x) (x##ULL) #else @@ -144,37 +103,6 @@ typedef unsigned long uintmax_t; #endif #endif -/* On some version of AIX, is buggy to the point of - unusability. We have to use macros here, not typedefs, to override. */ -#ifdef HAVE_INTTYPES_H -#ifndef INTTYPES_H_IS_SANE -#undef PRIdMAX -#undef PRIuMAX -#undef PRIxMAX -#undef INTMAX_C -#undef UINTMAX_C -#undef HAVE_STRTOUMAX - -#ifdef HAVE_LONG_LONG -#define intmax_t long long -#define uintmax_t unsigned long long -#define PRIdMAX "Ld" -#define PRIuMAX "Lu" -#define PRIxMAX "Lx" -#define INTMAX_C(x) (x##LL) -#define UINTMAX_C(x) (x##ULL) -#else -#define intmax_t long -#define uintmax_t unsigned long -#define PRIdMAX "ld" -#define PRIuMAX "lu" -#define PRIxMAX "lx" -#define INTMAX_C(x) (x##L) -#define UINTMAX_C(x) (x##UL) -#endif -#endif -#endif - /* Even if intmax_t is defined, we may need this (Solaris 8 braindamage) */ #ifndef HAVE_STRTOUMAX #if defined(HAVE_LONG_LONG) && defined(HAVE_STRTOULL) @@ -204,11 +132,6 @@ typedef unsigned long u_long; #endif #endif -/* socklen_t */ -#ifndef HAVE_SOCKLEN_T -typedef int socklen_t; -#endif - /* sysexits.h */ #ifdef HAVE_SYSEXITS_H @@ -249,118 +172,12 @@ typedef int socklen_t; #endif #endif -/* netinet/in.h, and possible missing pieces */ +/* Sometimes IPPORT_TFTP isn't defined */ -#include - -#if !HAVE_DECL_IPPORT_TFTP && !defined(IPPORT_TFTP) +#ifndef HAVE_IPPORT_TFTP_DEFINITION +#ifndef IPPORT_TFTP #define IPPORT_TFTP 69 #endif - -/* arpa/{inet,tftp}.h, and possible missing pieces */ - -#ifdef HAVE_ARPA_INET_H -#include -#endif -/* If we don't have arpa/tftp.h we have problems... */ -#include - -#ifndef OACK -#define OACK 6 -#endif -#ifndef EOPTNEG -#define EOPTNEG 8 #endif -/* Prototypes for libxtra functions */ - -void *xmalloc(size_t); -char *xstrdup(const char *); - -#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 -#ifndef HAVE_DAEMON -int daemon(int, int); -#endif - -#ifndef HAVE_GETADDRINFO -#ifndef HAVE_STRUCT_ADDRINFO -struct addrinfo { - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - size_t ai_addrlen; - struct sockaddr *ai_addr; - char *ai_canonname; - struct addrinfo *ai_next; -}; -#endif -int getaddrinfo(const char *, const char *, const struct addrinfo *, - struct addrinfo **); -void freeaddrinfo(struct addrinfo *); -const char *gai_strerror(int); - - -#ifndef EAI_NONAME -#define EAI_NONAME -2 /* NAME or SERVICE is unknown. */ -#endif -#ifndef EAI_ADDRFAMILY -#define EAI_ADDRFAMILY -9 /* Address family for NAME not supported. */ -#endif -#ifndef EAI_MEMORY -#define EAI_MEMORY -10 /* Memory allocation failure. */ -#endif -#ifndef EAI_SYSTEM -#define EAI_SYSTEM -11 /* System error returned in `errno'. */ -#endif -#endif - -#ifndef AI_CANONNAME -#define AI_CANONNAME 0 -#endif - -#ifndef AI_ADDRCONFIG -#define AI_ADDRCONFIG 0 -#endif - -#ifndef INET6_ADDRSTRLEN -#define INET6_ADDRSTRLEN 46 -#endif - -#ifndef HAVE_INET_NTOP -const char *inet_ntop(int, const void *, char *, socklen_t); -#endif - -/* tftp-hpa version and configuration strings */ - -#include "version.h" - -#ifdef WITH_READLINE -#define WITH_READLINE_STR ", with readline" -#else -#define WITH_READLINE_STR ", without readline" -#endif - -#ifdef WITH_REGEX -#define WITH_REGEX_STR ", with remap" -#else -#define WITH_REGEX_STR ", without remap" -#endif - -#ifdef HAVE_LIBWRAP -#define HAVE_LIBWRAP_STR ", with tcpwrappers" -#else -#define HAVE_LIBWRAP_STR ", without tcpwrappers" -#endif - -#define TFTP_CONFIG_STR VERSION WITH_READLINE_STR -#define TFTPD_CONFIG_STR VERSION WITH_REGEX_STR HAVE_LIBWRAP_STR - #endif diff --git a/configure.ac b/configure.ac deleted file mode 100644 index deaa4fc..0000000 --- a/configure.ac +++ /dev/null @@ -1,283 +0,0 @@ -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 new file mode 100644 index 0000000..e76decf --- /dev/null +++ b/configure.in @@ -0,0 +1,141 @@ +dnl +dnl autoconf input file to generate MCONFIG +dnl + +AC_PREREQ(2.52) +AC_REVISION([$Id$]) +AC_INIT(MCONFIG.in) +AC_PREFIX_DEFAULT(/usr) + +AC_ISC_POSIX +AC_AIX +AC_MINIX +AC_PROG_CC +AC_C_CONST +AC_C_INLINE + +AC_SYS_LARGEFILE + +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) + +AC_HEADER_STDC +AC_CHECK_HEADERS(inttypes.h) +AC_CHECK_HEADERS(libgen.h) +AC_CHECK_HEADERS(memory.h) +AC_CHECK_HEADERS(stddef.h) +AC_CHECK_HEADERS(stdint.h) +AC_CHECK_HEADERS(stdlib.h) +AC_CHECK_HEADERS(string.h) +AC_CHECK_HEADERS(strings.h) +AC_CHECK_HEADERS(sysexits.h) +AC_CHECK_HEADERS(unistd.h) +AC_CHECK_HEADERS(sys/filio.h) +AC_CHECK_HEADERS(sys/stat.h) +AC_CHECK_HEADERS(sys/types.h) +dnl This is needed on some versions of FreeBSD... +AC_CHECK_HEADERS(machine/param.h) + +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_TYPE_PID_T + +AC_SEARCH_LIBS(socket, socket, , [AC_MSG_ERROR(socket library not found)]) +AC_SEARCH_LIBS(gethostbyname, [nsl resolv], , [AC_MSG_ERROR(gethostbyname not found)]) +AC_SEARCH_LIBS(inet_aton, [nsl resolv], , [AC_MSG_ERROR(inet_aton not found)]) +AC_SEARCH_LIBS(herror, [nsl resolv], , [AC_MSG_ERROR(herror not found)]) + +AC_CHECK_FUNCS(setsid) +AC_CHECK_FUNCS(recvmsg) +AC_CHECK_FUNCS(setreuid) +AC_CHECK_FUNCS(setregid) +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 + + +AH_TEMPLATE([HAVE_IPPORT_TFTP_DEFINITION], +[Define if netinet/in.h defines IPPORT_TFTP.]) +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.o $TFTPDOBJS" + ]) + ]) +],:) + +AH_TEMPLATE([WITH_READLINE], +[Define if we are compiling with readline command-line editing.]) + +PA_WITH_BOOL(readline, 1, +[ --without-readline disable the use of readline command-line editing], +[ + USE_READLINE=true + AC_CHECK_HEADER(readline/readline.h, [], [USE_READLINE=false]) + AC_CHECK_HEADER(readline/history.h, [], [USE_READLINE=false]) + if $USE_READLINE + then + AC_SEARCH_LIBS(readline, [readline history], + [ + AC_DEFINE(WITH_READLINE) + ]) + fi +],:) + +AH_TEMPLATE([HAVE_SIGSETJMP], +[Define if we have sigsetjmp, siglongjmp and sigjmp_buf.]) +PA_SIGSETJMP([AC_DEFINE(HAVE_SIGSETJMP)]) + +LIBXTRA=false +AC_SEARCH_LIBS(xmalloc, iberty, , LIBXTRA=true LIBOBJS="$LIBOBJS xmalloc.o") +AC_SEARCH_LIBS(xstrdup, iberty, , LIBXTRA=true LIBOBJS="$LIBOBJS xstrdup.o") +AC_SEARCH_LIBS(bsd_signal, bsd, , LIBXTRA=true LIBOBJS="$LIBOBJS bsdsignal.o") +if $LIBXTRA; then + LIBS="../lib/libxtra.a $LIBS" +fi + +AC_SUBST(LIBOBJS) +AC_SUBST(TFTPDOBJS) + +AC_PROG_RANLIB +AC_PROG_INSTALL + +AC_CONFIG_HEADER(acconfig.h) +AC_OUTPUT(MCONFIG) diff --git a/lib/Makefile b/lib/Makefile index a7fd057..4e20ad3 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -4,19 +4,15 @@ SRCROOT = .. --include ../config/MCONFIG +-include ../MCONFIG include ../MRULES -ifeq ($(LIBOBJS),) -all: -else all: libxtra.a -endif install: clean: - -rm -f *.a *.o *.obj *.exe + -rm -f *.a *.o distclean: clean -rm -f *~ @@ -25,3 +21,5 @@ libxtra.a: $(LIBOBJS) -rm -f libxtra.a $(AR) libxtra.a $(LIBOBJS) $(RANLIB) libxtra.a + + diff --git a/lib/bsdsignal.c b/lib/bsdsignal.c new file mode 100644 index 0000000..bc3ceb5 --- /dev/null +++ b/lib/bsdsignal.c @@ -0,0 +1,32 @@ +/* + * bsdsignal.c + * + * Use sigaction() to simulate BSD signal() + */ + +#include +#include +#include + +void (*bsd_signal(int, void (*)(int)))(int); + +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/lib/daemon.c b/lib/daemon.c deleted file mode 100644 index 0eb39c9..0000000 --- a/lib/daemon.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * daemon.c - "daemonize" a process - */ - -#include "config.h" - -int daemon(int nochdir, int noclose) -{ - int nullfd; - pid_t f; - - if (!nochdir) { - if (chdir("/")) - return -1; - } - - if (!noclose) { - if ((nullfd = open("/dev/null", O_RDWR)) < 0 || - dup2(nullfd, 0) < 0 || - dup2(nullfd, 1) < 0 || dup2(nullfd, 2) < 0) - return -1; - close(nullfd); - } - - f = fork(); - if (f < 0) - return -1; - else if (f > 0) - _exit(0); - -#ifdef HAVE_SETSID - return setsid(); -#else - return 0; -#endif -} diff --git a/lib/dup2.c b/lib/dup2.c deleted file mode 100644 index bba45c4..0000000 --- a/lib/dup2.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * dup2.c - * - * Ersatz dup2() for really ancient systems - */ - -#include "config.h" - -int dup2(int oldfd, int newfd) -{ - int rv, nfd; - - close(newfd); - - nfd = rv = dup(oldfd); - - if (rv >= 0 && rv != newfd) { - rv = dup2(oldfd, newfd); - close(nfd); - } - - return rv; -} diff --git a/lib/getaddrinfo.c b/lib/getaddrinfo.c deleted file mode 100644 index ef7c9ae..0000000 --- a/lib/getaddrinfo.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * getaddrinfo.c - * - * Simple version of getaddrinfo() - * - */ - -#include "config.h" - -extern int errno; -extern int h_errno; - -void freeaddrinfo(struct addrinfo *res) -{ - if (!res) - return; - if (res->ai_next) - freeaddrinfo(res->ai_next); - if (res->ai_addr) - free(res->ai_addr); - if (res->ai_canonname) - free(res->ai_canonname); - free(res); -} - -int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, - struct addrinfo **res) -{ - struct hostent *host; - struct sockaddr *sa; - int err, size = 0; - - if ((!node) || (!res)) { - errno = EINVAL; - return EAI_SYSTEM; - } - *res = NULL; - /* we do not support service in this version */ - if (service) { - errno = EINVAL; - return EAI_SYSTEM; - } - host = gethostbyname(node); - if (!host) - return EAI_NONAME; - if (hints) { - if (hints->ai_family != AF_UNSPEC) { - if (hints->ai_family != host->h_addrtype) - return EAI_ADDRFAMILY; - } - } - *res = malloc(sizeof(struct addrinfo)); - if (!*res) { - return EAI_MEMORY; - } - memset(*res, 0, sizeof(struct addrinfo)); - (*res)->ai_family = host->h_addrtype; - if (host->h_length) { - if (host->h_addrtype == AF_INET) - size = sizeof(struct sockaddr_in); -#ifdef HAVE_IPV6 - else if (host->h_addrtype == AF_INET6) - size = sizeof(struct sockaddr_in6); -#endif - else { - free(*res); - *res = NULL; - return EAI_ADDRFAMILY; - } - sa = malloc(size); - if (!sa) { - free(*res); - *res = NULL; - return EAI_MEMORY; - } - memset(sa, 0, size); - (*res)->ai_addr = sa; - (*res)->ai_addrlen = size; - sa->sa_family = host->h_addrtype; - if (host->h_addrtype == AF_INET) - memcpy(&((struct sockaddr_in *)sa)->sin_addr, host->h_addr, host->h_length); -#ifdef HAVE_IPV6 - else - memcpy(&((struct sockaddr_in6 *)sa)->sin6_addr, host->h_addr, host->h_length); -#endif - } - if (host->h_name) - (*res)->ai_canonname = strdup(host->h_name); - - /* we only handle the first address entry and do not build a list now */ - return 0; -} - - - -const char *gai_strerror(int errcode) -{ - const char *s = NULL; - - switch(errcode) { - case 0: - s = "no error"; - break; - case EAI_MEMORY: - s = "no memory"; - break; - case EAI_SYSTEM: - s = strerror(errno); - break; - case EAI_NONAME: - s = hstrerror(h_errno); - break; - case EAI_ADDRFAMILY: - s = "address does not match address family"; - break; - default: - s = "unknown error code"; - break; - } - return s; -} diff --git a/lib/getopt.h b/lib/getopt.h deleted file mode 100644 index c1ad561..0000000 --- a/lib/getopt.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef LIB_GETOPT_H -#define LIB_GETOPT_H - -extern char *optarg; -extern int optind, opterr, optopt; - -struct option { - const char *name; - int has_arg; - int *flag; - int val; -}; - -enum { - no_argument = 0, - required_argument = 1, - optional_argument = 2, -}; - -int getopt_long(int, char *const *, const char *, - const struct option *, int *); - -#endif /* LIB_GETOPT_H */ diff --git a/lib/getopt_long.c b/lib/getopt_long.c deleted file mode 100644 index 49d1274..0000000 --- a/lib/getopt_long.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * getopt_long.c - * - * getopt_long(), or at least a common subset thereof: - * - * - Option reordering is not supported - * - -W foo is not supported - * - First optstring character "-" not supported. - */ - -#include "config.h" - -char *optarg; -int optind, opterr, optopt; - -static struct getopt_private_state { - const char *optptr; - const char *last_optstring; - char *const *last_argv; -} pvt; - -static inline const char *option_matches(const char *arg_str, - const char *opt_name) -{ - while (*arg_str != '\0' && *arg_str != '=') { - if (*arg_str++ != *opt_name++) - return NULL; - } - - if (*opt_name) - return NULL; - - return arg_str; -} - -int getopt_long(int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longindex) -{ - const char *carg; - const char *osptr; - int opt; - - /* getopt() relies on a number of different global state - variables, which can make this really confusing if there is - more than one use of getopt() in the same program. This - attempts to detect that situation by detecting if the - "optstring" or "argv" argument have changed since last time - we were called; if so, reinitialize the query state. */ - - if (optstring != pvt.last_optstring || argv != pvt.last_argv || - optind < 1 || optind > argc) { - /* optind doesn't match the current query */ - pvt.last_optstring = optstring; - pvt.last_argv = argv; - optind = 1; - pvt.optptr = NULL; - } - - carg = argv[optind]; - - /* First, eliminate all non-option cases */ - - if (!carg || carg[0] != '-' || !carg[1]) - return -1; - - if (carg[1] == '-') { - const struct option *lo; - const char *opt_end = NULL; - - optind++; - - /* Either it's a long option, or it's -- */ - if (!carg[2]) { - /* It's -- */ - return -1; - } - - for (lo = longopts; lo->name; lo++) { - if ((opt_end = option_matches(carg+2, lo->name))) - break; - } - if (!opt_end) - return '?'; - - if (longindex) - *longindex = lo-longopts; - - if (*opt_end == '=') { - if (lo->has_arg) - optarg = (char *)opt_end+1; - else - return '?'; - } else if (lo->has_arg == 1) { - if (!(optarg = argv[optind])) - return '?'; - optind++; - } - - if (lo->flag) { - *lo->flag = lo->val; - return 0; - } else { - return lo->val; - } - } - - if ((uintptr_t) (pvt.optptr - carg) > (uintptr_t) strlen(carg)) { - /* Someone frobbed optind, change to new opt. */ - pvt.optptr = carg + 1; - } - - opt = *pvt.optptr++; - - if (opt != ':' && (osptr = strchr(optstring, opt))) { - if (osptr[1] == ':') { - if (*pvt.optptr) { - /* Argument-taking option with attached - argument */ - optarg = (char *)pvt.optptr; - optind++; - } else { - /* Argument-taking option with non-attached - argument */ - if (argv[optind + 1]) { - optarg = (char *)argv[optind+1]; - optind += 2; - } else { - /* Missing argument */ - optind++; - return (optstring[0] == ':') - ? ':' : '?'; - } - } - return opt; - } else { - /* Non-argument-taking option */ - /* pvt.optptr will remember the exact position to - resume at */ - if (!*pvt.optptr) - optind++; - return opt; - } - } else { - /* Unknown option */ - optopt = opt; - if (!*pvt.optptr) - optind++; - return '?'; - } -} diff --git a/lib/inet_ntop.c b/lib/inet_ntop.c deleted file mode 100644 index fe8e560..0000000 --- a/lib/inet_ntop.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * inet_ntop.c - * - * Simple version of inet_ntop() - * - */ - -#include "config.h" - -extern int errno; - -const char *inet_ntop(int af, const void *src, - char *dst, socklen_t cnt) -{ - char *p; - - switch(af) { - case AF_INET: - p = inet_ntoa(*((struct in_addr *)src)); - if (p) { - if (cnt <= strlen(p)) { - errno = ENOSPC; - dst = NULL; - } else - strcpy(dst, p); - } else - dst = NULL; - break; -#ifdef HAVE_IPV6 - case AF_INET6: - if (cnt < 40) { - errno = ENOSPC; - dst = NULL; - } else { - struct in6_addr *a = src; - int i; - - p = (char *)dst; - /* we do not compress :0: to :: */ - for (i = 0; i < 8; i++) - p += sprintf(p, "%x:", ntohs(a->s6_addr16[i])); - p--; - *p = 0; - } - break; -#endif - default: - errno = EAFNOSUPPORT; - dst = NULL; - } - return dst; -} diff --git a/lib/xmalloc.c b/lib/xmalloc.c index 30704f3..6e8ae24 100644 --- a/lib/xmalloc.c +++ b/lib/xmalloc.c @@ -5,16 +5,17 @@ * */ -#include "config.h" +#include +#include void *xmalloc(size_t size) { - void *p = malloc(size); + void *p = malloc(size); - if (!p) { - fprintf(stderr, "Out of memory!\n"); - exit(128); - } + if ( !p ) { + fprintf(stderr, "Out of memory!\n"); + exit(128); + } - return p; + return p; } diff --git a/lib/xstrdup.c b/lib/xstrdup.c index 05e3054..5d65b7e 100644 --- a/lib/xstrdup.c +++ b/lib/xstrdup.c @@ -5,16 +5,18 @@ * */ -#include "config.h" +#include +#include +#include char *xstrdup(const char *s) { - char *p = strdup(s); + char *p = strdup(s); - if (!p) { - fprintf(stderr, "Out of memory!\n"); - exit(128); - } + if ( !p ) { + fprintf(stderr, "Out of memory!\n"); + exit(128); + } - return p; + return p; } diff --git a/release.sh b/release.sh deleted file mode 100755 index 1f72922..0000000 --- a/release.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/sh -xe -# -# Script for generating a release -# - -PACKAGE=tftp-hpa - -if [ -z "$1" ]; then - echo "Usage: $0 release-id" 1>&2 - exit 1 -fi - -release="$1" -releasetag=$PACKAGE-$release -releasedir=$PACKAGE-$release - -GIT_DIR=`cd "${GIT_DIR-.git}" && pwd` -export GIT_DIR - -if [ `git diff --cached | wc -l` -ne 0 ]; then - echo "$0: index not clean" 1>&2 - exit 1 -fi - -if [ x"$release" = x'test' ]; then - release=`cat version` - releasetag=HEAD - releasedir=$PACKAGE-$release -else - echo $release > version - if [ `git diff version | wc -l` -ne 0 ]; then - git add version - git commit -m "Update version for release $release" version - else - git checkout version - fi - rm -f "$GIT_DIR"/refs/tags/$releasetag - git tag -a -m "$releasetag" -f "$releasetag" -fi - -here=`pwd` - -tmpdir=/var/tmp/release.$$ -rm -rf $tmpdir -mkdir -p $tmpdir -cd $tmpdir -mkdir -p $releasedir -git archive --format=tar $releasetag | tar -xf - -C $releasedir -cd $releasedir -make release -rm -f release.sh -cd .. -tar cvvf $releasedir.tar $releasedir -gzip -9 $releasedir.tar -mv -f $releasedir.tar.gz $here/.. -cd .. -rm -rf $tmpdir diff --git a/tftp-xinetd b/tftp-xinetd deleted file mode 100644 index 982fe09..0000000 --- a/tftp-xinetd +++ /dev/null @@ -1,18 +0,0 @@ -# default: off -# description: The tftp server serves files using the trivial file transfer \ -# protocol. The tftp protocol is often used to boot diskless \ -# workstations, download configuration files to network-aware printers, \ -# and to start the installation process for some operating systems. -service tftp -{ - socket_type = dgram - protocol = udp - wait = yes - user = root - server = /usr/sbin/in.tftpd - server_args = -s /tftpboot - disable = yes - per_source = 11 - cps = 100 2 - flags = IPv4 -} diff --git a/tftp.spec.in b/tftp.spec.in deleted file mode 100644 index cd5e4cc..0000000 --- a/tftp.spec.in +++ /dev/null @@ -1,228 +0,0 @@ -Summary: The client for the Trivial File Transfer Protocol (TFTP). -Name: tftp -Version: @@VERSION@@ -Release: 1 -License: BSD -Group: Applications/Internet -Source0: http://www.kernel.org/pub/software/network/tftp/tftp-hpa-%{version}.tar.gz -BuildRequires: tcp_wrappers-devel -BuildRoot: %{_tmppath}/%{name}-root - -%description -The Trivial File Transfer Protocol (TFTP) is normally used only for -booting diskless workstations. The tftp package provides the user -interface for TFTP, which allows users to transfer files to and from a -remote machine. This program and TFTP provide very little security, -and should not be enabled unless it is expressly needed. - -%package server -Group: System Environment/Daemons -Summary: The server for the Trivial File Transfer Protocol (TFTP). -Requires: xinetd - -%description server -The Trivial File Transfer Protocol (TFTP) is normally used only for -booting diskless workstations. The tftp-server package provides the -server for TFTP, which allows users to transfer files to and from a -remote machine. TFTP provides very little security, and should not be -enabled unless it is expressly needed. The TFTP server is run from -/etc/xinetd.d/tftp, and is disabled by default on Red Hat Linux systems. - -%prep -%setup -q -n tftp-hpa-%{version} - -%build - -%configure -make %{?_smp_mflags} - -%install -rm -rf ${RPM_BUILD_ROOT} -mkdir -p ${RPM_BUILD_ROOT}%{_bindir} -mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man{1,8} -mkdir -p ${RPM_BUILD_ROOT}%{_sbindir} - -make INSTALLROOT=${RPM_BUILD_ROOT} \ - SBINDIR=%{_sbindir} MANDIR=%{_mandir} \ - install -install -m755 -d ${RPM_BUILD_ROOT}%{_sysconfdir}/xinetd.d/ ${RPM_BUILD_ROOT}/tftpboot -install -m644 tftp-xinetd ${RPM_BUILD_ROOT}%{_sysconfdir}/xinetd.d/tftp - -%post server -/sbin/service xinetd reload > /dev/null 2>&1 || : - -%postun server -if [ $1 = 0 ]; then - /sbin/service xinetd reload > /dev/null 2>&1 || : -fi - -%clean -rm -rf ${RPM_BUILD_ROOT} - -%files -%defattr(-,root,root) -%{_bindir}/tftp -%{_mandir}/man1/* - -%files server -%defattr(-,root,root) -%config(noreplace) %{_sysconfdir}/xinetd.d/tftp -%dir /tftpboot -%{_sbindir}/in.tftpd -%{_mandir}/man8/* - -%changelog -* Tue Sep 14 2004 H. Peter Anvin -- removed completely broken "Malta" patch. -- integrated into build machinery so rpm -ta works. - -* Fri Feb 13 2004 Elliot Lee -- rebuilt - -* Wed Jun 04 2003 Elliot Lee -- rebuilt - -* Fri Apr 11 2003 Elliot Lee -- 0.33 -- Add /tftpboot directory (#88204) - -* Mon Feb 24 2003 Elliot Lee -- rebuilt - -* Sun Feb 23 2003 Tim Powers -- add BuildPreReq on tcp_wrappers - -* Wed Jan 22 2003 Tim Powers -- rebuilt - -* Mon Nov 11 2002 Elliot Lee 0.32-1 -- Update to 0.32 - -* Wed Oct 23 2002 Elliot Lee 0.30-1 -- Fix #55789 -- Update to 0.30 - -* Thu Jun 27 2002 Elliot Lee -- Try applying HJ's patch from #65476 - -* Fri Jun 21 2002 Tim Powers -- automated rebuild - -* Mon Jun 17 2002 Elliot Lee -- Update to 0.29 - -* Thu May 23 2002 Tim Powers -- automated rebuild - -* Wed Jan 09 2002 Tim Powers -- automated rebuild - -* Tue Dec 18 2001 Elliot Lee 0.17-15 -- Add patch4: netkit-tftp-0.17-defaultport.patch for bug #57562 -- Update to tftp-hpa-0.28 (bug #56131) -- Remove include/arpa/tftp.h to fix #57259 -- Add resource limits in tftp-xinetd (#56722) - -* Sun Jun 24 2001 Elliot Lee -- Bump release + rebuild. - -* Tue Jun 12 2001 Helge Deller (0.17-13) -- updated tftp-hpa source to tftp-hpa-0.17 -- tweaked specfile with different defines for tftp-netkit and tftp-hpa version -- use hpa's tftpd.8 man page instead of the netkits one - -* Mon May 07 2001 Helge Deller -- rebuilt in 7.1.x - -* Wed Apr 18 2001 Helge Deller -- fix tftp client's put problems (#29529) -- update to tftp-hpa-0.16 - -* Wed Apr 4 2001 Jakub Jelinek -- don't let configure to guess compiler, it can pick up egcs - -* Thu Feb 08 2001 Helge Deller -- changed "wait" in xinetd file to "yes" (hpa-tftpd forks and exits) (#26467) -- fixed hpa-tftpd to handle files greater than 32MB (#23725) -- added "-l" flag to hpa-tftpd for file-logging (#26467) -- added description for "-l" to the man-page - -* Thu Feb 08 2001 Helge Deller -- updated tftp client to 0.17 stable (#19640), -- drop dependency on xinetd for tftp client (#25051), - -* Wed Jan 17 2001 Jeff Johnson -- xinetd shouldn't wait on tftp (which forks) (#23923). - -* Sat Jan 6 2001 Jeff Johnson -- fix to permit tftp put's (#18128). -- startup as root with chroot to /tftpboot with early reversion to nobody - is preferable to starting as nobody w/o ability to chroot. -- %%post is needed by server, not client. Add %%postun for erasure as well. - -* Wed Aug 23 2000 Nalin Dahyabhai -- default to being disabled - -* Thu Aug 17 2000 Jeff Johnson -- correct group. - -* Tue Jul 25 2000 Nalin Dahyabhai -- change user from root to nobody - -* Sat Jul 22 2000 Jeff Johnson -- update to tftp-hpa-0.14 (#14003). -- add server_args (#14003). -- remove -D_BSD_SOURCE (#14003). - -* Fri Jul 21 2000 Nalin Dahyabhai -- cook up an xinetd config file for tftpd - -* Wed Jul 12 2000 Prospector -- automatic rebuild - -* Sun Jun 18 2000 Jeff Johnson -- FHS packaging. -- update to 0.17. - -* Fri May 5 2000 Matt Wilson -- use _BSD_SOURCE for hpa's tftpd so we get BSD signal semantics. - -* Fri Feb 11 2000 Bill Nottingham -- fix description - -* Wed Feb 9 2000 Jeff Johnson -- compress man pages (again). - -* Wed Feb 02 2000 Cristian Gafton -- man pages are compressed -- fix description and summary - -* Tue Jan 4 2000 Bill Nottingham -- split client and server - -* Tue Dec 21 1999 Jeff Johnson -- update to 0.16. - -* Sat Aug 28 1999 Jeff Johnson -- update to 0.15. - -* Wed Apr 7 1999 Jeff Johnson -- tftpd should truncate file when overwriting (#412) - -* Sun Mar 21 1999 Cristian Gafton -- auto rebuild in the new build environment (release 22) - -* Mon Mar 15 1999 Jeff Johnson -- compile for 6.0. - -* Fri Aug 7 1998 Jeff Johnson -- build root - -* Mon Apr 27 1998 Prospector System -- translations modified for de, fr, tr - -* Mon Sep 22 1997 Erik Troan -- added check for getpwnam() failure - -* Tue Jul 15 1997 Erik Troan -- initial build diff --git a/tftp/Makefile b/tftp/Makefile index 9faa62f..ca91196 100644 --- a/tftp/Makefile +++ b/tftp/Makefile @@ -1,28 +1,24 @@ -SRCROOT = .. -VERSION = $(shell cat ../version) +all: tftp --include ../config/MCONFIG +SRCROOT = .. + +-include ../MCONFIG include ../MRULES -OBJS = tftp.$(O) main.$(O) +OBJS = tftp.o main.o tftpsubs.o -all: tftp$(X) tftp.1 +tftp: $(OBJS) + $(CC) $(LDFLAGS) $^ $(LIBS) -o $@ -tftp$(X): $(OBJS) - $(CC) $(LDFLAGS) $^ $(TFTP_LIBS) -o $@ +$(OBJS): tftpsubs.h -$(OBJS): ../common/tftpsubs.h - -tftp.1: tftp.1.in ../version - sed -e 's/@@VERSION@@/$(VERSION)/g' < $< > $@ - -install: all +install: tftp mkdir -p $(INSTALLROOT)$(BINDIR) $(INSTALLROOT)$(MANDIR)/man1 - $(INSTALL_PROGRAM) tftp$(X) $(INSTALLROOT)$(BINDIR) - $(INSTALL_DATA) tftp.1 $(INSTALLROOT)$(MANDIR)/man1 + $(INSTALL_PROGRAM) -s tftp $(INSTALLROOT)$(BINDIR) + $(INSTALL_DATA) tftp.1 $(INSTALLROOT)$(MANDIR)/man1 clean: - rm -f *.o *.obj *.exe tftp tftp.1 + rm -f *.o tftp distclean: clean rm -f *~ diff --git a/tftp/extern.h b/tftp/extern.h index 9c578e6..60ad0e9 100644 --- a/tftp/extern.h +++ b/tftp/extern.h @@ -1,3 +1,8 @@ +/* $Id$ */ + +/* $OpenBSD: extern.h,v 1.2 1996/06/26 05:40:33 deraadt Exp $ */ +/* $NetBSD: extern.h,v 1.2 1994/12/08 09:51:24 jtc Exp $ */ + /* * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. @@ -29,15 +34,14 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 */ -#ifndef EXTERN_H -#define EXTERN_H +#ifndef RECVFILE_H +#define RECVFILE_H -#include "config.h" - -void tftp_recvfile(int, const char *, const char *); -void tftp_sendfile(int, const char *, const char *); -extern sigjmp_buf toplevel; +void tftp_recvfile (int, char *, char *); +void tftp_sendfile (int, char *, char *); #endif diff --git a/tftp/main.c b/tftp/main.c index ecdc0e0..7ff7d2e 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -1,3 +1,6 @@ +/* $OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $ */ +/* $NetBSD: main.c,v 1.6 1995/05/21 16:54:10 mycroft Exp $ */ + /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. @@ -31,916 +34,745 @@ * SUCH DAMAGE. */ -#include "common/tftpsubs.h" +#include "tftpsubs.h" + +#ifndef lint +static const char *copyright UNUSED = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +/* static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; */ +/* static char rcsid[] = "$OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $"; */ +static const char *rcsid UNUSED = + "tftp-hpa $Id$"; +#endif /* not lint */ /* Many bug fixes are from Jim Guyton */ /* * TFTP User Program -- Command Interface. */ +#include +#include #include + +#include + +#include + #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #ifdef WITH_READLINE #include -#ifdef HAVE_READLINE_HISTORY_H #include #endif -#endif #include "extern.h" -#define TIMEOUT 5 /* secs between rexmt's */ -#define LBUFLEN 200 /* size of input buffer */ +#define TIMEOUT 5 /* secs between rexmt's */ +#define LBUFLEN 200 /* size of input buffer */ -struct modes { - const char *m_name; - const char *m_mode; - int m_openflags; -}; - -static const struct modes modes[] = { - {"netascii", "netascii", O_TEXT}, - {"ascii", "netascii", O_TEXT}, - {"octet", "octet", O_BINARY}, - {"binary", "octet", O_BINARY}, - {"image", "octet", O_BINARY}, - {0, 0, 0} -}; - -#define MODE_OCTET (&modes[2]) -#define MODE_NETASCII (&modes[0]) -#define MODE_DEFAULT MODE_NETASCII - -#ifdef HAVE_IPV6 -int ai_fam = AF_UNSPEC; -int ai_fam_sock = AF_UNSPEC; -#else -int ai_fam = AF_INET; -int ai_fam_sock = AF_INET; -#endif - -union sock_addr peeraddr; -int f = -1; -u_short port; -int trace; -int verbose; -int literal; -int connected; -const struct modes *mode; +struct sockaddr_in peeraddr; +int f; +short port; +int trace; +int verbose; +int connected; +char mode[32]; #ifdef WITH_READLINE -char *line = NULL; +char *line = NULL; #else -char line[LBUFLEN]; +char line[LBUFLEN]; #endif -int margc; -char *margv[20]; +int margc; +char *margv[20]; const char *prompt = "tftp> "; -sigjmp_buf toplevel; -void intr(int); -struct servent *sp; -int portrange = 0; -unsigned int portrange_from = 0; -unsigned int portrange_to = 0; +sigjmp_buf toplevel; +void intr(int); +struct servent *sp; -void get(int, char **); -void help(int, char **); -void modecmd(int, char **); -void put(int, char **); -void quit(int, char **); -void setascii(int, char **); -void setbinary(int, char **); -void setpeer(int, char **); -void setrexmt(int, char **); -void settimeout(int, char **); -void settrace(int, char **); -void setverbose(int, char **); -void status(int, char **); -void setliteral(int, char **); +void get (int, char **); +void help (int, char **); +void modecmd (int, char **); +void put (int, char **); +void quit (int, char **); +void setascii (int, char **); +void setbinary (int, char **); +void setpeer (int, char **); +void setrexmt (int, char **); +void settimeout (int, char **); +void settrace (int, char **); +void setverbose (int, char **); +void status (int, char **); -static void command(void); +static void command (void); -static void getusage(char *); -static void makeargv(void); -static void putusage(char *); -static void settftpmode(const struct modes *); +static void getusage (char *); +static void makeargv (void); +static void putusage (char *); +static void settftpmode (const char *); #define HELPINDENT (sizeof("connect")) struct cmd { - const char *name; - const char *help; - void (*handler) (int, char **); + const char *name; + const char *help; + void (*handler) (int, char **); }; struct cmd cmdtab[] = { - {"connect", - "connect to remote tftp", - setpeer}, - {"mode", - "set file transfer mode", - modecmd}, - {"put", - "send file", - put}, - {"get", - "receive file", - get}, - {"quit", - "exit tftp", - quit}, - {"verbose", - "toggle verbose mode", - setverbose}, - {"trace", - "toggle packet tracing", - settrace}, - {"literal", - "toggle literal mode, ignore ':' in file name", - setliteral}, - {"status", - "show current status", - status}, - {"binary", - "set mode to octet", - setbinary}, - {"ascii", - "set mode to netascii", - setascii}, - {"rexmt", - "set per-packet transmission timeout", - setrexmt}, - {"timeout", - "set total retransmission timeout", - settimeout}, - {"?", - "print help information", - help}, - {"help", - "print help information", - help}, - {0, 0, 0} + { "connect", + "connect to remote tftp", + setpeer }, + { "mode", + "set file transfer mode", + modecmd }, + { "put", + "send file", + put }, + { "get", + "receive file", + get }, + { "quit", + "exit tftp", + quit }, + { "verbose", + "toggle verbose mode", + setverbose }, + { "trace", + "toggle packet tracing", + settrace }, + { "status", + "show current status", + status }, + { "binary", + "set mode to octet", + setbinary }, + { "ascii", + "set mode to netascii", + setascii }, + { "rexmt", + "set per-packet transmission timeout", + setrexmt }, + { "timeout", + "set total retransmission timeout", + settimeout }, + { "?", + "print help information", + help }, + { "help", + "print help information", + help }, + { 0, 0, 0 } }; -struct cmd *getcmd(char *); -char *tail(char *); +struct cmd *getcmd(char *); +char *tail(char *); char *xstrdup(const char *); -const char *program; - -static void usage(int errcode) +int +main(int argc, char *argv[]) { - fprintf(stderr, -#ifdef HAVE_IPV6 - "Usage: %s [-4][-6][-v][-l][-m mode] [host [port]] [-c command]\n", -#else - "Usage: %s [-v][-l][-m mode] [host [port]] [-c command]\n", -#endif - program); - exit(errcode); -} + struct sockaddr_in s_in; -int main(int argc, char *argv[]) -{ - union sock_addr sa; - int arg; - static int pargc, peerargc; - static int iscmd = 0; - char **pargv; - const char *optx; - char *peerargv[3]; + sp = getservbyname("tftp", "udp"); + if (sp == 0) { + fprintf(stderr, "tftp: udp/tftp: unknown service\n"); + exit(1); + } + f = socket(AF_INET, SOCK_DGRAM, 0); + if (f < 0) { + perror("tftp: socket"); + exit(3); + } + bzero((char *)&s_in, sizeof (s_in)); + s_in.sin_family = AF_INET; + if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { + perror("tftp: bind"); + exit(1); + } + strcpy(mode, "netascii"); + bsd_signal(SIGINT, intr); + if (argc > 1) { + if (sigsetjmp(toplevel,1) != 0) + exit(0); + setpeer(argc, argv); + } + if (sigsetjmp(toplevel,1) != 0) + (void)putchar('\n'); - program = argv[0]; - - mode = MODE_DEFAULT; - - peerargv[0] = argv[0]; - peerargc = 1; - - for (arg = 1; !iscmd && arg < argc; arg++) { - if (argv[arg][0] == '-') { - for (optx = &argv[arg][1]; *optx; optx++) { - switch (*optx) { - case '4': - ai_fam = AF_INET; - break; -#ifdef HAVE_IPV6 - case '6': - ai_fam = AF_INET6; - break; -#endif - case 'v': - verbose = 1; - break; - case 'V': - /* Print version and configuration to stdout and exit */ - printf("%s\n", TFTP_CONFIG_STR); - exit(0); - case 'l': - literal = 1; - break; - case 'm': - if (++arg >= argc) - usage(EX_USAGE); - { - const struct modes *p; - - for (p = modes; p->m_name; p++) { - if (!strcmp(argv[arg], p->m_name)) - break; - } - if (p->m_name) { - settftpmode(p); - } else { - fprintf(stderr, "%s: invalid mode: %s\n", - argv[0], argv[arg]); - exit(EX_USAGE); - } - } - break; - case 'c': - iscmd = 1; - break; - case 'R': - if (++arg >= argc) - usage(EX_USAGE); - if (sscanf - (argv[arg], "%u:%u", &portrange_from, - &portrange_to) != 2 - || portrange_from > portrange_to - || portrange_to > 65535) { - fprintf(stderr, "Bad port range: %s\n", argv[arg]); - exit(EX_USAGE); - } - portrange = 1; - break; - case 'h': - default: - usage(*optx == 'h' ? 0 : EX_USAGE); - } - } - } else { - if (peerargc >= 3) - usage(EX_USAGE); - - peerargv[peerargc++] = argv[arg]; - } - } - - ai_fam_sock = ai_fam; - - pargv = argv + arg; - pargc = argc - arg; - - sp = getservbyname("tftp", "udp"); - if (sp == 0) { - /* Use canned values */ - if (verbose) - fprintf(stderr, - "tftp: tftp/udp: unknown service, faking it...\n"); - sp = xmalloc(sizeof(struct servent)); - sp->s_name = (char *)"tftp"; - sp->s_aliases = NULL; - sp->s_port = htons(IPPORT_TFTP); - sp->s_proto = (char *)"udp"; - } - - tftp_signal(SIGINT, intr, 0); - - if (peerargc) { - /* Set peer */ - if (sigsetjmp(toplevel, 1) != 0) - exit(EX_NOHOST); - setpeer(peerargc, peerargv); - } - - if (ai_fam_sock == AF_UNSPEC) - ai_fam_sock = AF_INET; - - f = socket(ai_fam_sock, SOCK_DGRAM, 0); - if (f < 0) { - perror("tftp: socket"); - exit(EX_OSERR); - } - bzero(&sa, sizeof(sa)); - sa.sa.sa_family = ai_fam_sock; - if (pick_port_bind(f, &sa, portrange_from, portrange_to)) { - perror("tftp: bind"); - exit(EX_OSERR); - } - - if (iscmd && pargc) { - /* -c specified; execute command and exit */ - struct cmd *c; - - if (sigsetjmp(toplevel, 1) != 0) - exit(EX_UNAVAILABLE); - - c = getcmd(pargv[0]); - if (c == (struct cmd *)-1 || c == (struct cmd *)0) { - fprintf(stderr, "%s: invalid command: %s\n", argv[0], - pargv[1]); - exit(EX_USAGE); - } - (*c->handler) (pargc, pargv); - exit(0); - } #ifdef WITH_READLINE -#ifdef HAVE_READLINE_HISTORY_H - using_history(); -#endif + using_history(); #endif - if (sigsetjmp(toplevel, 1) != 0) - (void)putchar('\n'); - command(); + command(); - return 0; /* Never reached */ + return 0; /* Never reached */ } -char *hostname; +char *hostname; /* Called when a command is incomplete; modifies the global variable "line" */ -static void getmoreargs(const char *partial, const char *mprompt) +static void +getmoreargs(const char *partial, const char *mprompt) { #ifdef WITH_READLINE - char *eline; - int len, elen; + char *eline; + int len, elen; + + len = strlen(partial); + eline = readline(mprompt); + if (!eline) + exit(0); /* EOF */ + + elen = strlen(eline); - len = strlen(partial); - eline = readline(mprompt); - if (!eline) - exit(0); /* EOF */ + if (line) + free(line); + line = xmalloc(len+elen+1); + strcpy(line, partial); + strcpy(line+len, eline); - elen = strlen(eline); - - if (line) { - free(line); - line = NULL; - } - line = xmalloc(len + elen + 1); - strcpy(line, partial); - strcpy(line + len, eline); - free(eline); - -#ifdef HAVE_READLINE_HISTORY_H - add_history(line); -#endif + add_history(line); #else - int len = strlen(partial); - - strcpy(line, partial); - fputs(mprompt, stdout); - if (fgets(line + len, LBUFLEN - len, stdin) == 0) - if (feof(stdin)) - exit(0); /* EOF */ + int len = strlen(partial); + + strcpy(line, partial); + fputs(mprompt, stdout); + if ( fgets(line+len, LBUFLEN-len, stdin) == 0 ) + if ( feof(stdin) ) + exit(0); /* EOF */ #endif } -void setpeer(int argc, char *argv[]) +void +setpeer(int argc, char *argv[]) { - int err; + struct hostent *host; - if (argc < 2) { - getmoreargs("connect ", "(to) "); - makeargv(); - argc = margc; - argv = margv; - } - if ((argc < 2) || (argc > 3)) { - printf("usage: %s host-name [port]\n", argv[0]); - return; - } - - peeraddr.sa.sa_family = ai_fam; - err = set_sock_addr(argv[1], &peeraddr, &hostname); - if (err) { - printf("Error: %s\n", gai_strerror(err)); - printf("%s: unknown host\n", argv[1]); - connected = 0; - return; - } - ai_fam = peeraddr.sa.sa_family; - if (f == -1) { /* socket not open */ - ai_fam_sock = ai_fam; - } else { /* socket was already open */ - if (ai_fam_sock != ai_fam) { /* need reopen socken for new family */ - union sock_addr sa; - - close(f); - ai_fam_sock = ai_fam; - f = socket(ai_fam_sock, SOCK_DGRAM, 0); - if (f < 0) { - perror("tftp: socket"); - exit(EX_OSERR); - } - bzero((char *)&sa, sizeof (sa)); - sa.sa.sa_family = ai_fam_sock; - if (pick_port_bind(f, &sa, portrange_from, portrange_to)) { - perror("tftp: bind"); - exit(EX_OSERR); - } - } - } - port = sp->s_port; - if (argc == 3) { - struct servent *usp; - usp = getservbyname(argv[2], "udp"); - if (usp) { - port = usp->s_port; - } else { - unsigned long myport; - char *ep; - myport = strtoul(argv[2], &ep, 10); - if (*ep || myport > 65535UL) { - printf("%s: bad port number\n", argv[2]); - connected = 0; - return; - } - port = htons((u_short) myport); - } - } - - if (verbose) { - char tmp[INET6_ADDRSTRLEN], *tp; - tp = (char *)inet_ntop(peeraddr.sa.sa_family, SOCKADDR_P(&peeraddr), - tmp, INET6_ADDRSTRLEN); - if (!tp) - tp = (char *)"???"; - printf("Connected to %s (%s), port %u\n", - hostname, tp, (unsigned int)ntohs(port)); - } - connected = 1; + if (argc < 2) { + getmoreargs("connect ", "(to) "); + makeargv(); + argc = margc; + argv = margv; + } + if ((argc < 2) || (argc > 3)) { + printf("usage: %s host-name [port]\n", argv[0]); + return; + } + if (inet_aton(argv[1], &peeraddr.sin_addr) != 0) { + peeraddr.sin_family = AF_INET; + hostname = xstrdup(argv[1]); + } else { + host = gethostbyname(argv[1]); + if (host == 0) { + connected = 0; + printf("%s: unknown host\n", argv[1]); + return; + } + peeraddr.sin_family = host->h_addrtype; + bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length); + hostname = xstrdup(host->h_name); + } + port = sp->s_port; + if (argc == 3) { + port = atoi(argv[2]); + if (port < 0) { + printf("%s: bad port number\n", argv[2]); + connected = 0; + return; + } + port = htons(port); + } + connected = 1; } -void modecmd(int argc, char *argv[]) +struct modes { + const char *m_name; + const char *m_mode; +} modes[] = { + { "ascii", "netascii" }, + { "netascii", "netascii" }, + { "binary", "octet" }, + { "image", "octet" }, + { "octet", "octet" }, +/* { "mail", "mail" }, */ + { 0, 0 } +}; + +void +modecmd(int argc, char *argv[]) { - const struct modes *p; - const char *sep; + struct modes *p; + const char *sep; - if (argc < 2) { - printf("Using %s mode to transfer files.\n", mode->m_mode); - return; - } - if (argc == 2) { - for (p = modes; p->m_name; p++) - if (strcmp(argv[1], p->m_name) == 0) - break; - if (p->m_name) { - settftpmode(p); - return; - } - printf("%s: unknown mode\n", argv[1]); - /* drop through and print usage message */ - } + if (argc < 2) { + printf("Using %s mode to transfer files.\n", mode); + return; + } + if (argc == 2) { + for (p = modes; p->m_name; p++) + if (strcmp(argv[1], p->m_name) == 0) + break; + if (p->m_name) { + settftpmode(p->m_mode); + return; + } + printf("%s: unknown mode\n", argv[1]); + /* drop through and print usage message */ + } - printf("usage: %s [", argv[0]); - sep = " "; - for (p = modes; p->m_name; p++) { - printf("%s%s", sep, p->m_name); - if (*sep == ' ') - sep = " | "; - } - printf(" ]\n"); - return; + printf("usage: %s [", argv[0]); + sep = " "; + for (p = modes; p->m_name; p++) { + printf("%s%s", sep, p->m_name); + if (*sep == ' ') + sep = " | "; + } + printf(" ]\n"); + return; } -void setbinary(int argc, char *argv[]) -{ - (void)argc; - (void)argv; /* Quiet unused warning */ - settftpmode(MODE_OCTET); +void +setbinary(int argc, char *argv[]) +{ + (void)argc; (void)argv; /* Quiet unused warning */ + settftpmode("octet"); } -void setascii(int argc, char *argv[]) +void +setascii(int argc, char *argv[]) { - (void)argc; - (void)argv; /* Quiet unused warning */ - settftpmode(MODE_NETASCII); + (void)argc; (void)argv; /* Quiet unused warning */ + settftpmode("netascii"); } -static void settftpmode(const struct modes *newmode) +static void +settftpmode(const char *newmode) { - mode = newmode; - if (verbose) - printf("mode set to %s\n", mode->m_mode); + strcpy(mode, newmode); + if (verbose) + printf("mode set to %s\n", mode); } + /* * Send file(s). */ -void put(int argc, char *argv[]) +void +put(int argc, char *argv[]) { - int fd; - int n, err; - char *cp, *targ; + int fd; + int n; + char *cp, *targ; - if (argc < 2) { - getmoreargs("send ", "(file) "); - makeargv(); - argc = margc; - argv = margv; - } - if (argc < 2) { - putusage(argv[0]); - return; - } - targ = argv[argc - 1]; - if (!literal && strchr(argv[argc - 1], ':')) { - for (n = 1; n < argc - 1; n++) - if (strchr(argv[n], ':')) { - putusage(argv[0]); - return; - } - cp = argv[argc - 1]; - targ = strchr(cp, ':'); - *targ++ = 0; - peeraddr.sa.sa_family = ai_fam; - err = set_sock_addr(cp, &peeraddr,&hostname); - if (err) { - printf("Error: %s\n", gai_strerror(err)); - printf("%s: unknown host\n", argv[1]); - connected = 0; - return; - } - ai_fam = peeraddr.sa.sa_family; - connected = 1; - } - if (!connected) { - printf("No target machine specified.\n"); - return; - } - if (argc < 4) { - cp = argc == 2 ? tail(targ) : argv[1]; - fd = open(cp, O_RDONLY | mode->m_openflags); - if (fd < 0) { - fprintf(stderr, "tftp: "); - perror(cp); - return; - } - if (verbose) - printf("putting %s to %s:%s [%s]\n", - cp, hostname, targ, mode->m_mode); - sa_set_port(&peeraddr, port); - tftp_sendfile(fd, targ, mode->m_mode); - return; - } - /* this assumes the target is a directory */ - /* on a remote unix system. hmmmm. */ - cp = strchr(targ, '\0'); - *cp++ = '/'; - for (n = 1; n < argc - 1; n++) { - strcpy(cp, tail(argv[n])); - fd = open(argv[n], O_RDONLY | mode->m_openflags); - if (fd < 0) { - fprintf(stderr, "tftp: "); - perror(argv[n]); - continue; - } - if (verbose) - printf("putting %s to %s:%s [%s]\n", - argv[n], hostname, targ, mode->m_mode); - sa_set_port(&peeraddr, port); - tftp_sendfile(fd, targ, mode->m_mode); - } + if (argc < 2) { + getmoreargs("send ", "(file) "); + makeargv(); + argc = margc; + argv = margv; + } + if (argc < 2) { + putusage(argv[0]); + return; + } + targ = argv[argc - 1]; + if (strchr(argv[argc - 1], ':')) { + struct hostent *hp; + + for (n = 1; n < argc - 1; n++) + if (strchr(argv[n], ':')) { + putusage(argv[0]); + return; + } + cp = argv[argc - 1]; + targ = strchr(cp, ':'); + *targ++ = 0; + hp = gethostbyname(cp); + if (hp == NULL) { + fprintf(stderr, "tftp: %s: ", cp); + herror((char *)NULL); + return; + } + bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length); + peeraddr.sin_family = hp->h_addrtype; + connected = 1; + hostname = xstrdup(hp->h_name); + } + if (!connected) { + printf("No target machine specified.\n"); + return; + } + if (argc < 4) { + cp = argc == 2 ? tail(targ) : argv[1]; + fd = open(cp, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "tftp: "); perror(cp); + return; + } + if (verbose) + printf("putting %s to %s:%s [%s]\n", + cp, hostname, targ, mode); + peeraddr.sin_port = port; + tftp_sendfile(fd, targ, mode); + return; + } + /* this assumes the target is a directory */ + /* on a remote unix system. hmmmm. */ + cp = strchr(targ, '\0'); + *cp++ = '/'; + for (n = 1; n < argc - 1; n++) { + strcpy(cp, tail(argv[n])); + fd = open(argv[n], O_RDONLY); + if (fd < 0) { + fprintf(stderr, "tftp: "); perror(argv[n]); + continue; + } + if (verbose) + printf("putting %s to %s:%s [%s]\n", + argv[n], hostname, targ, mode); + peeraddr.sin_port = port; + tftp_sendfile(fd, targ, mode); + } } -static void putusage(char *s) +static void +putusage(char *s) { - printf("usage: %s file ... host:target, or\n", s); - printf(" %s file ... target (when already connected)\n", s); + printf("usage: %s file ... host:target, or\n", s); + printf(" %s file ... target (when already connected)\n", s); } /* * Receive file(s). */ -void get(int argc, char *argv[]) +void +get(int argc, char *argv[]) { - int fd; - int n; - char *cp; - char *src; + int fd; + int n; + char *cp; + char *src; - if (argc < 2) { - getmoreargs("get ", "(files) "); - makeargv(); - argc = margc; - argv = margv; - } - if (argc < 2) { - getusage(argv[0]); - return; - } - if (!connected) { - for (n = 1; n < argc; n++) - if (literal || strchr(argv[n], ':') == 0) { - getusage(argv[0]); - return; - } - } - for (n = 1; n < argc; n++) { - src = strchr(argv[n], ':'); - if (literal || src == NULL) - src = argv[n]; - else { - int err; + if (argc < 2) { + getmoreargs("get ", "(files) "); + makeargv(); + argc = margc; + argv = margv; + } + if (argc < 2) { + getusage(argv[0]); + return; + } + if (!connected) { + for (n = 1; n < argc ; n++) + if (strchr(argv[n], ':') == 0) { + getusage(argv[0]); + return; + } + } + for (n = 1; n < argc ; n++) { + src = strchr(argv[n], ':'); + if (src == NULL) + src = argv[n]; + else { + struct hostent *hp; - *src++ = 0; - peeraddr.sa.sa_family = ai_fam; - err = set_sock_addr(argv[n], &peeraddr, &hostname); - if (err) { - printf("Warning: %s\n", gai_strerror(err)); - printf("%s: unknown host\n", argv[1]); - continue; - } - ai_fam = peeraddr.sa.sa_family; - connected = 1; - } - if (argc < 4) { - cp = argc == 3 ? argv[2] : tail(src); - fd = open(cp, O_WRONLY | O_CREAT | O_TRUNC | mode->m_openflags, - 0666); - if (fd < 0) { - fprintf(stderr, "tftp: "); - perror(cp); - return; - } - if (verbose) - printf("getting from %s:%s to %s [%s]\n", - hostname, src, cp, mode->m_mode); - sa_set_port(&peeraddr, port); - tftp_recvfile(fd, src, mode->m_mode); - break; - } - cp = tail(src); /* new .. jdg */ - fd = open(cp, O_WRONLY | O_CREAT | O_TRUNC | mode->m_openflags, - 0666); - if (fd < 0) { - fprintf(stderr, "tftp: "); - perror(cp); - continue; - } - if (verbose) - printf("getting from %s:%s to %s [%s]\n", - hostname, src, cp, mode->m_mode); - sa_set_port(&peeraddr, port); - tftp_recvfile(fd, src, mode->m_mode); - } + *src++ = 0; + hp = gethostbyname(argv[n]); + if (hp == NULL) { + fprintf(stderr, "tftp: %s: ", argv[n]); + herror((char *)NULL); + continue; + } + bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, + hp->h_length); + peeraddr.sin_family = hp->h_addrtype; + connected = 1; + hostname = xstrdup(hp->h_name); + } + if (argc < 4) { + cp = argc == 3 ? argv[2] : tail(src); + fd = creat(cp, 0644); + if (fd < 0) { + fprintf(stderr, "tftp: "); perror(cp); + return; + } + if (verbose) + printf("getting from %s:%s to %s [%s]\n", + hostname, src, cp, mode); + peeraddr.sin_port = port; + tftp_recvfile(fd, src, mode); + break; + } + cp = tail(src); /* new .. jdg */ + fd = creat(cp, 0644); + if (fd < 0) { + fprintf(stderr, "tftp: "); perror(cp); + continue; + } + if (verbose) + printf("getting from %s:%s to %s [%s]\n", + hostname, src, cp, mode); + peeraddr.sin_port = port; + tftp_recvfile(fd, src, mode); + } } -static void getusage(char *s) +static void +getusage(char *s) { - printf("usage: %s host:file host:file ... file, or\n", s); - printf(" %s file file ... file if connected\n", s); + printf("usage: %s host:file host:file ... file, or\n", s); + printf(" %s file file ... file if connected\n", s); } -int rexmtval = TIMEOUT; +int rexmtval = TIMEOUT; -void setrexmt(int argc, char *argv[]) +void +setrexmt(int argc, char *argv[]) { - int t; + int t; - if (argc < 2) { - getmoreargs("rexmt-timeout ", "(value) "); - makeargv(); - argc = margc; - argv = margv; - } - if (argc != 2) { - printf("usage: %s value\n", argv[0]); - return; - } - t = atoi(argv[1]); - if (t < 0) - printf("%s: bad value\n", argv[1]); - else - rexmtval = t; + if (argc < 2) { + getmoreargs("rexmt-timeout ", "(value) "); + makeargv(); + argc = margc; + argv = margv; + } + if (argc != 2) { + printf("usage: %s value\n", argv[0]); + return; + } + t = atoi(argv[1]); + if (t < 0) + printf("%s: bad value\n", argv[1]); + else + rexmtval = t; } -int maxtimeout = 5 * TIMEOUT; +int maxtimeout = 5 * TIMEOUT; -void settimeout(int argc, char *argv[]) +void +settimeout(int argc, char *argv[]) { - int t; + int t; - if (argc < 2) { - getmoreargs("maximum-timeout ", "(value) "); - makeargv(); - argc = margc; - argv = margv; - } - if (argc != 2) { - printf("usage: %s value\n", argv[0]); - return; - } - t = atoi(argv[1]); - if (t < 0) - printf("%s: bad value\n", argv[1]); - else - maxtimeout = t; + if (argc < 2) { + getmoreargs("maximum-timeout ", "(value) "); + makeargv(); + argc = margc; + argv = margv; + } + if (argc != 2) { + printf("usage: %s value\n", argv[0]); + return; + } + t = atoi(argv[1]); + if (t < 0) + printf("%s: bad value\n", argv[1]); + else + maxtimeout = t; } -void setliteral(int argc, char *argv[]) +void +status(int argc, char *argv[]) { - (void)argc; - (void)argv; /* Quiet unused warning */ - literal = !literal; - printf("Literal mode %s.\n", literal ? "on" : "off"); + (void)argc; (void)argv; /* Quiet unused warning */ + if (connected) + printf("Connected to %s.\n", hostname); + else + printf("Not connected.\n"); + printf("Mode: %s Verbose: %s Tracing: %s\n", mode, + verbose ? "on" : "off", trace ? "on" : "off"); + printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", + rexmtval, maxtimeout); } -void status(int argc, char *argv[]) +void +intr(int sig) { - (void)argc; - (void)argv; /* Quiet unused warning */ - if (connected) - printf("Connected to %s.\n", hostname); - else - printf("Not connected.\n"); - printf("Mode: %s Verbose: %s Tracing: %s Literal: %s\n", mode->m_mode, - verbose ? "on" : "off", trace ? "on" : "off", - literal ? "on" : "off"); - printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", - rexmtval, maxtimeout); + (void)sig; /* Quiet unused warning */ + + bsd_signal(SIGALRM, SIG_IGN); + alarm(0); + siglongjmp(toplevel, -1); } -void intr(int sig) +char * +tail(char *filename) { - (void)sig; /* Quiet unused warning */ - - alarm(0); - tftp_signal(SIGALRM, SIG_DFL, 0); - siglongjmp(toplevel, -1); -} - -char *tail(char *filename) -{ - char *s; - - while (*filename) { - s = strrchr(filename, '/'); - if (s == NULL) - break; - if (s[1]) - return (s + 1); - *s = '\0'; - } - return (filename); + char *s; + + while (*filename) { + s = strrchr(filename, '/'); + if (s == NULL) + break; + if (s[1]) + return (s + 1); + *s = '\0'; + } + return (filename); } /* * Command parser. */ -static void command(void) +static void +command(void) { - struct cmd *c; + struct cmd *c; - for (;;) { + for (;;) { #ifdef WITH_READLINE - if (line) { - free(line); - line = NULL; - } - line = readline(prompt); - if (!line) - exit(0); /* EOF */ + if ( line ) + free(line); + line = readline(prompt); + if ( !line ) + exit(0); /* EOF */ #else - fputs(prompt, stdout); - fflush(stdout); - if (fgets(line, LBUFLEN, stdin) == 0) { - if (feof(stdin)) { - exit(0); - } else { - continue; - } - } + fputs(prompt, stdout); + if (fgets(line, LBUFLEN, stdin) == 0) { + if (feof(stdin)) { + exit(0); + } else { + continue; + } + } #endif - if ((line[0] == 0) || (line[0] == '\n')) - continue; + if ((line[0] == 0) || (line[0] == '\n')) + continue; #ifdef WITH_READLINE -#ifdef HAVE_READLINE_HISTORY_H - add_history(line); + add_history(line); #endif -#endif - makeargv(); - if (margc == 0) - continue; + makeargv(); + if (margc == 0) + continue; - c = getcmd(margv[0]); - if (c == (struct cmd *)-1) { - printf("?Ambiguous command\n"); - continue; - } - if (c == 0) { - printf("?Invalid command\n"); - continue; - } - (*c->handler) (margc, margv); - } + c = getcmd(margv[0]); + if (c == (struct cmd *)-1) { + printf("?Ambiguous command\n"); + continue; + } + if (c == 0) { + printf("?Invalid command\n"); + continue; + } + (*c->handler)(margc, margv); + } } -struct cmd *getcmd(char *name) +struct cmd * +getcmd(char *name) { - const char *p; - char *q; - struct cmd *c, *found; - int nmatches, longest; + const char *p; + char *q; + struct cmd *c, *found; + int nmatches, longest; - longest = 0; - nmatches = 0; - found = 0; - for (c = cmdtab; (p = c->name) != NULL; c++) { - for (q = name; *q == *p++; q++) - if (*q == 0) /* exact match? */ - return (c); - if (!*q) { /* the name was a prefix */ - if (q - name > longest) { - longest = q - name; - nmatches = 1; - found = c; - } else if (q - name == longest) - nmatches++; - } - } - if (nmatches > 1) - return ((struct cmd *)-1); - return (found); + longest = 0; + nmatches = 0; + found = 0; + for (c = cmdtab; (p = c->name) != NULL; c++) { + for (q = name; *q == *p++; q++) + if (*q == 0) /* exact match? */ + return (c); + if (!*q) { /* the name was a prefix */ + if (q - name > longest) { + longest = q - name; + nmatches = 1; + found = c; + } else if (q - name == longest) + nmatches++; + } + } + if (nmatches > 1) + return ((struct cmd *)-1); + return (found); } /* * Slice a string up into argc/argv. */ -static void makeargv(void) +static void +makeargv(void) { - char *cp; - char **argp = margv; + char *cp; + char **argp = margv; - margc = 0; - for (cp = line; *cp;) { - while (isspace(*cp)) - cp++; - if (*cp == '\0') - break; - *argp++ = cp; - margc += 1; - while (*cp != '\0' && !isspace(*cp)) - cp++; - if (*cp == '\0') - break; - *cp++ = '\0'; - } - *argp++ = 0; + margc = 0; + for (cp = line; *cp;) { + while (isspace(*cp)) + cp++; + if (*cp == '\0') + break; + *argp++ = cp; + margc += 1; + while (*cp != '\0' && !isspace(*cp)) + cp++; + if (*cp == '\0') + break; + *cp++ = '\0'; + } + *argp++ = 0; } -void quit(int argc, char *argv[]) +void +quit(int argc, char *argv[]) { - (void)argc; - (void)argv; /* Quiet unused warning */ - exit(0); + (void)argc; (void)argv; /* Quiet unused warning */ + exit(0); } /* * Help command. */ -void help(int argc, char *argv[]) +void +help(int argc, char *argv[]) { - struct cmd *c; + struct cmd *c; - printf("%s\n", VERSION); - - if (argc == 1) { - printf("Commands may be abbreviated. Commands are:\n\n"); - for (c = cmdtab; c->name; c++) - printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); - return; - } - while (--argc > 0) { - char *arg; - arg = *++argv; - c = getcmd(arg); - if (c == (struct cmd *)-1) - printf("?Ambiguous help command %s\n", arg); - else if (c == (struct cmd *)0) - printf("?Invalid help command %s\n", arg); - else - printf("%s\n", c->help); - } + if (argc == 1) { + printf("Commands may be abbreviated. Commands are:\n\n"); + for (c = cmdtab; c->name; c++) + printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); + return; + } + while (--argc > 0) { + char *arg; + arg = *++argv; + c = getcmd(arg); + if (c == (struct cmd *)-1) + printf("?Ambiguous help command %s\n", arg); + else if (c == (struct cmd *)0) + printf("?Invalid help command %s\n", arg); + else + printf("%s\n", c->help); + } } -void settrace(int argc, char *argv[]) +void +settrace(int argc, char *argv[]) { - (void)argc; - (void)argv; /* Quiet unused warning */ + (void)argc; (void)argv; /* Quiet unused warning */ - trace = !trace; - printf("Packet tracing %s.\n", trace ? "on" : "off"); + trace = !trace; + printf("Packet tracing %s.\n", trace ? "on" : "off"); } -void setverbose(int argc, char *argv[]) +void +setverbose(int argc, char *argv[]) { - (void)argc; - (void)argv; /* Quiet unused warning */ + (void)argc; (void)argv; /* Quiet unused warning */ - verbose = !verbose; - printf("Verbose mode %s.\n", verbose ? "on" : "off"); + verbose = !verbose; + printf("Verbose mode %s.\n", verbose ? "on" : "off"); } diff --git a/tftp/tftp.1 b/tftp/tftp.1 new file mode 100644 index 0000000..41685c8 --- /dev/null +++ b/tftp/tftp.1 @@ -0,0 +1,177 @@ +.\" $OpenBSD: tftp.1,v 1.4 1999/06/05 01:21:43 aaron Exp $ +.\" $NetBSD: tftp.1,v 1.5 1995/08/18 14:45:44 pk Exp $ +.\" +.\" Copyright (c) 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tftp.1 8.2 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt TFTP 1 +.Os +.Sh NAME +.Nm tftp +.Nd trivial file transfer program +.Sh SYNOPSIS +.Nm tftp +.Op Ar host +.Sh DESCRIPTION +.Nm tftp +is the user interface to the Internet +.Tn TFTP +(Trivial File Transfer Protocol), +which allows users to transfer files to and from a remote machine. +The remote +.Ar host +may be specified on the command line, in which case +.Nm tftp +uses +.Ar host +as the default host for future transfers (see the +.Ic connect +command below). +.Sh COMMANDS +Once +.Nm tftp +is running, it issues the prompt +.Ql tftp> +and recognizes the following commands: +.Pp +.Bl -tag -width verbose -compact +.It Ic \&? Ar command-name Op Ar ... +Print help information. +.Pp +.It Ic ascii +Shorthand for +.Ic mode ascii . +.Pp +.It Ic binary +Shorthand for +.Ic mode binary . +.Pp +.It Ic connect Ar host Op Ar port +Set the +.Ar host +(and optionally +.Ar port ) +for transfers. +Note that the +.Tn TFTP +protocol, unlike the +.Tn FTP +protocol, +does not maintain connections between transfers; thus, the +.Ic connect +command does not actually create a connection, +but merely remembers what host is to be used for transfers. +You do not have to use the +.Ic connect +command; the remote host can be specified as part of the +.Ic get +or +.Ic put +commands. +.Pp +.It Ic get Ar filename +.It Ic get Ar remotename localname +.It Ic get Ar file Op Ar ... +Get a file or set of files from the specified +.Ar sources . +.Ar source +can be in one of two forms: +a filename on the remote host, if the host has already been specified, +or a string of the form +.Ar hosts:filename +to specify both a host and filename at the same time. +If the latter form is used, +the last hostname specified becomes the default for future transfers. +.Pp +.It Ic mode Ar transfer-mode +Set the mode for transfers; +.Ar transfer-mode +may be one of +.Ic ascii +or +.Ic binary . +The default is +.Ic ascii . +.Pp +.It Ic put Ar file +.It Ic put Ar localfile remotefile +.It Ic put Ar file1 file2 ... fileN remote-directory +Put a file or set of files to the specified +remote file or directory. +The destination +can be in one of two forms: +a filename on the remote host, if the host has already been specified, +or a string of the form +.Ar hosts:filename +to specify both a host and filename at the same time. +If the latter form is used, +the hostname specified becomes the default for future transfers. +If the remote-directory form is used, the remote host is +assumed to be a +.Tn UNIX +machine. +.Pp +.It Ic quit +Exit +.Nm tftp . +An end-of-file also exits. +.Pp +.It Ic rexmt Ar retransmission-timeout +Set the per-packet retransmission timeout, in seconds. +.Pp +.It Ic status +Show current status. +.Pp +.It Ic timeout Ar total-transmission-timeout +Set the total transmission timeout, in seconds. +.Pp +.It Ic trace +Toggle packet tracing. +.Pp +.It Ic verbose +Toggle verbose mode. +.El +.Sh BUGS +Because there is no user login or validation within +the +.Tn TFTP +protocol, the remote site will probably have some +sort of file access restrictions in place. The +exact methods are specific to each site and therefore +difficult to document here. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/tftp/tftp.1.in b/tftp/tftp.1.in deleted file mode 100644 index b41f7b5..0000000 --- a/tftp/tftp.1.in +++ /dev/null @@ -1,207 +0,0 @@ -.\" -*- nroff -*- --------------------------------------------------------- * -.\" -.\" Copyright (c) 1990, 1993, 1994 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" Copyright 2001 H. Peter Anvin - All Rights Reserved -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\"----------------------------------------------------------------------- */ -.TH TFTP 1 "23 July 2008" "tftp-hpa @@VERSION@@" "User's Manual" -.SH NAME -.B tftp -\- IPv4 Trivial File Transfer Protocol client -.SH SYNOPSIS -.B tftp -[ \fIoptions...\fP ] -[\fIhost\fP [\fIport\fP]] -[\fB\-c\fP \fIcommand\fP] -.br -.SH DESCRIPTION -.B tftp -is a client for the Trivial file Transfer Protocol, which can be -used to transfer files to and from remote machines, including some -very minimalistic, usually embedded, systems. The remote -.I host -may be specified on the command line, in which case -.B tftp -uses -.I host -as the default host for future transfers (see the -.B connect -command below.) -.SH OPTIONS -.TP -.B \-4 -Connect with IPv4 only, even if IPv6 support was compiled in. -.TP -.B \-6 -Connect with IPv6 only, if compiled in. -.TP -\fB\-c\fP \fIcommand\fP -Execute \fIcommand\fP as if it had been entered on the tftp prompt. -Must be specified last on the command line. -.TP -.B \-l -Default to literal mode. Used to avoid special processing of ':' in a -file name. -.TP -\fB\-m\fP \fImode\fP -Set the default transfer mode to \fImode\fP. This is usually used with \-c. -.TP -\fB\-R\fP \fIport:port\fP -Force the originating port number to be in the specified range of port -numbers. -.TP -.B \-v -Default to verbose mode. -.TP -.B \-V -Print the version number and configuration to standard output, then -exit gracefully. -.SH COMMANDS -Once -.B tftp -is running, it issues the prompt -\f(CWtftp>\fP -and recognizes the following commands: -.TP -\fB?\fP \fIcommand-name...\fP -.TP -\fBhelp\fP \fIcommand-name...\fP -Print help information -.TP -.B ascii -Shorthand for -.BR "mode ascii" . -.TP -.B binary -Shorthand for -.BR "mode binary" . -.TP -\fBconnect\fP \fIhost [port]\fP -Set the -.I host -(and optionally -.IR port ) -for transfers. Note that the TFTP protocol, unlike the FTP protocol, -does not maintain connections between transfers; thus, the -.B connect -command does not actually create a connection, but merely remembers -what host is to be used for transfers. You do not have to use the -.B connect -command; the remote host can be specified as part of the -.B get -or -.B put -commands. -.TP -\fBget\fP \fIfile\fP -.sp -.6l -.TP -\fBget\fP \fIremotefile localfile\fP -.sp -.6l -.TP -\fBget\fP \fIfile1 file2 file3...\fP -Get a file or set of files from the specified sources. A remote -filename can be in one of two forms: a plain filename on the remote -host, if the host has already been specified, or a string of the form -.I "host:filename" -to specify both a host and filename at the same time. If the latter -form is used, the last hostname specified becomes the default for -future transfers. Enable -.B literal -mode to prevent special treatment of the ':' character (e.g. C:\\dir\\file). -.TP -.B literal -Toggle literal mode. When set, this mode prevents special treatment of ':' in filenames. -.TP -\fBmode\fP \fItransfer-mode\fP -Specify the mode for transfers; -.I transfer-mode -may be one of -.B ascii -(or -.BR netascii ) -or -.B binary -(or -.BR octet .) -The default is -.BR ascii . -.TP -\fBput\fP \fIfile\fP -.sp -.6l -.TP -\fBput\fP \fIlocalfile remotefile\fP -.sp -.6l -.TP -\fBput\fP \fIfile1 file2 file3... remote-directory\fP -Put a file or set of files to the specified remote file or directory. -The destination can be in one of two forms: a filename on the remote -host, if the host has already been specified, or a string of the form -.I "host:filename" -to specify both a host and filename at the same time. If the latter -form is used, the hostname specified becomes the default for future -transfers. If the remote-directory form is used, the remote host is -assumed to be a UNIX system or another system using -.B / -as directory separator. Enable -.B literal -mode to prevent special treatment of the ':' character (e.g. C:\\dir\\file). -.TP -.B quit -Exit -.BR tftp . -End-of-file will also exit. -.TP -\fBrexmt\fP \fIretransmission-timeout\fP -Set the per-packet retransmission timeout, in seconds. -.TP -.B status -Show current status. -.TP -\fBtimeout\fP \fItotal-transmission-timeout\fP -Set the total transmission timeout, in seconds. -.TP -.B trace -Toggle packet tracing (a debugging feature.) -.TP -.B verbose -Toggle verbose mode. -.SH "NOTES" -The TFTP protocol provides no provisions for authentication or -security. Therefore, the remote server will probably implement some -kinds of access restriction or firewalling. These access restrictions -are likely to be site- and server-specific. -.SH "AUTHOR" -This version of -.B tftp -is maintained by H. Peter Anvin . It was derived from, -but has substantially diverged from, an OpenBSD source base, with -added patches by Markus Gutschke and Gero Kulhman. -.SH "SEE ALSO" -.BR tftpd (8). diff --git a/tftp/tftp.c b/tftp/tftp.c index 9c9c5af..fe8a88d 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -1,3 +1,8 @@ +/* $Id$ */ + +/* $OpenBSD: tftp.c,v 1.4 1997/08/06 06:43:45 deraadt Exp $ */ +/* $NetBSD: tftp.c,v 1.5 1995/04/29 05:55:25 cgd Exp $ */ + /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. @@ -31,26 +36,58 @@ * SUCH DAMAGE. */ -#include "common/tftpsubs.h" +#include "tftpsubs.h" + +#ifndef lint +/* static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; */ +/* static char rcsid[] = "$OpenBSD: tftp.c,v 1.4 1997/08/06 06:43:45 deraadt Exp $"; */ +static const char *rcsid UNUSED = +"tftp-hpa $Id$"; +#endif /* not lint */ + +/* Many bug fixes are from Jim Guyton */ /* * TFTP User Program -- Protocol Machines */ +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + #include "extern.h" -extern union sock_addr peeraddr; /* filled in by main */ -extern int f; /* the opened socket */ -extern int trace; -extern int verbose; -extern int rexmtval; -extern int maxtimeout; +extern struct sockaddr_in peeraddr; /* filled in by main */ +extern int f; /* the opened socket */ +extern int trace; +extern int verbose; +extern int rexmtval; +extern int maxtimeout; + +#ifndef EOPTNEG +#define EOPTNEG 8 +#endif +#ifndef OACK +#define OACK 6 +#endif #define PKTSIZE SEGSIZE+4 -char ackbuf[PKTSIZE]; -int timeout; -static sigjmp_buf timeoutbuf; +char ackbuf[PKTSIZE]; +int timeout; +sigjmp_buf toplevel; +sigjmp_buf timeoutbuf; -static void nak(int, const char *); +static void nak(int); static int makerequest(int, const char *, struct tftphdr *, const char *); static void printstats(const char *, unsigned long); static void startclock(void); @@ -61,365 +98,363 @@ static void tpacket(const char *, struct tftphdr *, int); /* * Send the requested file. */ -void tftp_sendfile(int fd, const char *name, const char *mode) +void +tftp_sendfile(int fd, char *name, char *mode) { - struct tftphdr *ap; /* data and ack packets */ - struct tftphdr *dp; - int n; - volatile int is_request; - volatile u_short block; - volatile int size, convert; - volatile off_t amount; - union sock_addr from; - socklen_t fromlen; - FILE *file; - u_short ap_opcode, ap_block; + struct tftphdr *ap; /* data and ack packets */ + struct tftphdr *dp; + int n; + volatile int is_request; + volatile u_short block; + volatile int size, convert; + volatile off_t amount; + struct sockaddr_in from; + int fromlen; + FILE *file; - startclock(); /* start stat's clock */ - dp = r_init(); /* reset fillbuf/read-ahead code */ - ap = (struct tftphdr *)ackbuf; - convert = !strcmp(mode, "netascii"); - file = fdopen(fd, convert ? "rt" : "rb"); - block = 0; - is_request = 1; /* First packet is the actual WRQ */ - amount = 0; + startclock(); /* start stat's clock */ + dp = r_init(); /* reset fillbuf/read-ahead code */ + ap = (struct tftphdr *)ackbuf; + file = fdopen(fd, "r"); + convert = !strcmp(mode, "netascii"); + block = 0; + is_request = 1; /* First packet is the actual WRQ */ + amount = 0; - tftp_signal(SIGALRM, timer, 0); - do { - if (is_request) { - size = makerequest(WRQ, name, dp, mode) - 4; - } else { - /* size = read(fd, dp->th_data, SEGSIZE); */ - size = readit(file, &dp, convert); - if (size < 0) { - nak(errno + 100, NULL); - break; - } - dp->th_opcode = htons((u_short) DATA); - dp->th_block = htons((u_short) block); - } - timeout = 0; - (void)sigsetjmp(timeoutbuf, 1); + bsd_signal(SIGALRM, timer); + do { + if (is_request) { + size = makerequest(WRQ, name, dp, mode) - 4; + } else { + /* size = read(fd, dp->th_data, SEGSIZE); */ + size = readit(file, &dp, convert); + if (size < 0) { + nak(errno + 100); + break; + } + dp->th_opcode = htons((u_short)DATA); + dp->th_block = htons((u_short)block); + } + timeout = 0; + (void) sigsetjmp(timeoutbuf,1); +send_data: + if (trace) + tpacket("sent", dp, size + 4); + n = sendto(f, dp, size + 4, 0, + (struct sockaddr *)&peeraddr, sizeof(peeraddr)); + if (n != size + 4) { + perror("tftp: sendto"); + goto abort; + } + read_ahead(file, convert); + for ( ; ; ) { + alarm(rexmtval); + do { + fromlen = sizeof(from); + n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, + (struct sockaddr *)&from, &fromlen); + } while (n <= 0); + alarm(0); + if (n < 0) { + perror("tftp: recvfrom"); + goto abort; + } + peeraddr.sin_port = from.sin_port; /* added */ + if (trace) + tpacket("received", ap, n); + /* should verify packet came from server */ + ap->th_opcode = ntohs(ap->th_opcode); + ap->th_block = ntohs(ap->th_block); + if (ap->th_opcode == ERROR) { + printf("Error code %d: %s\n", ap->th_code, + ap->th_msg); + goto abort; + } + if (ap->th_opcode == ACK) { + int j; - if (trace) - tpacket("sent", dp, size + 4); - n = sendto(f, dp, size + 4, 0, - &peeraddr.sa, SOCKLEN(&peeraddr)); - if (n != size + 4) { - perror("tftp: sendto"); - goto abort; - } - read_ahead(file, convert); - for (;;) { - alarm(rexmtval); - do { - fromlen = sizeof(from); - n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, - &from.sa, &fromlen); - } while (n <= 0); - alarm(0); - if (n < 0) { - perror("tftp: recvfrom"); - goto abort; - } - sa_set_port(&peeraddr, SOCKPORT(&from)); /* added */ - if (trace) - tpacket("received", ap, n); - /* should verify packet came from server */ - ap_opcode = ntohs((u_short) ap->th_opcode); - ap_block = ntohs((u_short) ap->th_block); - if (ap_opcode == ERROR) { - printf("Error code %d: %s\n", ap_block, ap->th_msg); - goto abort; - } - if (ap_opcode == ACK) { - int j; - - if (ap_block == block) { - break; - } - /* On an error, try to synchronize - * both sides. - */ - j = synchnet(f); - if (j && trace) { - printf("discarded %d packets\n", j); - } - /* - * RFC1129/RFC1350: We MUST NOT re-send the DATA - * packet in response to an invalid ACK. Doing so - * would cause the Sorcerer's Apprentice bug. - */ - } - } - if (!is_request) - amount += size; - is_request = 0; - block++; - } while (size == SEGSIZE || block == 1); - abort: - fclose(file); - stopclock(); - if (amount > 0) - printstats("Sent", amount); + if (ap->th_block == block) { + break; + } + /* On an error, try to synchronize + * both sides. + */ + j = synchnet(f); + if (j && trace) { + printf("discarded %d packets\n", + j); + } + if (ap->th_block == (block-1)) { + goto send_data; + } + } + } + if ( !is_request ) + amount += size; + is_request = 0; + block++; + } while (size == SEGSIZE || block == 1); +abort: + fclose(file); + stopclock(); + if (amount > 0) + printstats("Sent", amount); } /* * Receive a file. */ -void tftp_recvfile(int fd, const char *name, const char *mode) +void +tftp_recvfile(int fd, char *name, char *mode) { - struct tftphdr *ap; - struct tftphdr *dp; - int n; - volatile u_short block; - volatile int size, firsttrip; - volatile unsigned long amount; - union sock_addr from; - socklen_t fromlen; - FILE *file; - volatile int convert; /* true if converting crlf -> lf */ - u_short dp_opcode, dp_block; + struct tftphdr *ap; + struct tftphdr *dp; + int n; + volatile u_short block; + volatile int size, firsttrip; + volatile unsigned long amount; + struct sockaddr_in from; + int fromlen; + FILE *file; + volatile int convert; /* true if converting crlf -> lf */ - startclock(); - dp = w_init(); - ap = (struct tftphdr *)ackbuf; - convert = !strcmp(mode, "netascii"); - file = fdopen(fd, convert ? "wt" : "wb"); - block = 1; - firsttrip = 1; - amount = 0; + startclock(); + dp = w_init(); + ap = (struct tftphdr *)ackbuf; + file = fdopen(fd, "w"); + convert = !strcmp(mode, "netascii"); + block = 1; + firsttrip = 1; + amount = 0; - tftp_signal(SIGALRM, timer, 0); - do { - if (firsttrip) { - size = makerequest(RRQ, name, ap, mode); - firsttrip = 0; - } else { - ap->th_opcode = htons((u_short) ACK); - ap->th_block = htons((u_short) block); - size = 4; - block++; - } - timeout = 0; - (void)sigsetjmp(timeoutbuf, 1); - send_ack: - if (trace) - tpacket("sent", ap, size); - if (sendto(f, ackbuf, size, 0, &peeraddr.sa, - SOCKLEN(&peeraddr)) != size) { - alarm(0); - perror("tftp: sendto"); - goto abort; - } - write_behind(file, convert); - for (;;) { - alarm(rexmtval); - do { - fromlen = sizeof(from); - n = recvfrom(f, dp, PKTSIZE, 0, - &from.sa, &fromlen); - } while (n <= 0); - alarm(0); - if (n < 0) { - perror("tftp: recvfrom"); - goto abort; - } - sa_set_port(&peeraddr, SOCKPORT(&from)); /* added */ - if (trace) - tpacket("received", dp, n); - /* should verify client address */ - dp_opcode = ntohs((u_short) dp->th_opcode); - dp_block = ntohs((u_short) dp->th_block); - if (dp_opcode == ERROR) { - printf("Error code %d: %s\n", dp_block, dp->th_msg); - goto abort; - } - if (dp_opcode == DATA) { - int j; + bsd_signal(SIGALRM, timer); + do { + if (firsttrip) { + size = makerequest(RRQ, name, ap, mode); + firsttrip = 0; + } else { + ap->th_opcode = htons((u_short)ACK); + ap->th_block = htons((u_short)(block)); + size = 4; + block++; + } + timeout = 0; + (void) sigsetjmp(timeoutbuf,1); +send_ack: + if (trace) + tpacket("sent", ap, size); + if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, + sizeof(peeraddr)) != size) { + alarm(0); + perror("tftp: sendto"); + goto abort; + } + write_behind(file, convert); + for ( ; ; ) { + alarm(rexmtval); + do { + fromlen = sizeof(from); + n = recvfrom(f, dp, PKTSIZE, 0, + (struct sockaddr *)&from, &fromlen); + } while (n <= 0); + alarm(0); + if (n < 0) { + perror("tftp: recvfrom"); + goto abort; + } + peeraddr.sin_port = from.sin_port; /* added */ + if (trace) + tpacket("received", dp, n); + /* should verify client address */ + dp->th_opcode = ntohs(dp->th_opcode); + dp->th_block = ntohs(dp->th_block); + if (dp->th_opcode == ERROR) { + printf("Error code %d: %s\n", dp->th_code, + dp->th_msg); + goto abort; + } + if (dp->th_opcode == DATA) { + int j; - if (dp_block == block) { - break; /* have next packet */ - } - /* On an error, try to synchronize - * both sides. - */ - j = synchnet(f); - if (j && trace) { - printf("discarded %d packets\n", j); - } - if (dp_block == (block - 1)) { - goto send_ack; /* resend ack */ - } - } - } - /* size = write(fd, dp->th_data, n - 4); */ - size = writeit(file, &dp, n - 4, convert); - if (size < 0) { - nak(errno + 100, NULL); - break; - } - amount += size; - } while (size == SEGSIZE); - abort: /* ok to ack, since user */ - ap->th_opcode = htons((u_short) ACK); /* has seen err msg */ - ap->th_block = htons((u_short) block); - (void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, - SOCKLEN(&peeraddr)); - write_behind(file, convert); /* flush last buffer */ - fclose(file); - stopclock(); - if (amount > 0) - printstats("Received", amount); + if (dp->th_block == block) { + break; /* have next packet */ + } + /* On an error, try to synchronize + * both sides. + */ + j = synchnet(f); + if (j && trace) { + printf("discarded %d packets\n", j); + } + if (dp->th_block == (block-1)) { + goto send_ack; /* resend ack */ + } + } + } + /* size = write(fd, dp->th_data, n - 4); */ + size = writeit(file, &dp, n - 4, convert); + if (size < 0) { + nak(errno + 100); + break; + } + amount += size; + } while (size == SEGSIZE); +abort: /* ok to ack, since user */ + ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ + ap->th_block = htons((u_short)block); + (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, + sizeof(peeraddr)); + write_behind(file, convert); /* flush last buffer */ + fclose(file); + stopclock(); + if (amount > 0) + printstats("Received", amount); } static int makerequest(int request, const char *name, - struct tftphdr *tp, const char *mode) + struct tftphdr *tp, const char *mode) { - char *cp; - size_t len; + char *cp; - tp->th_opcode = htons((u_short) request); - cp = (char *)&(tp->th_stuff); - len = strlen(name) + 1; - memcpy(cp, name, len); - cp += len; - len = strlen(mode) + 1; - memcpy(cp, mode, len); - cp += len; - return (cp - (char *)tp); + tp->th_opcode = htons((u_short)request); + cp = (char *) &(tp->th_stuff); + strcpy(cp, name); + cp += strlen(name); + *cp++ = '\0'; + strcpy(cp, mode); + cp += strlen(mode); + *cp++ = '\0'; + return (cp - (char *)tp); } -static const char *const errmsgs[] = { - "Undefined error code", /* 0 - EUNDEF */ - "File not found", /* 1 - ENOTFOUND */ - "Access denied", /* 2 - EACCESS */ - "Disk full or allocation exceeded", /* 3 - ENOSPACE */ - "Illegal TFTP operation", /* 4 - EBADOP */ - "Unknown transfer ID", /* 5 - EBADID */ - "File already exists", /* 6 - EEXISTS */ - "No such user", /* 7 - ENOUSER */ - "Failure to negotiate RFC2347 options" /* 8 - EOPTNEG */ +struct errmsg { + int e_code; + const char *e_msg; +} errmsgs[] = { + { EUNDEF, "Undefined error code" }, + { ENOTFOUND, "File not found" }, + { EACCESS, "Access violation" }, + { ENOSPACE, "Disk full or allocation exceeded" }, + { EBADOP, "Illegal TFTP operation" }, + { EBADID, "Unknown transfer ID" }, + { EEXISTS, "File already exists" }, + { ENOUSER, "No such user" }, + { EOPTNEG, "Failure to negotiate RFC2347 options" }, + { -1, 0 } }; -#define ERR_CNT (sizeof(errmsgs)/sizeof(const char *)) - /* * Send a nak packet (error message). * Error code passed in is one of the * standard TFTP codes, or a UNIX errno * offset by 100. */ -static void nak(int error, const char *msg) +static void +nak(int error) { - struct tftphdr *tp; - int length; + struct errmsg *pe; + struct tftphdr *tp; + int length; - tp = (struct tftphdr *)ackbuf; - tp->th_opcode = htons((u_short) ERROR); - tp->th_code = htons((u_short) error); - - if (error >= 100) { - /* This is a Unix errno+100 */ - if (!msg) - msg = strerror(error - 100); - error = EUNDEF; - } else { - if ((unsigned)error >= ERR_CNT) - error = EUNDEF; - - if (!msg) - msg = errmsgs[error]; - } - - tp->th_code = htons((u_short) error); - - length = strlen(msg) + 1; - memcpy(tp->th_msg, msg, length); - length += 4; /* Add space for header */ - - if (trace) - tpacket("sent", tp, length); - if (sendto(f, ackbuf, length, 0, &peeraddr.sa, - SOCKLEN(&peeraddr)) != length) - perror("nak"); + tp = (struct tftphdr *)ackbuf; + tp->th_opcode = htons((u_short)ERROR); + tp->th_code = htons((u_short)error); + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + break; + if (pe->e_code < 0) { + pe->e_msg = strerror(error - 100); + tp->th_code = EUNDEF; + } + strcpy(tp->th_msg, pe->e_msg); + length = strlen(pe->e_msg) + 4; + if (trace) + tpacket("sent", tp, length); + if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, + sizeof(peeraddr)) != length) + perror("nak"); } -static void tpacket(const char *s, struct tftphdr *tp, int n) +static void +tpacket(const char *s, struct tftphdr *tp, int n) { - static const char *opcodes[] = - { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" }; - char *cp, *file; - u_short op = ntohs((u_short) tp->th_opcode); + static const char *opcodes[] = + { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" }; + char *cp, *file; + u_short op = ntohs(tp->th_opcode); - if (op < RRQ || op > ERROR) - printf("%s opcode=%x ", s, op); - else - printf("%s %s ", s, opcodes[op]); - switch (op) { + if (op < RRQ || op > ERROR) + printf("%s opcode=%x ", s, op); + else + printf("%s %s ", s, opcodes[op]); + switch (op) { - case RRQ: - case WRQ: - n -= 2; - file = cp = (char *)&(tp->th_stuff); - cp = strchr(cp, '\0'); - printf("\n", file, cp + 1); - break; + case RRQ: + case WRQ: + n -= 2; + file = cp = (char *) &(tp->th_stuff); + cp = strchr(cp, '\0'); + printf("\n", file, cp + 1); + break; - case DATA: - printf("\n", ntohs(tp->th_block), n - 4); - break; + case DATA: + printf("\n", ntohs(tp->th_block), n - 4); + break; - case ACK: - printf("\n", ntohs(tp->th_block)); - break; + case ACK: + printf("\n", ntohs(tp->th_block)); + break; - case ERROR: - printf("\n", ntohs(tp->th_code), tp->th_msg); - break; - } + case ERROR: + printf("\n", ntohs(tp->th_code), tp->th_msg); + break; + } } struct timeval tstart; struct timeval tstop; -static void startclock(void) +static void +startclock(void) { - (void)gettimeofday(&tstart, NULL); + (void)gettimeofday(&tstart, NULL); } -static void stopclock(void) +static void +stopclock(void) { - (void)gettimeofday(&tstop, NULL); + (void)gettimeofday(&tstop, NULL); } -static void printstats(const char *direction, unsigned long amount) +static void +printstats(const char *direction, unsigned long amount) { - double delta; - - delta = (tstop.tv_sec + (tstop.tv_usec / 100000.0)) - - (tstart.tv_sec + (tstart.tv_usec / 100000.0)); - if (verbose) { - printf("%s %lu bytes in %.1f seconds", direction, amount, delta); - printf(" [%.0f bit/s]", (amount * 8.) / delta); - putchar('\n'); - } + double delta; + /* compute delta in 1/10's second units */ + delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - + ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); + delta = delta/10.; /* back to seconds */ + printf("%s %lu bytes in %.1f seconds", direction, amount, delta); + if (verbose) + printf(" [%.0f bits/sec]", (amount*8.)/delta); + putchar('\n'); } -static void timer(int sig) +static void +timer(int sig) { - int save_errno = errno; + int save_errno = errno; - (void)sig; /* Shut up unused warning */ + (void)sig; /* Shut up unused warning */ - timeout += rexmtval; - if (timeout >= maxtimeout) { - printf("Transfer timed out.\n"); - errno = save_errno; - siglongjmp(toplevel, -1); - } - errno = save_errno; - siglongjmp(timeoutbuf, 1); + timeout += rexmtval; + if (timeout >= maxtimeout) { + printf("Transfer timed out.\n"); + errno = save_errno; + siglongjmp(toplevel, -1); + } + errno = save_errno; + siglongjmp(timeoutbuf, 1); } diff --git a/tftp/tftpsubs.c b/tftp/tftpsubs.c new file mode 100644 index 0000000..c358ccd --- /dev/null +++ b/tftp/tftpsubs.c @@ -0,0 +1,282 @@ +/* tftp-hpa: $Id$ */ + +/* $OpenBSD: tftpsubs.c,v 1.2 1996/06/26 05:40:36 deraadt Exp $ */ +/* $NetBSD: tftpsubs.c,v 1.3 1994/12/08 09:51:31 jtc Exp $ */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "tftpsubs.h" + +#ifndef lint +/* static char sccsid[] = "@(#)tftpsubs.c 8.1 (Berkeley) 6/6/93"; */ +/* static char rcsid[] = "$OpenBSD: tftpsubs.c,v 1.2 1996/06/26 05:40:36 deraadt Exp $"; */ +static const char *rcsid UNUSED = +"tftp-hpa: $Id$"; +#endif /* not lint */ + +/* Simple minded read-ahead/write-behind subroutines for tftp user and + server. Written originally with multiple buffers in mind, but current + implementation has two buffer logic wired in. + + Todo: add some sort of final error check so when the write-buffer + is finally flushed, the caller can detect if the disk filled up + (or had an i/o error) and return a nak to the other side. + + Jim Guyton 10/85 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PKTSIZE MAX_SEGSIZE+4 /* should be moved to tftp.h */ + +int segsize = SEGSIZE; /* Default segsize */ + +struct bf { + int counter; /* size of data in buffer, or flag */ + char buf[PKTSIZE]; /* room for data packet */ +} bfs[2]; + + /* Values for bf.counter */ +#define BF_ALLOC -3 /* alloc'd but not yet filled */ +#define BF_FREE -2 /* free */ +/* [-1 .. segsize] = size of data in the data buffer */ + +static int nextone; /* index of next buffer to use */ +static int current; /* index of buffer in use */ + + /* control flags for crlf conversions */ +int newline = 0; /* fillbuf: in middle of newline expansion */ +int prevchar = -1; /* putbuf: previous char (cr check) */ + +static struct tftphdr *rw_init(int); + +struct tftphdr *w_init() { return rw_init(0); } /* write-behind */ +struct tftphdr *r_init() { return rw_init(1); } /* read-ahead */ + +/* init for either read-ahead or write-behind */ +/* x == zero for write-behind, one for read-head */ +static struct tftphdr * +rw_init(int x) +{ + newline = 0; /* init crlf flag */ + prevchar = -1; + bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ + current = 0; + bfs[1].counter = BF_FREE; + nextone = x; /* ahead or behind? */ + return (struct tftphdr *)bfs[0].buf; +} + + +/* Have emptied current buffer by sending to net and getting ack. + Free it and return next buffer filled with data. + */ +int +readit(FILE *file, struct tftphdr **dpp, int convert) +{ + struct bf *b; + + bfs[current].counter = BF_FREE; /* free old one */ + current = !current; /* "incr" current */ + + b = &bfs[current]; /* look at new buffer */ + if (b->counter == BF_FREE) /* if it's empty */ + read_ahead(file, convert); /* fill it */ +/* assert(b->counter != BF_FREE);*//* check */ + *dpp = (struct tftphdr *)b->buf; /* set caller's ptr */ + return b->counter; +} + +/* + * fill the input buffer, doing ascii conversions if requested + * conversions are lf -> cr,lf and cr -> cr, nul + */ +void +read_ahead(FILE *file, int convert) +{ + int i; + char *p; + int c; + struct bf *b; + struct tftphdr *dp; + + b = &bfs[nextone]; /* look at "next" buffer */ + if (b->counter != BF_FREE) /* nop if not free */ + return; + nextone = !nextone; /* "incr" next buffer ptr */ + + dp = (struct tftphdr *)b->buf; + + if (convert == 0) { + b->counter = read(fileno(file), dp->th_data, segsize); + return; + } + + p = dp->th_data; + for (i = 0 ; i < segsize; i++) { + if (newline) { + if (prevchar == '\n') + c = '\n'; /* lf to cr,lf */ + else c = '\0'; /* cr to cr,nul */ + newline = 0; + } + else { + c = getc(file); + if (c == EOF) break; + if (c == '\n' || c == '\r') { + prevchar = c; + c = '\r'; + newline = 1; + } + } + *p++ = c; + } + b->counter = (int)(p - dp->th_data); +} + +/* Update count associated with the buffer, get new buffer + from the queue. Calls write_behind only if next buffer not + available. + */ +int +writeit(FILE *file, struct tftphdr **dpp, int ct, int convert) +{ + bfs[current].counter = ct; /* set size of data to write */ + current = !current; /* switch to other buffer */ + if (bfs[current].counter != BF_FREE) /* if not free */ + (void)write_behind(file, convert); /* flush it */ + bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ + *dpp = (struct tftphdr *)bfs[current].buf; + return ct; /* this is a lie of course */ +} + +/* + * Output a buffer to a file, converting from netascii if requested. + * CR,NUL -> CR and CR,LF => LF. + * Note spec is undefined if we get CR as last byte of file or a + * CR followed by anything else. In this case we leave it alone. + */ +int +write_behind(FILE *file, int convert) +{ + char *buf; + int count; + int ct; + char *p; + int c; /* current character */ + struct bf *b; + struct tftphdr *dp; + + b = &bfs[nextone]; + if (b->counter < -1) /* anything to flush? */ + return 0; /* just nop if nothing to do */ + + count = b->counter; /* remember byte count */ + b->counter = BF_FREE; /* reset flag */ + dp = (struct tftphdr *)b->buf; + nextone = !nextone; /* incr for next time */ + buf = dp->th_data; + + if (count <= 0) return -1; /* nak logic? */ + + if (convert == 0) + return write(fileno(file), buf, count); + + p = buf; + ct = count; + while (ct--) { /* loop over the buffer */ + c = *p++; /* pick up a character */ + if (prevchar == '\r') { /* if prev char was cr */ + if (c == '\n') /* if have cr,lf then just */ + fseek(file, -1, 1); /* smash lf on top of the cr */ + else + if (c == '\0') /* if have cr,nul then */ + goto skipit; /* just skip over the putc */ + /* else just fall through and allow it */ + } + putc(c, file); +skipit: + prevchar = c; + } + return count; +} + + +/* When an error has occurred, it is possible that the two sides + * are out of synch. Ie: that what I think is the other side's + * response to packet N is really their response to packet N-1. + * + * So, to try to prevent that, we flush all the input queued up + * for us on the network connection on our host. + * + * We return the number of packets we flushed (mostly for reporting + * when trace is active). + */ + +int +synchnet(int f) /* socket to flush */ +{ + int pktcount = 0; + char rbuf[PKTSIZE]; + struct sockaddr_in from; + int fromlen; + fd_set socketset; + struct timeval notime; + + while ( 1 ) { + notime.tv_sec = notime.tv_usec = 0; + + FD_ZERO(&socketset); + FD_SET(f, &socketset); + + if ( select(f, &socketset, NULL, NULL, ¬ime) <= 0 ) + break; /* Nothing to read */ + + /* Otherwise drain the packet */ + pktcount++; + fromlen = sizeof from; + (void) recvfrom(f, rbuf, sizeof (rbuf), 0, + (struct sockaddr *)&from, &fromlen); + } + + return pktcount; /* Return packets drained */ +} diff --git a/common/tftpsubs.h b/tftp/tftpsubs.h similarity index 55% rename from common/tftpsubs.h rename to tftp/tftpsubs.h index b3a3bf3..645c6b9 100644 --- a/common/tftpsubs.h +++ b/tftp/tftpsubs.h @@ -1,3 +1,8 @@ +/* $Id$ */ + +/* $OpenBSD: tftpsubs.h,v 1.2 1996/06/26 05:40:37 deraadt Exp $ */ +/* $NetBSD: tftpsubs.h,v 1.2 1994/12/08 09:51:32 jtc Exp $ */ + /* * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. @@ -29,6 +34,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * @(#)tftpsubs.h 8.1 (Berkeley) 6/6/93 */ /* @@ -40,82 +47,30 @@ #include "config.h" -union sock_addr { - struct sockaddr sa; - struct sockaddr_in si; -#ifdef HAVE_IPV6 - struct sockaddr_in6 s6; -#endif -}; - -#define SOCKLEN(sock) \ - (((union sock_addr*)sock)->sa.sa_family == AF_INET ? \ - (sizeof(struct sockaddr_in)) : \ - (sizeof(union sock_addr))) - -#ifdef HAVE_IPV6 -#define SOCKPORT(sock) \ - (((union sock_addr*)sock)->sa.sa_family == AF_INET ? \ - ((union sock_addr*)sock)->si.sin_port : \ - ((union sock_addr*)sock)->s6.sin6_port) -#else -#define SOCKPORT(sock) \ - (((union sock_addr*)sock)->si.sin_port) -#endif - -#ifdef HAVE_IPV6 -#define SOCKADDR_P(sock) \ - (((union sock_addr*)sock)->sa.sa_family == AF_INET ? \ - (void *)&((union sock_addr*)sock)->si.sin_addr : \ - (void *)&((union sock_addr*)sock)->s6.sin6_addr) -#else -#define SOCKADDR_P(sock) \ - (void *)&((union sock_addr*)sock)->si.sin_addr -#endif - -#ifdef HAVE_IPV6 -int is_numeric_ipv6(const char *); -char *strip_address(char *); -#else -#define is_numeric_ipv6(a) 0 -#define strip_address(a) (a) -#endif - -static inline int sa_set_port(union sock_addr *s, u_short port) -{ - switch (s->sa.sa_family) { - case AF_INET: - s->si.sin_port = port; - break; -#ifdef HAVE_IPV6 - case AF_INET6: - s->s6.sin6_port = port; - break; -#endif - default: - return -1; - } - return 0; -} - -int set_sock_addr(char *, union sock_addr *, char **); - struct tftphdr; struct tftphdr *r_init(void); -void read_ahead(FILE *, int); -int readit(FILE *, struct tftphdr **, int); +void read_ahead(FILE *, int); +int readit(FILE *, struct tftphdr **, int); -int synchnet(int); +int synchnet(int); struct tftphdr *w_init(void); -int write_behind(FILE *, int); -int writeit(FILE *, struct tftphdr **, int, int); +int write_behind(FILE *, int); +int writeit(FILE *, struct tftphdr **, int, int); extern int segsize; #define MAX_SEGSIZE 65464 -int pick_port_bind(int sockfd, union sock_addr *myaddr, - unsigned int from, unsigned int to); +/* + * Prototype for xmalloc/xstrdup + */ +extern void *xmalloc(size_t); +extern char *xstrdup(const char *); + +/* + * Signal-related stuff + */ +void (*bsd_signal(int, void (*)(int)))(int); #endif diff --git a/tftpd/Makefile b/tftpd/Makefile index 5963581..f538c97 100644 --- a/tftpd/Makefile +++ b/tftpd/Makefile @@ -1,29 +1,30 @@ -SRCROOT = .. -VERSION = $(shell cat ../version) +all: tftpd --include ../config/MCONFIG +SRCROOT = .. + +-include ../MCONFIG include ../MRULES -OBJS = tftpd.$(O) recvfrom.$(O) misc.$(O) $(TFTPDOBJS) +OBJS = tftpd.o tftpsubs.o recvfrom.o misc.o $(TFTPDOBJS) -all: tftpd$(X) tftpd.8 +tftpd: $(OBJS) + $(CC) $(LDFLAGS) $^ $(LIBS) -o $@ -tftpd$(X): $(OBJS) - $(CC) $(LDFLAGS) $^ $(TFTPD_LIBS) -o $@ +tftpsubs.c: + ln -sf ../tftp/tftpsubs.c . +tftpsubs.h: + ln -sf ../tftp/tftpsubs.h . -$(OBJS): ../common/tftpsubs.h +$(OBJS): tftpsubs.h -tftpd.8: tftpd.8.in ../version - sed -e 's/@@VERSION@@/$(VERSION)/g' < $< > $@ - -install: all +install: tftpd mkdir -p $(INSTALLROOT)$(SBINDIR) $(INSTALLROOT)$(MANDIR)/man8 - $(INSTALL_PROGRAM) tftpd$(X) $(INSTALLROOT)$(SBINDIR)/in.tftpd + $(INSTALL_PROGRAM) tftpd $(INSTALLROOT)$(SBINDIR)/in.tftpd $(INSTALL_DATA) tftpd.8 $(INSTALLROOT)$(MANDIR)/man8/in.tftpd.8 - cd $(INSTALLROOT)$(MANDIR)/man8 && $(LN_S) -f in.tftpd.8 tftpd.8 + ln -sf in.tftpd.8 $(INSTALLROOT)$(MANDIR)/man8/tftpd.8 clean: - rm -f *.o *.obj *.exe tftpd tftpsubs.c tftpsubs.h tftpd.8 + rm -f *.o tftpd tftpsubs.c tftpsubs.h distclean: clean rm -f *~ diff --git a/tftpd/misc.c b/tftpd/misc.c index 9070f96..62f2c07 100644 --- a/tftpd/misc.c +++ b/tftpd/misc.c @@ -1,6 +1,7 @@ +/* $Id$ */ /* ----------------------------------------------------------------------- * - * - * Copyright 2001-2007 H. Peter Anvin - All Rights Reserved + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -14,19 +15,28 @@ * Minor help routines. */ -#include "config.h" /* Must be included first! */ +#include "config.h" /* Must be included first! */ #include +#include #include "tftpd.h" /* - * Set the signal handler and flags, and error out on failure. + * Set the signal handler and flags. Basically a user-friendly + * wrapper around sigaction(). */ -void set_signal(int signum, sighandler_t handler, int flags) +void set_signal(int signum, void (*handler)(int), int flags) { - if (tftp_signal(signum, handler, flags)) { - syslog(LOG_ERR, "sigaction: %m"); - exit(EX_OSERR); - } + 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) ) { + syslog(LOG_ERR, "sigaction: %m"); + exit(EX_OSERR); + } } /* @@ -34,14 +44,14 @@ void set_signal(int signum, sighandler_t handler, int flags) */ void *tfmalloc(size_t size) { - void *p = malloc(size); + void *p = malloc(size); - if (!p) { - syslog(LOG_ERR, "malloc: %m"); - exit(EX_OSERR); - } + if ( !p ) { + syslog(LOG_ERR, "malloc: %m"); + exit(EX_OSERR); + } - return p; + return p; } /* @@ -49,12 +59,13 @@ void *tfmalloc(size_t size) */ char *tfstrdup(const char *str) { - char *p = strdup(str); + char *p = strdup(str); - if (!p) { - syslog(LOG_ERR, "strdup: %m"); - exit(EX_OSERR); - } + if ( !p ) { + syslog(LOG_ERR, "strdup: %m"); + exit(EX_OSERR); + } - return p; + return p; } + diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 320678d..d094a69 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -1,6 +1,7 @@ +/* $Id$ */ /* ----------------------------------------------------------------------- * - * - * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -17,30 +18,28 @@ * */ -#include "config.h" /* Must be included first! */ -#include "common/tftpsubs.h" +#include "config.h" /* Must be included first! */ #include "recvfrom.h" +#include "tftpsubs.h" #ifdef HAVE_MACHINE_PARAM_H -#include /* Needed on some versions of FreeBSD */ +#include /* Needed on some versions of FreeBSD */ #endif #if defined(HAVE_RECVMSG) && defined(HAVE_MSGHDR_MSG_CONTROL) -#ifdef HAVE_SYS_UIO_H -# include -#endif +#include #ifdef IP_PKTINFO -# ifndef HAVE_STRUCT_IN_PKTINFO_IPI_ADDR +# ifndef HAVE_STRUCT_IN_PKTINFO # ifdef __linux__ /* Assume this version of glibc simply lacks the definition */ struct in_pktinfo { - int ipi_ifindex; - struct in_addr ipi_spec_dst; - struct in_addr ipi_addr; + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; }; # else -# undef IP_PKTINFO /* No definition, no way to get it */ +# undef IP_PKTINFO /* No definition, no way to get it */ # endif # endif #endif @@ -52,246 +51,102 @@ struct in_pktinfo { # define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size)) #endif -/* - * 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) +int +myrecvfrom(int s, void *buf, int len, unsigned int flags, + struct sockaddr *from, int *fromlen, + struct sockaddr_in *myaddr) { - 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 (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 (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(sa1.sa.sa_family, SOCK_DGRAM, 0); - if (sockfd < 0) - goto err; - - if (bind(sockfd, &sa1.sa, SOCKLEN(&sa1))) - goto err; - - addrlen = SOCKLEN(addr); - if (getsockname(sockfd, (struct sockaddr *)&sa2, &addrlen)) - goto err; - - 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 (sa2.sa.sa_family == AF_INET6) - rv = IN6_ARE_ADDR_EQUAL(&sa1.s6.sin6_addr, &sa2.s6.sin6_addr); -#endif - else - rv = 0; - -err: - e = errno; - - if (sockfd >= 0) - close(sockfd); - - errno = e; - 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); - } + struct msghdr msg; + struct iovec iov[1]; + int n; + struct cmsghdr *cmptr; + union { + struct cmsghdr cm; +#ifdef IP_PKTINFO + char control[CMSG_SPACE(sizeof(struct in_addr)) + + CMSG_SPACE(sizeof(struct in_pktinfo))]; #else - (void)myaddr; + char control[CMSG_SPACE(sizeof(struct in_addr))]; #endif + } control_un; + int on = 1; +#ifdef IP_PKTINFO + struct in_pktinfo pktinfo; +#endif + + /* Try to enable getting the return address */ +#ifdef IP_RECVDSTADDR + setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); +#endif +#ifdef IP_PKTINFO + setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); +#endif + + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof(control_un.control); + msg.msg_flags = 0; + + msg.msg_name = from; + msg.msg_namelen = *fromlen; + iov[0].iov_base = buf; + iov[0].iov_len = len; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + if ( (n = recvmsg(s, &msg, flags)) < 0 ) + return n; /* Error */ + + *fromlen = msg.msg_namelen; + + if ( myaddr ) { + bzero(myaddr, sizeof(struct sockaddr_in)); + myaddr->sin_family = AF_INET; + + if ( msg.msg_controllen < sizeof(struct cmsghdr) || + (msg.msg_flags & MSG_CTRUNC) ) + return n; /* No information available */ + + for ( cmptr = CMSG_FIRSTHDR(&msg) ; cmptr != NULL ; + cmptr = CMSG_NXTHDR(&msg, cmptr) ) { + +#ifdef IP_RECVSTDADDR + if ( cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_RECVDSTADDR ) { + memcpy(&myaddr->sin_addr, CMSG_DATA(cmptr), + sizeof(struct in_addr)); + } +#endif + +#ifdef IP_PKTINFO + if ( cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_PKTINFO ) { + memcpy(&pktinfo, CMSG_DATA(cmptr), sizeof(struct in_pktinfo)); + memcpy(&myaddr->sin_addr, &pktinfo.ipi_addr, sizeof(struct in_addr)); + } +#endif + + } + } + + return n; } +#else /* pointless... */ + int myrecvfrom(int s, void *buf, int len, unsigned int flags, - union sock_addr *from, union sock_addr *myaddr) + struct sockaddr *from, int *fromlen, + struct sockaddr_in *myaddr) { - struct msghdr msg; - struct iovec iov; - int n; - struct cmsghdr *cmptr; - union { - struct cmsghdr cm; -#ifdef IP_PKTINFO - char control[CMSG_SPACE(sizeof(struct in_addr)) + - CMSG_SPACE(sizeof(struct in_pktinfo))]; -#else - char control[CMSG_SPACE(sizeof(struct in_addr))]; -#endif -#ifdef HAVE_IPV6 -#ifdef HAVE_STRUCT_IN6_PKTINFO - char control6[CMSG_SPACE(sizeof(struct in6_addr)) + - CMSG_SPACE(sizeof(struct in6_pktinfo))]; -#else - char control6[CMSG_SPACE(sizeof(struct in6_addr))]; -#endif -#endif - } control_un; - int on = 1; -#ifdef IP_PKTINFO - struct in_pktinfo pktinfo; -#endif -#ifdef HAVE_STRUCT_IN6_PKTINFO - struct in6_pktinfo pktinfo6; -#endif + /* There is no way we can get the local address, fudge it */ - /* Try to enable getting the return address */ -#ifdef IP_RECVDSTADDR - if (from->sa.sa_family == AF_INET) - setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); -#endif -#ifdef IP_PKTINFO - 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.sa_family == AF_INET6) - setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); -#endif -#endif - bzero(&msg, sizeof msg); /* Clear possible system-dependent fields */ - msg.msg_control = control_un.control; - msg.msg_controllen = sizeof(control_un); - msg.msg_flags = 0; + bzero(myaddr, sizeof(struct sockaddr_in)); + myaddr->sin_family = AF_INET; - msg.msg_name = &from->sa; - msg.msg_namelen = sizeof(*from); - iov.iov_base = buf; - iov.iov_len = len; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; + myaddr->sin_port = htons(IPPORT_TFTP); + bzero(&myaddr->sin_addr, sizeof(myaddr->sin_addr)); - if ((n = recvmsg(s, &msg, flags)) < 0) - return n; /* Error */ - - if (myaddr) { - bzero(myaddr, sizeof(*myaddr)); - myaddr->sa.sa_family = from->sa.sa_family; - - if (msg.msg_controllen < sizeof(struct cmsghdr) || - (msg.msg_flags & MSG_CTRUNC)) - return n; /* No information available */ - - for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; - cmptr = CMSG_NXTHDR(&msg, cmptr)) { - - if (from->sa.sa_family == AF_INET) { - myaddr->sa.sa_family = AF_INET; -#ifdef IP_RECVDSTADDR - if (cmptr->cmsg_level == IPPROTO_IP && - cmptr->cmsg_type == IP_RECVDSTADDR) { - memcpy(&myaddr->si.sin_addr, CMSG_DATA(cmptr), - sizeof(struct in_addr)); - } -#endif - -#ifdef IP_PKTINFO - if (cmptr->cmsg_level == IPPROTO_IP && - cmptr->cmsg_type == IP_PKTINFO) { - memcpy(&pktinfo, CMSG_DATA(cmptr), - sizeof(struct in_pktinfo)); - memcpy(&myaddr->si.sin_addr, &pktinfo.ipi_addr, - sizeof(struct in_addr)); - } -#endif - } -#ifdef HAVE_IPV6 - else if (from->sa.sa_family == AF_INET6) { - myaddr->sa.sa_family = AF_INET6; -#ifdef IP6_RECVDSTADDR - if (cmptr->cmsg_level == IPPROTO_IPV6 && - cmptr->cmsg_type == IPV6_RECVDSTADDR ) - memcpy(&myaddr->s6.sin6_addr, CMSG_DATA(cmptr), - sizeof(struct in6_addr)); -#endif - -#ifdef HAVE_STRUCT_IN6_PKTINFO - if (cmptr->cmsg_level == IPPROTO_IPV6 && - ( -#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)); - } -#endif - } -#endif - } - - normalize_ip6_compat(myaddr); - - /* If the address is not a valid local address, - * then bind to any address... - */ - if (address_is_local(myaddr) != 1) { - if (myaddr->sa.sa_family == AF_INET) - ((struct sockaddr_in *)myaddr)->sin_addr.s_addr = INADDR_ANY; -#ifdef HAVE_IPV6 - else if (myaddr->sa.sa_family == AF_INET6) - memset(&myaddr->s6.sin6_addr, 0, sizeof(struct in6_addr)); -#endif - } - } - - normalize_ip6_compat(from); - - return n; -} - -#else /* pointless... */ - -int -myrecvfrom(int s, void *buf, int len, unsigned int flags, - 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.sa_family; - sa_set_port(myaddr, htons(IPPORT_TFTP)); - - return recvfrom(s, buf, len, flags, &from->sa, &fromlen); + return recvfrom(s,buf,len,flags,from,fromlen); } #endif diff --git a/tftpd/recvfrom.h b/tftpd/recvfrom.h index 7773a0d..1de145e 100644 --- a/tftpd/recvfrom.h +++ b/tftpd/recvfrom.h @@ -1,6 +1,7 @@ +/* $Id$ */ /* ----------------------------------------------------------------------- * - * - * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -15,8 +16,14 @@ * */ -#include "config.h" +#define _XPG4_2 /* Needed on Solaris */ +#include +#include +#include +#include +#include int myrecvfrom(int s, void *buf, int len, unsigned int flags, - union sock_addr *from, union sock_addr *myaddr); + struct sockaddr *from, int *fromlen, + struct sockaddr_in *myaddr); diff --git a/tftpd/remap.c b/tftpd/remap.c index 413d117..ee93c98 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -1,6 +1,7 @@ +/* $Id$ */ /* ----------------------------------------------------------------------- * - * - * Copyright 2001-2024 H. Peter Anvin - All Rights Reserved + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -14,7 +15,7 @@ * Perform regular-expression based filename remapping. */ -#include "config.h" /* Must be included first! */ +#include "config.h" /* Must be included first! */ #include #include #include @@ -22,524 +23,317 @@ #include "tftpd.h" #include "remap.h" -#define DEADMAN_MAX_STEPS 4096 /* Timeout after this many steps */ -#define MAXLINE 16384 /* Truncate a line at this many bytes */ +#define DEADMAN_MAX_STEPS 1024 /* Timeout after this many steps */ +#define MAXLINE 16384 /* Truncate a line at this many bytes */ -#define RULE_REWRITE 0x01 /* This is a rewrite rule */ -#define RULE_GLOBAL 0x02 /* Global rule (repeat until no match) */ -#define RULE_EXIT 0x04 /* Exit after matching this rule */ -#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; +#define RULE_REWRITE 0x01 /* This is a rewrite rule */ +#define RULE_GLOBAL 0x02 /* Global rule (repeat until no match) */ +#define RULE_EXIT 0x04 /* Exit after matching this rule */ +#define RULE_RESTART 0x08 /* Restart at the top after matching this rule */ +#define RULE_ABORT 0x10 /* Terminate processing with an error */ +#define RULE_GETONLY 0x20 /* Applicable to GET only */ +#define RULE_PUTONLY 0x40 /* Applicable to PUT only */ struct rule { - struct rule *next; - int nrule; - unsigned int rule_flags; - regex_t rx; - const char *pattern; + struct rule *next; + int nrule; + int rule_flags; + regex_t rx; + const char *pattern; }; -static int xform_null(int 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) { - return c; -} + int len = 0; + int n, mlen; + int endbytes; -static int xform_toupper(int c) -{ - return toupper(c); -} + /* Get section before match; note pmatch[0] is the whole match */ + endbytes = strlen(input) - pmatch[0].rm_eo; + len = pmatch[0].rm_so + endbytes; + if ( string ) { + memcpy(string, input, pmatch[0].rm_so); + string += pmatch[0].rm_so; + } -static int xform_tolower(int c) -{ - return tolower(c); -} - -/* - * 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 = start + pmatch[0].rm_so; - if (string) { - /* Copy the prefix before "start" as well! */ - memcpy(string, ibuf, start + pmatch[0].rm_so); - string += start + pmatch[0].rm_so; + /* Transform matched section */ + while ( *pattern ) { + if ( *pattern == '\\' && pattern[1] != '\0' ) { + if ( pattern[1] < '0' || pattern[1] > '9' ) { + len++; + if ( string ) + *string++ = pattern[1]; + } else { + n = pattern[1] - '0'; + + if ( pmatch[n].rm_so != -1 ) { + mlen = pmatch[n].rm_eo - pmatch[n].rm_so; + len += mlen; + if ( string ) { + memcpy(string, input+pmatch[n].rm_so, mlen); + string += mlen; + } + } + } + pattern += 2; + } else { + len++; + if ( string ) + *string++ = *pattern; + pattern++; } + } - /* Transform matched section */ - while (*pattern) { - mlen = 0; + /* Copy section after match */ + if ( string ) { + memcpy(string, input+pmatch[0].rm_eo, endbytes); + string[endbytes] = '\0'; + } - if (*pattern == '\\' && pattern[1] != '\0') { - char macro = pattern[1]; - switch (macro) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - n = pattern[1] - '0'; - - if (pmatch[n].rm_so != -1) { - mlen = pmatch[n].rm_eo - pmatch[n].rm_so; - len += mlen; - if (string) { - const char *p = input + start + pmatch[n].rm_so; - while (mlen--) - *string++ = xform(*p++); - } - } - break; - - case 'L': - xform = xform_tolower; - break; - - case 'U': - xform = xform_toupper; - break; - - case 'E': - xform = xform_null; - break; - - default: - if (macrosub && (sublen = macrosub(macro, string)) >= 0) { - while (sublen--) { - len++; - if (string) { - *string = xform(*string); - string++; - } - } - } else { - len++; - if (string) - *string++ = xform(pattern[1]); - } - } - pattern += 2; - } else { - len++; - if (string) - *string++ = xform(*pattern); - 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'; - } - - return len; + 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, - * functionally \n. - */ +/* Extract a string terminated by non-escaped whitespace; ignore leading whitespace */ +/* Consider an unescaped # to be a comment marker, functionally \n */ static int readescstring(char *buf, char **str) { - char *p = *str; - int wasbs = 0, len = 0; + char *p = *str; + int wasbs = 0, len = 0; - while (*p && isspace(*p)) - p++; - - if (!*p) { - *buf = '\0'; - *str = p; - return 0; - } - - while (*p) { - if (!wasbs && (isspace(*p) || *p == '#')) { - *buf = '\0'; - *str = p; - return len; - } - /* Important: two backslashes leave us in the !wasbs state! */ - wasbs = !wasbs && (*p == '\\'); - *buf++ = *p++; - len++; - } + while ( *p && isspace(*p) ) + p++; + if ( ! *p ) { *buf = '\0'; *str = p; - return len; + return 0; + } + + while ( *p ) { + if ( !wasbs && (isspace(*p) || *p == '#') ) { + *buf = '\0'; + *str = p; + return len; + } + /* Important: two backslashes leave us in the !wasbs state! */ + wasbs = !wasbs && ( *p == '\\' ); + *buf++ = *p++; + len++; + } + + *buf = '\0'; + *str = p; + return len; } /* Parse a line into a set of instructions */ static int parseline(char *line, struct rule *r, int lineno) { - char buffer[MAXLINE]; - char *p; - int rv; - int rxflags = REG_EXTENDED; - static int nrule; + char buffer[MAXLINE]; + char *p; + int rv; + int rxflags = REG_EXTENDED; + static int nrule; - memset(r, 0, sizeof *r); - r->nrule = nrule; + memset(r, 0, sizeof r); + r->nrule = nrule; - if (!readescstring(buffer, &line)) - return 0; /* No rule found */ + if ( !readescstring(buffer, &line) ) + return 0; /* No rule found */ - for (p = buffer; *p; p++) { - switch (*p) { - case 'r': - r->rule_flags |= RULE_REWRITE; - break; - case 'g': - 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; - case 'a': - r->rule_flags |= RULE_ABORT; - break; - case 'i': - rxflags |= REG_ICASE; - break; - case '~': - r->rule_flags |= RULE_INVERSE; - break; - 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, - "Remap command \"%s\" on line %d contains invalid char \"%c\"", - buffer, lineno, *p); - return -1; /* Error */ - break; - } + for ( p = buffer ; *p ; p++ ) { + switch(*p) { + case 'r': + r->rule_flags |= RULE_REWRITE; + break; + case 'g': + r->rule_flags |= RULE_GLOBAL; + break; + case 'e': + r->rule_flags |= RULE_EXIT; + break; + case 's': + r->rule_flags |= RULE_RESTART; + break; + case 'a': + r->rule_flags |= RULE_ABORT; + break; + case 'i': + rxflags |= REG_ICASE; + break; + case 'G': + r->rule_flags |= RULE_GETONLY; + break; + case 'P': + r->rule_flags |= RULE_PUTONLY; + break; + default: + syslog(LOG_ERR, "Remap command \"%s\" on line %d contains invalid char \"%c\"", + buffer, lineno, *p); + return -1; /* Error */ + break; } + } - 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); - } + /* RULE_GLOBAL only applies when RULE_REWRITE specified */ + if ( !(r->rule_flags & RULE_REWRITE) ) + r->rule_flags &= ~RULE_GLOBAL; - /* Read and compile the regex */ - if (!readescstring(buffer, &line)) { - syslog(LOG_ERR, "No regex on remap line %d: %s\n", lineno, line); - return -1; /* Error */ - } + /* Read and compile the regex */ + if ( !readescstring(buffer, &line) ) { + syslog(LOG_ERR, "No regex on remap line %d: %s\n", lineno, line); + return -1; /* Error */ + } - if ((rv = regcomp(&r->rx, buffer, rxflags)) != 0) { - char errbuf[BUFSIZ]; - regerror(rv, &r->rx, errbuf, BUFSIZ); - syslog(LOG_ERR, "Bad regex in remap line %d: %s\n", lineno, - errbuf); - return -1; /* Error */ - } + if ( (rv = regcomp(&r->rx, buffer, rxflags)) != 0 ) { + char errbuf[BUFSIZ]; + regerror(rv, &r->rx, errbuf, BUFSIZ); + syslog(LOG_ERR, "Bad regex in remap line %d: %s\n", lineno, errbuf); + return -1; /* Error */ + } - /* Read the rewrite pattern, if any */ - if (readescstring(buffer, &line)) { - r->pattern = tfstrdup(buffer); - } else { - r->pattern = ""; - } + /* Read the rewrite pattern, if any */ + if ( readescstring(buffer, &line) ) { + r->pattern = tfstrdup(buffer); + } else { + r->pattern = ""; + } - nrule++; - return 1; /* Rule found */ + nrule++; + return 1; /* Rule found */ } /* Read a rule file */ -struct rule *parserulefile(FILE * f) +struct rule *parserulefile(FILE *f) { - char line[MAXLINE]; - struct rule *first_rule = NULL; - struct rule **last_rule = &first_rule; - struct rule *this_rule = tfmalloc(sizeof(struct rule)); - int rv; - int lineno = 0; - int err = 0; + char line[MAXLINE]; + struct rule *first_rule = NULL; + struct rule **last_rule = &first_rule; + struct rule *this_rule = tfmalloc(sizeof(struct rule)); + int rv; + int lineno = 0; + int err = 0; - while (lineno++, fgets(line, MAXLINE, f)) { - rv = parseline(line, this_rule, lineno); - if (rv < 0) - err = 1; - if (rv > 0) { - *last_rule = this_rule; - last_rule = &this_rule->next; - this_rule = tfmalloc(sizeof(struct rule)); - } + while ( lineno++, fgets(line, MAXLINE, f) ) { + rv = parseline(line, this_rule, lineno); + if ( rv < 0 ) + err = 1; + if ( rv > 0 ) { + *last_rule = this_rule; + last_rule = &this_rule->next; + this_rule = tfmalloc(sizeof(struct rule)); } + } - free(this_rule); /* Last one is always unused */ + free(this_rule); /* Last one is always unused */ - if (err) { - /* Bail on error, we have already logged an error message */ - exit(EX_CONFIG); - } + if ( err ) { + /* Bail on error, we have already logged an error message */ + exit(EX_CONFIG); + } - return first_rule; + return first_rule; } /* Destroy a rule file data structure */ void freerules(struct rule *r) { - struct rule *next; + struct rule *next; - while (r) { - next = r->next; + while ( r ) { + next = r->next; - regfree(&r->rx); + regfree(&r->rx); - /* "" patterns aren't allocated by malloc() */ - if (r->pattern && *r->pattern) - free((void *)r->pattern); + /* "" patterns aren't allocated by malloc() */ + if ( r->pattern && *r->pattern ) + free((void *)r->pattern); + + free(r); - free(r); - - r = next; - } + r = next; + } } /* Execute a rule set on a string; returns a malloc'd new string. */ -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 *rewrite_string(const char *input, const struct rule *rules, int is_put) { - char *current = tfstrdup(input); - 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 matchsense; - int pmatches; - unsigned int bad_flags; - int ggoffset; + char *current = tfstrdup(input); + char *newstr; + const struct rule *ruleptr = rules; + regmatch_t pmatch[10]; + int len; + int was_match = 0; + int deadman = DEADMAN_MAX_STEPS; - /* Default error */ - *errmsg = "Remap table failure"; + if ( verbosity >= 3 ) { + syslog(LOG_INFO, "remap: input: %s", current); + } - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: input: %s", current); + for ( ruleptr = rules ; ruleptr ; ruleptr = ruleptr->next ) { + if ( ((ruleptr->rule_flags & RULE_GETONLY) && is_put) || + ((ruleptr->rule_flags & RULE_PUTONLY) && !is_put) ) { + 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; - - 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 (!deadman--) - goto dead; - - if (regexec(&ruleptr->rx, current, pmatches, pmatch, 0) - != matchsense) - break; /* No match, break out of do loop */ - - /* Match on this rule */ - was_match = 1; - - 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 */ - genmatchstring(&newstr, ruleptr->pattern, current, - pmatch, macrosub, 0, NULL); - *errmsg = newstr; - } else { - *errmsg = NULL; - } - free(current); - return (NULL); - } - - if (ruleptr->rule_flags & RULE_REWRITE) { - len = genmatchstring(&newstr, ruleptr->pattern, current, - pmatch, macrosub, 0, &ggoffset); - - 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; - } - } - - 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); - } - } + if ( ! deadman-- ) { + syslog(LOG_WARNING, "remap: Breaking loop, input = %s, last = %s", + input, current); + free(current); + return NULL; /* Did not terminate! */ } - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: done"); - } - return current; + do { + if ( regexec(&ruleptr->rx, current, 10, pmatch, 0) == 0 ) { + /* Match on this rule */ + was_match = 1; + + if ( ruleptr->rule_flags & RULE_ABORT ) { + if ( verbosity >= 3 ) { + syslog(LOG_INFO, "remap: rule %d: abort: %s", + ruleptr->nrule, current); + } + free(current); + return(NULL); + } + + if ( ruleptr->rule_flags & RULE_REWRITE ) { + len = genmatchstring(NULL, ruleptr->pattern, current, pmatch); + newstr = tfmalloc(len+1); + genmatchstring(newstr, ruleptr->pattern, current, pmatch); + free(current); + current = newstr; + if ( verbosity >= 3 ) { + syslog(LOG_INFO, "remap: rule %d: rewrite: %s", + ruleptr->nrule, current); + } + } + } else { + break; /* No match, terminate unconditionally */ + } + /* If the rule is global, keep going until no match */ + } while ( ruleptr->rule_flags & RULE_GLOBAL ); + + if ( was_match ) { + was_match = 0; -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! */ + if ( ruleptr->rule_flags & RULE_EXIT ) { + 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); + } + } + } + } + + if ( verbosity >= 3 ) { + syslog(LOG_INFO, "remap: done"); + } + return current; } diff --git a/tftpd/remap.h b/tftpd/remap.h index c8d76ff..a562df5 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -1,6 +1,7 @@ +/* $Id$ */ /* ----------------------------------------------------------------------- * - * - * Copyright 2001-2007 H. Peter Anvin - All Rights Reserved + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -17,17 +18,15 @@ #ifndef TFTPD_REMAP_H #define TFTPD_REMAP_H +#include +#include +#include "../config.h" + /* Opaque type */ struct rule; #ifdef WITH_REGEX -/* This is called when we encounter a substitution like \i. The - macro character is passed as the first argument; the output buffer, - if any, is passed as the second argument. The function should return - the number of characters output, or -1 on failure. */ -typedef int (*match_pattern_callback) (char, char *); - /* Read a rule file */ struct rule *parserulefile(FILE *); @@ -35,13 +34,8 @@ struct rule *parserulefile(FILE *); void freerules(struct rule *); /* Execute a rule set on a string; returns a malloc'd new string. */ -struct formats; -char *rewrite_string(const struct formats *, const char *, - const struct rule *, int, int, - match_pattern_callback, const char **); +char *rewrite_string(const char *, const struct rule *, int); -/* Remapping deadman counter */ -extern int deadman_max_steps; +#endif /* WITH_REGEX */ +#endif /* TFTPD_REMAP_H */ -#endif /* WITH_REGEX */ -#endif /* TFTPD_REMAP_H */ diff --git a/tftpd/sample.rules b/tftpd/sample.rules index 55b56be..7b59e70 100644 --- a/tftpd/sample.rules +++ b/tftpd/sample.rules @@ -1,3 +1,4 @@ +# $Id$ # # Sample rule file for the -m (remapping option) # diff --git a/tftpd/tftpd.8 b/tftpd/tftpd.8 new file mode 100644 index 0000000..505bacf --- /dev/null +++ b/tftpd/tftpd.8 @@ -0,0 +1,262 @@ +.\" tftp-hpa: $Id$ +.\" $OpenBSD: tftpd.8,v 1.7 1999/07/09 13:35:51 aaron Exp $ +.\" +.\" Copyright (c) 1983, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)tftpd.8 6.7 (Berkeley) 5/13/91 +.\" $OpenBSD: tftpd.8,v 1.7 1999/07/09 13:35:51 aaron Exp $ +.\" +.Dd August 6, 2001 +.Dt TFTPD 8 +.Os +.Sh NAME +.Nm tftpd +.Nd +IPv4 Trivial File Transfer Protocol server +.Sh SYNOPSIS +.Nm in.tftpd +.Op Fl v +.Op Fl c +.Op Fl l +.Op Fl a Ar [address][:port] +.Op Fl m Ar mapfile +.Op Fl u Ar userid +.Op Fl t Ar timeout +.Op Fl r Ar option... +.Op Fl s +.Op Ar directory +.Sh DESCRIPTION +.Nm +is a server which supports the +.Tn DARPA +Trivial File Transfer +Protocol. +The +.Tn TFTP +server operates +at the port indicated in the +.Ql tftp +service description; +see +.Xr services 5 . +The server is normally started by +.Xr inetd 8 , +but can also run standalone. +.Pp +The use of +.Xr tftp 1 +does not require an account or password on the remote system. +Due to the lack of authentication information, +.Nm +will allow only publicly readable files to be +accessed. +Files may be written only if they already exist and are publicly writable. +Note that this extends the concept of +.Dq public +to include +all users on all hosts that can be reached through the network; +this may not be appropriate on all systems, and its implications +should be considered before enabling tftp service. +The server should have the user ID with the lowest possible privilege +(see the +.Fl u +flag below.) +.Pp +Access to files may be restricted by invoking +.Nm +with a list of directories by including pathnames +as server program arguments in +.Pa /etc/inetd.conf +or on the standalone server command line. In this case access is +restricted to files whose names are prefixed by the one of the given +directories. +.Pp +If the +.Fl l +flag is used, the server runs in standalone (listen) mode. In listen +mode, the +.Fl t +option is ignored, and the +.Fl a +option can be used to specify a specific local address or port to +listen to. +.Pp +If the +.Fl c +flag is used, +.Nm +will allow new files to be created; otherwise uploaded files must already +exist. Files are created with default permissions allowing anyone to read +or write to them. +.Pp +When using the +.Fl s +flag with a directory name, +.Nm +will +.Xr chroot 2 +on startup; therefore the remote host is not expected to pass the +directory as part of the file name to transfer. This option is +recommended for security, as well as compatibility with boot ROMs +which do not include a directory name. +.Pp +The +.Fl u +option can be used to specify a user ID which +.Nm +will run as; the default is ``nobody''. +.Pp +The +.Fl t +option specifies the server timeout in inetd mode. +.Pp +The +.Fl m +flag specifies a file which contains filename remapping rules. +.Pp +The +.Fl v +flag increases the logging verbosity of +.Nm tftpd , +it can be specified multiple times. +.Pp +This version of +.Nm +supports RFC 2347 option negotiation; the current version supports the +.Pa blksize +(RFC 2348), +.Pa tsize , +(RFC 2349), and +.Pa timeout +(RFC 2349) options. The +.Fl r +flag can be used to disable options individually; this may allow +working around client bugs. +.Sh FILENAME REMAPPING +The +.Fl m +option specifies a file which contains filename remapping rules. Each +non-comment line (comments begin with hash marks, #) contains an +.Ar operation , +a +.Ar regex , +a regular expression in the style of +.Xr egrep 1 , +and optionally a +.Ar "replacement pattern" . +The operation indicated by +.Ar operation +is performed if the +.Ar regex +matches all or part of the filename. Rules are processed from the top +down, and by default, all rules are processed even if there is a +match. +.Pp +The +.Ar operation +can be any combination of the following letters: +.Pp +.Bl -tag -width verbose -compact +.It Ic r +Replace the substring matched by +.Ar regex +by the +.Ar "replacement pattern" . +The escape sequence +\\0 +can be used to copy the entire matched string, and the sequences +\\1 to \\9 +copies parenthesized subexpressions. To specify a backslash, white +space or hash mark, you need to \\-escape it. +.Pp +.It Ic g +Repeat this rule until it no longer matches. This is always used with +.Ic r . +.Pp +.It Ic i +Match the +.Ar regex +case-insensitively. By default it is case sensitive. +.Pp +.It Ic e +If this rule matches, end rule processing after executing the rule. +.Pp +.It Ic s +If this rule matches, start rule processing over from the very first +rule after executing this rule. +.Pp +.It Ic a +If this rule matches, refuse the request and send an access denied +error to the client. +.Pp +.It Ic G +This rule applies to GET (RRQ) requests only. +.Pp +.It Ic P +This rule applies to PUT (WRQ) requests only. +.El +.Pp +If the mapping file is changed, you need to send SIGHUP +(kill -HUP) to any outstanding +.Nm +process. +.Sh SEE ALSO +.Xr tftp 1 , +.Xr egrep 1 , +.Xr regex 7 , +.Xr inetd 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +.Pp +The +.Fl s +flag appeared in NetBSD 0.9a. +.Pp +The +.Fl c +flag was added in OpenBSD 2.1 . +.Pp +The +.Fl r +flag and RFC 2347 options were added by H. Peter Anvin based on +patches by Markus Gutschke and Gero Kulhman. +.Pp +The +.Fl u , +.Fl v +and +.Fl m +flags were added by H. Peter Anvin. + diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in deleted file mode 100644 index 3d43325..0000000 --- a/tftpd/tftpd.8.in +++ /dev/null @@ -1,457 +0,0 @@ -.\" -*- nroff -*- --------------------------------------------------------- * -.\" -.\" Copyright (c) 1990, 1993, 1994 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" Copyright 2001-2009 H. Peter Anvin - All Rights Reserved -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\"----------------------------------------------------------------------- */ -.TH TFTPD 8 "7 June 2014" "tftp-hpa @@VERSION@@" "System Manager's Manual" -.SH NAME -.B tftpd -\- Trivial File Transfer Protocol server -.SH SYNOPSIS -.B in.tftpd -.RI [ options... ] -.I directory... -.SH DESCRIPTION -.B tftpd -is a server for the Trivial File Transfer Protocol. The TFTP -protocol is extensively used to support remote booting of diskless -devices. The server is normally started by -.BR inetd , -but can also run standalone. -.PP -.SH OPTIONS -.TP -\fB\-\-ipv4\fP, \fB\-4\fP -Connect with IPv4 only, even if IPv6 support was compiled in. -.TP -\fB\-\-ipv6\fP, \fB\-6\fP -Connect with IPv6 only, if compiled in. -.TP -\fB\-l\fP, \fB\-\-listen\fP -Run the server in standalone (listen) mode, rather than run from -.BR inetd . -In listen mode, the -.B \-\-timeout -option is ignored, and the -.B \-\-address -option can be used to specify a specific local address or port to -listen to. -.TP -\fB\-\-foreground\fP, \fB\-L\fP -Similar to -.B \-\-listen -but do not detach from the foreground process. Implies -.BR \-\-listen . -.TP -\fB\-\-address\fP \fI[address][:port]\fP, \fB\-a\fP \fI[address][:port]\fP -Specify a specific -.I address -and -.I port -to listen to when called with the -.B \-\-listen -or -.B \-\-foreground -option. The default is to listen to the -.I tftp -port specified in -.I /etc/services -on all local addresses. - -.B Please note: -Numeric IPv6 adresses must be enclosed in square brackets -to avoid ambiguity with the optional port information. -.TP -\fB\-\-create\fP, \fB\-c\fP -Allow new files to be created. By default, -.B tftpd -will only allow upload of files that already exist. Files are created -with default permissions allowing anyone to read or write them, unless -the -.B \-\-permissive -or -.B \-\-umask -options are specified. -.TP -\fB\-\-secure\fP, \fB\-s\fP -Change root directory on startup. This means the remote host does not -need to pass along the directory as part of the transfer, and may add -security. When -.B \-\-secure -is specified, exactly one -.I directory -should be specified on the command line. The use of this option is -recommended for security as well as compatibility with some boot ROMs -which cannot be easily made to include a directory name in its request. -.TP -\fB\-\-user\fP \fIusername\fP, \fB\-u\fP \fIusername\fP -Specify the username which -.B tftpd -will run as; the default is "nobody". The user ID, group ID, and (if -possible on the platform) the supplementary group IDs will be set to -the ones specified in the system permission database for this -username. -.TP -\fB\-\-umask\fP \fIumask\fP, \fB\-U\fP \fIumask\fP -Sets the \fIumask\fP for newly created files to the specified value. -The default is zero (anyone can read or write) if the -.B \-\-permissive -option is not specified, or inherited from the invoking process if -.B \-\-permissive -is specified. -.TP -\fB\-\-permissive\fP, \fB\-p\fP -Perform no additional permissions checks above the normal -system-provided access controls for the user specified via the -.B \-\-user -option. -.TP -\fB\-\-pidfile\fP \fIpidfile\fP, \fB\-P\fP \fIpidfile\fP -When run in standalone mode, write the process ID of the listening -server into \fIpidfile\fP. On normal termination (SIGTERM or SIGINT) -the pid file is automatically removed. -.TP -\fB\-\-timeout\fP \fItimeout\fP, \fB\-t\fP \fItimeout\fP -When run from -.B inetd -this specifies how long, in seconds, to wait for a second connection -before terminating the server. -.B inetd -will then respawn the server when another request comes in. The -default is 900 (15 minutes.) -.TP -\fB\-\-retransmit\fP \fItimeout, \fP\fB\-T\fP \fItimeout\fP -Determine the default timeout, in microseconds, before the first -packet is retransmitted. This can be modified by the client if the -.B timeout -or -.B utimeout -option is negotiated. The default is 1000000 (1 second.) -.TP -\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 -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 . -This flag can be specified multiple times for even higher verbosity. -.TP -\fB\-\-verbosity\fP \fIvalue\fP -Set the verbosity value to \fIvalue\fP. -.TP -\fB\-\-refuse\fP \fItftp-option\fP, \fB\-r\fP \fItftp-option\fP -Indicate that a specific RFC 2347 TFTP option should never be -accepted. -.TP -\fB\-\-blocksize\fP \fImax-block-size\fP, \fB\-B\fP \fImax-block-size\fP -Specifies the maximum permitted block size. The permitted range for -this parameter is from 512 to 65464. Some embedded clients request -large block sizes and yet do not handle fragmented packets correctly; -for these clients, it is recommended to set this value to the smallest -MTU on your network minus 32 bytes (20 bytes for IP, 8 for UDP, and 4 -for TFTP; less if you use IP options on your network.) For example, -on a standard Ethernet (MTU 1500) a value of 1468 is reasonable. -.TP -\fB\-\-port-range\fP \fIport:port\fP, \fB\-R\fP \fIport:port\fP -Force the server port number (the Transaction ID) to be in the -specified range of port numbers. -.TP -\fB\-\-version\fP, \fB\-V\fP -Print the version number and configuration to standard output, then -exit gracefully. -.SH "RFC 2347 OPTION NEGOTIATION" -This version of -.B tftpd -supports RFC 2347 option negotation. Currently implemented options -are: -.TP -\fBblksize\fP (RFC 2348) -Set the transfer block size to anything less than or equal to the -specified option. This version of -.B tftpd -can support any block size up to the theoretical maximum of 65464 -bytes. -.TP -\fBblksize2\fP (nonstandard) -Set the transfer block size to anything less than or equal to the -specified option, but restrict the possible responses to powers of 2. -The maximum is 32768 bytes (the largest power of 2 less than or equal -to 65464.) -.TP -\fBtsize\fP (RFC 2349) -Report the size of the file that is about to be transferred. This -version of -.B tftpd -only supports the -.B tsize -option for binary (octet) mode transfers. -.TP -\fBtimeout\fP (RFC 2349) -Set the time before the server retransmits a packet, in seconds. -.TP -\fButimeout\fP (nonstandard) -Set the time before the server retransmits a packet, in microseconds. -.TP -\fBrollover\fP (nonstandard) -Set the block number to resume at after a block number rollover. The -default and recommended value is zero. -.PP -The -.B \-\-refuse -option can be used to disable specific options; this may be necessary -to work around bugs in specific TFTP client implementations. For -example, some TFTP clients have been found to request the -.B blksize -option, but crash with an error if they actually get the option -accepted by the server. -.SH "FILENAME REMAPPING" -The -.B \-\-map-file -option specifies a file which contains filename remapping rules. Each -non-comment line (comments begin with hash marks, -.BR # ) -contains an -.IR operation , -specified below; a -.IR regex , -a regular expression in the style of -.BR egrep ; -and optionally a -.IR "replacement pattern" . -The operation indicated by -.I operation -is performed if the -.I regex -matches all or part of the filename. Rules are processed from the top -down, and by default, all rules are processed even if there is a -match. -.PP -The -.I operation -can be any combination of the following letters: -.TP -.B r -Replace the substring matched by -.I regex -by the -.IR "replacement pattern" . -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 -.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 -Match the -.I regex -case-insensitively. By default it is case sensitive. -.TP -.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. -.TP -.B a -If this rule matches, refuse the request and send an access denied -error to the client. -.TP -.B G -This rule applies to GET (RRQ) requests only. -.TP -.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 -.BR r . -.PP -The following escape sequences are recognized as part of a -.IR "replacement pattern" : -.TP -\fB\\0\fP -The entire string matched by the -.IR regex . -.TP -\fB\\1\fP to \fB\\9\fP -The strings matched by each of the first nine parenthesized -subexpressions, \\( ... \\), of the -.I regex -pattern. -.TP -\fB\\i\fP -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 expanded hexadecimal -notation (e.g. C00002A9 for IPv4, or 20010DB8000000000000000000000001 -for IPv6). -.TP -\fB\\\\\fP -Literal backslash. -.TP -\fB\\\fP\fIwhitespace\fP -Literal whitespace. -.TP -\fB\\#\fP -Literal hash mark. -.TP -\fB\\U\fP -Turns all subsequent letters to upper case. -.TP -\fB\\L\fP -Turns all subsequent letters to lower case. -.TP -\fB\\E\fP -Cancels the effect of \fB\\U\fP or \fB\\L\fP. -.PP -If the mapping file is changed, you need to send -.B SIGHUP -to any outstanding -.B tftpd -process. -.SH "SECURITY" -The use of TFTP services does not require an account or password on -the server system. Due to the lack of authentication information, -.B tftpd -will allow only publicly readable files (o+r) to be accessed, unless the -.B \-\-permissive -option is specified. Files may be written only if they already exist -and are publicly writable, unless the -.B \-\-create -option is specified. Note that this extends the concept of ``public'' -to include all users on all hosts that can be reached through the -network; this may not be appropriate on all systems, and its -implications should be considered before enabling TFTP service. -Typically, some kind of firewall or packet-filter solution should be -employed. If appropriately compiled (see the output of -.BR "in.tftpd \-\-version" ) -.B tftpd -will query the -.BR hosts_access (5) -database for access control information. This may be slow; sites -requiring maximum performance may want to compile without this option -and rely on firewalling or kernel-based packet filters instead. -.PP -The server should be set to run as the user with the lowest possible -privilege; please see the -.B \-\-user -flag. It is probably a good idea to set up a specific user account for -.BR tftpd , -rather than letting it run as "nobody", to guard against privilege -leaks between applications. -.PP -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 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 ( \-\-map-file -flag) support can be used to provide a limited amount of additional -access control. -.SH "CONFORMING TO" -RFC 1123, -.IR "Requirements for Internet Hosts \- Application and Support" . -.br -RFC 1350, -.IR "The TFTP Protocol (revision 2)" . -.br -RFC 2347, -.IR "TFTP Option Extension" . -.br -RFC 2348, -.IR "TFTP Blocksize Option" . -.br -RFC 2349, -.IR "TFTP Timeout Interval and Transfer Size Options" . -.SH "AUTHOR" -This version of -.B tftpd -is maintained by H. Peter Anvin . It was derived from, -but has substantially diverged from, an OpenBSD source base, with -added patches by Markus Gutschke and Gero Kulhman. -.SH "SEE ALSO" -.BR tftp (1), -.BR egrep (1), -.BR umask (2), -.BR hosts_access (5), -.BR regex (7), -.BR inetd (8). diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index e0041dc..c019591 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1,7 +1,9 @@ +/* tftp-hpa: $Id$ */ + +/* $OpenBSD: tftpd.c,v 1.13 1999/06/23 17:01:36 deraadt Exp $ */ + /* * Copyright (c) 1983 Regents of the University of California. - * Copyright (c) 1999-2009 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 @@ -33,1173 +35,616 @@ * SUCH DAMAGE. */ -#include "config.h" /* Must be included first */ +#include "config.h" /* Must be included first */ #include "tftpd.h" +#ifndef lint +static const char *copyright UNUSED = +"@(#) Copyright (c) 1983 Regents of the University of California.\n\ + All rights reserved.\n"; +/*static char sccsid[] = "from: @(#)tftpd.c 5.13 (Berkeley) 2/26/91";*/ +/*static char rcsid[] = "$OpenBSD: tftpd.c,v 1.13 1999/06/23 17:01:36 deraadt Exp $: tftpd.c,v 1.6 1997/02/16 23:49:21 deraadt Exp $";*/ +static const char *rcsid UNUSED = +"tftp-hpa $Id$"; +#endif /* not lint */ + /* * Trivial file transfer protocol server. * * This version includes many modifications by Jim Guyton */ +#include #include +#include +#include #include -#include -#include -#include -#include +#include -#include "common/tftpsubs.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tftpsubs.h" #include "recvfrom.h" #include "remap.h" #ifdef HAVE_SYS_FILIO_H -#include /* Necessary for FIONBIO on Solaris */ +#include /* Necessary for FIONBIO on Solaris */ #endif #ifdef HAVE_TCPWRAPPERS #include -int deny_severity = LOG_WARNING; -int allow_severity = -1; /* Don't log at all */ +int deny_severity = LOG_WARNING; +int allow_severity = -1; /* Don't log at all */ -static struct request_info wrap_request; +struct request_info wrap_request; #endif -#ifdef HAVE_IPV6 -static int ai_fam = AF_UNSPEC; -#else -static int ai_fam = AF_INET; +#define TIMEOUT 5 /* Default timeout (seconds) */ +#define TRIES 4 /* Number of attempts to send each packet */ +#define TIMEOUT_LIMIT (TRIES*(TRIES+1)/2) + +#ifndef OACK +#define OACK 6 +#endif +#ifndef EOPTNEG +#define EOPTNEG 8 #endif -#define TIMEOUT 1000000 /* Default timeout (us) */ -#define TRIES 6 /* Number of attempts to send each packet */ -#define TIMEOUT_LIMIT ((1 << TRIES)-1) - -const char *tftpd_progname; -static int peer; -static unsigned long timeout = TIMEOUT; /* Current timeout value */ -static unsigned long rexmtval = TIMEOUT; /* Basic timeout value */ -static unsigned long maxtimeout = TIMEOUT_LIMIT * TIMEOUT; -static int timeout_quit = 0; -static sigjmp_buf timeoutbuf; -static uint16_t rollover_val = 0; +char *__progname; +int peer; +int timeout = TIMEOUT; +int rexmtval = TIMEOUT; +int maxtimeout = TIMEOUT_LIMIT*TIMEOUT; #define PKTSIZE MAX_SEGSIZE+4 -static char buf[PKTSIZE]; -static char ackbuf[PKTSIZE]; -static unsigned int max_blksize = MAX_SEGSIZE; +char buf[PKTSIZE]; +char ackbuf[PKTSIZE]; +struct sockaddr_in from; +int fromlen; +off_t tsize; +int tsize_ok; -static char tmpbuf[INET6_ADDRSTRLEN], *tmp_p; +int ndirs; +const char **dirs; -static union sock_addr from; -static off_t tsize; -static int tsize_ok; +int secure = 0; +int cancreate = 0; -static int ndirs; -static const char **dirs; - -static int secure = 0; -int cancreate = 0; -int unixperms = 0; -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); -static void do_opt(const char *, const char *, char **); +void nak(int); +void timer(int); +void justquit(int); +void do_opt(char *, char *, char **); -static int set_blksize(uintmax_t *); -static int set_blksize2(uintmax_t *); -static int set_tsize(uintmax_t *); -static int set_timeout(uintmax_t *); -static int set_utimeout(uintmax_t *); -static int set_rollover(uintmax_t *); +int set_blksize(char *, char **); +int set_blksize2(char *, char **); +int set_tsize(char *, char **); +int set_timeout(char *, char **); struct options { - const char *o_opt; - int (*o_fnc)(uintmax_t *); + const char *o_opt; + int (*o_fnc)(char *, char **); } options[] = { - {"blksize", set_blksize}, - {"blksize2", set_blksize2}, - {"tsize", set_tsize}, - {"timeout", set_timeout}, - {"utimeout", set_utimeout}, - {"rollover", set_rollover}, - {NULL, NULL} + { "blksize", set_blksize }, + { "blksize2", set_blksize2 }, + { "tsize", set_tsize }, + { "timeout", set_timeout }, + { NULL, NULL } }; /* Simple handler for SIGHUP */ static volatile sig_atomic_t caught_sighup = 0; static void handle_sighup(int sig) { - (void)sig; /* Suppress unused warning */ - caught_sighup = 1; + (void)sig; /* Suppress unused warning */ + caught_sighup = 1; } -/* Handle exit requests by SIGTERM and SIGINT */ -static volatile sig_atomic_t exit_signal = 0; -static void handle_exit(int sig) + +static void +usage(void) { - exit_signal = sig; + syslog(LOG_ERR, "Usage: %s [-vcl][-a address][-m mappings][-u user][-t timeout][-r option...] [-s] [directory ...]", + __progname); + exit(EX_USAGE); } -/* Handle timeout signal or timeout event */ -static void timer(int sig) -{ - (void)sig; /* Suppress unused warning */ - timeout <<= 1; - if (timeout >= maxtimeout || timeout_quit) - exit(0); - siglongjmp(timeoutbuf, 1); -} #ifdef WITH_REGEX -static struct rule *read_remap_rules(const char *rulefile) +static struct rule * +read_remap_rules(const char *file) { - FILE *f; - struct rule *rulep; + FILE *f; + struct rule *rulep; - f = fopen(rulefile, "rt"); - if (!f) { - syslog(LOG_ERR, "Cannot open map file: %s: %m", rulefile); - exit(EX_NOINPUT); - } - rulep = parserulefile(f); - fclose(f); + f = fopen(file, "rt"); + if ( !f ) { + syslog(LOG_ERR, "Cannot open map file: %s: %m", file); + exit(EX_NOINPUT); + } + rulep = parserulefile(f); + fclose(f); - return rulep; + return rulep; } #endif -/* - * Rules for locking files; return 0 on success, -1 on failure - */ -static int lock_file(int fd, int lock_write) +int +main(int argc, char **argv) { - (void)lock_write; -#if defined(HAVE_FCNTL) && HAVE_DECL_F_SETLK - struct flock fl; - - fl.l_type = lock_write ? F_WRLCK : F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; /* Whole file */ - return fcntl(fd, F_SETLK, &fl); -#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... */ -#endif -} - -static void set_socket_nonblock(int fd, int flag) -{ - int err; - int flags; -#if defined(HAVE_FCNTL) && defined(HAVE_O_NONBLOCK_DEFINITION) - /* Posixly correct */ - err = ((flags = fcntl(fd, F_GETFL, 0)) < 0) || - (fcntl - (fd, F_SETFL, - flag ? flags | O_NONBLOCK : flags & ~O_NONBLOCK) < 0); -#else - flags = flag ? 1 : 0; - err = (ioctl(fd, FIONBIO, &flags) < 0); -#endif - if (err) { - syslog(LOG_ERR, "Cannot set nonblock flag on socket: %m"); - exit(EX_OSERR); - } -} - -static void pmtu_discovery_off(int fd) -{ -#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) - int pmtu = IP_PMTUDISC_DONT; - - setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); -#endif -} - -/* - * Receive packet with synchronous timeout; timeout is adjusted - * to account for time spent waiting. - */ -static int recv_time(int s, void *rbuf, int len, unsigned int flags, - unsigned long *timeout_us_p) -{ - fd_set fdset; - struct timeval tmv, t0, t1; - int rv, err; - unsigned long timeout_us = *timeout_us_p; - unsigned long timeout_left, dt; - - gettimeofday(&t0, NULL); - timeout_left = timeout_us; - - for (;;) { - FD_ZERO(&fdset); - FD_SET(s, &fdset); - - do { - tmv.tv_sec = timeout_left / 1000000; - tmv.tv_usec = timeout_left % 1000000; - - rv = select(s + 1, &fdset, NULL, NULL, &tmv); - err = errno; - - gettimeofday(&t1, NULL); - - dt = (t1.tv_sec - t0.tv_sec) * 1000000 + - (t1.tv_usec - t0.tv_usec); - *timeout_us_p = timeout_left = - (dt >= timeout_us) ? 1 : (timeout_us - dt); - } while (rv == -1 && err == EINTR); - - if (rv == 0) { - timer(0); /* Should not return */ - return -1; - } - - set_socket_nonblock(s, 1); - rv = recv(s, rbuf, len, flags); - err = errno; - set_socket_nonblock(s, 0); - - if (rv < 0) { - if (E_WOULD_BLOCK(err) || err == EINTR) { - continue; /* Once again, with feeling... */ - } else { - errno = err; - return rv; - } - } else { - return rv; - } - } -} - -static int split_port(char **ap, char **pp) -{ - char *a, *p; - int ret = AF_UNSPEC; - - a = *ap; -#ifdef HAVE_IPV6 - if (is_numeric_ipv6(a)) { - if (*a++ != '[') - return -1; - *ap = a; - p = strrchr(a, ']'); - if (!p) - return -1; - *p++ = 0; - a = p; - ret = AF_INET6; - p = strrchr(a, ':'); - if (p) - *p++ = 0; - } else -#endif - { - struct in_addr in; - - p = strrchr(a, ':'); - if (p) - *p++ = 0; - if (inet_aton(a, &in)) - ret = AF_INET; - } - *pp = p; - return ret; -} - -enum long_only_options { - OPT_VERBOSITY = 256, - OPT_MAP_STEPS -}; - -static struct option long_options[] = { - { "ipv4", 0, NULL, '4' }, - { "ipv6", 0, NULL, '6' }, - { "create", 0, NULL, 'c' }, - { "secure", 0, NULL, 's' }, - { "permissive", 0, NULL, 'p' }, - { "verbose", 0, NULL, 'v' }, - { "verbosity", 1, NULL, OPT_VERBOSITY }, - { "version", 0, NULL, 'V' }, - { "listen", 0, NULL, 'l' }, - { "foreground", 0, NULL, 'L' }, - { "address", 1, NULL, 'a' }, - { "blocksize", 1, NULL, 'B' }, - { "user", 1, NULL, 'u' }, - { "umask", 1, NULL, 'U' }, - { "refuse", 1, NULL, 'r' }, - { "timeout", 1, NULL, 't' }, - { "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 } -}; -static const char short_options[] = "46cspvVlLa:B:u:U:r:t:T:R:m:P:"; - -int main(int argc, char **argv) -{ - struct tftphdr *tp; - struct passwd *pw; - struct options *opt; - union sock_addr myaddr; - struct sockaddr_in bindaddr4; -#ifdef HAVE_IPV6 - struct sockaddr_in6 bindaddr6; - int force_ipv6 = 0; -#endif - int n; - int fd = -1; - int fd4 = -1; - int fd6 = -1; - int fdmax = 0; - int standalone = 0; /* Standalone (listen) mode */ - int nodaemon = 0; /* Do not detach process */ - char *address = NULL; /* Address to listen to */ - pid_t pid; - mode_t my_umask = 0; - 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; + struct tftphdr *tp; + struct passwd *pw; + struct options *opt; + struct sockaddr_in myaddr; + struct sockaddr_in bindaddr; + int n; + int on = 1; + int fd = 0; + int standalone = 0; /* Standalone (listen) mode */ + char *address = NULL; /* Address to listen to */ + pid_t pid; + int c; + int setrv; + int waittime = 900; /* Default time to wait for a connect*/ + const char *user = "nobody"; /* Default user */ + char *p; #ifdef WITH_REGEX - char *rewrite_file = NULL; -#endif - const char *pidfile = NULL; - u_short tp_opcode; - - /* basename() is way too much of a pain from a portability standpoint */ - - p = strrchr(argv[0], '/'); - tftpd_progname = (p && p[1]) ? p + 1 : argv[0]; - - openlog(tftpd_progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); - - srand(time(NULL) ^ getpid()); - - while ((c = getopt_long(argc, argv, short_options, long_options, NULL)) - != -1) - switch (c) { - case '4': - ai_fam = AF_INET; - break; -#ifdef HAVE_IPV6 - case '6': - ai_fam = AF_INET6; - force_ipv6 = 1; - break; -#endif - case 'c': - cancreate = 1; - break; - case 's': - secure = 1; - break; - case 'p': - unixperms = 1; - break; - case 'l': - standalone = 1; - break; - case 'L': - standalone = 1; - nodaemon = 1; - break; - case 'a': - address = optarg; - break; - case 't': - waittime = atoi(optarg); - break; - case 'B': - { - char *vp; - max_blksize = (unsigned int)strtoul(optarg, &vp, 10); - if (max_blksize < 512 || max_blksize > MAX_SEGSIZE || *vp) { - syslog(LOG_ERR, - "Bad maximum blocksize value (range 512-%d): %s", - MAX_SEGSIZE, optarg); - exit(EX_USAGE); - } - } - break; - case 'T': - { - char *vp; - unsigned long tov = strtoul(optarg, &vp, 10); - if (tov < 10000UL || tov > 255000000UL || *vp) { - syslog(LOG_ERR, "Bad timeout value: %s", optarg); - exit(EX_USAGE); - } - rexmtval = timeout = tov; - maxtimeout = rexmtval * TIMEOUT_LIMIT; - } - break; - case 'R': - { - if (sscanf(optarg, "%u:%u", &portrange_from, &portrange_to) - != 2 || portrange_from > portrange_to - || portrange_to >= 65535) { - syslog(LOG_ERR, "Bad port range: %s", optarg); - exit(EX_USAGE); - } - portrange = 1; - } - break; - case 'u': - user = optarg; - break; - case 'U': - my_umask = strtoul(optarg, &ep, 8); - if (*ep) { - syslog(LOG_ERR, "Invalid umask: %s", optarg); - exit(EX_USAGE); - } - spec_umask = 1; - break; - case 'r': - for (opt = options; opt->o_opt; opt++) { - if (!strcasecmp(optarg, opt->o_opt)) { - opt->o_opt = ""; /* Don't support this option */ - break; - } - } - if (!opt->o_opt) { - syslog(LOG_ERR, "Unknown option: %s", optarg); - exit(EX_USAGE); - } - break; -#ifdef WITH_REGEX - case 'm': - if (rewrite_file) { - syslog(LOG_ERR, "Multiple -m options"); - exit(EX_USAGE); - } - 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++; - break; - case OPT_VERBOSITY: - verbosity = atoi(optarg); - break; - case 'V': - /* Print configuration to stdout and exit */ - printf("%s\n", TFTPD_CONFIG_STR); - exit(0); - break; - case 'P': - pidfile = optarg; - break; - default: - syslog(LOG_ERR, "Unknown option: '%c'", optopt); - break; - } - - dirs = xmalloc((argc - optind + 1) * sizeof(char *)); - for (ndirs = 0; optind != argc; optind++) - dirs[ndirs++] = argv[optind]; - - dirs[ndirs] = NULL; - - if (secure) { - if (ndirs == 0) { - syslog(LOG_ERR, "no -s directory"); - exit(EX_USAGE); - } - if (ndirs > 1) { - syslog(LOG_ERR, "too many -s directories"); - exit(EX_USAGE); - } - if (chdir(dirs[0])) { - syslog(LOG_ERR, "%s: %m", dirs[0]); - exit(EX_NOINPUT); - } - } - - pw = getpwnam(user); - if (!pw) { - syslog(LOG_ERR, "no user %s: %m", user); - exit(EX_NOUSER); - } - -#ifdef WITH_REGEX - if (rewrite_file) - rewrite_rules = read_remap_rules(rewrite_file); + char *rewrite_file = NULL; #endif - if (pidfile && !standalone) { - syslog(LOG_WARNING, "not in standalone mode, ignoring pid file"); - pidfile = NULL; - } + /* basename() is way too much of a pain from a portability standpoint */ - /* If we're running standalone, set up the input port */ - if (standalone) { - FILE *pf; -#ifdef HAVE_IPV6 - if (ai_fam != AF_INET6) { -#endif - fd4 = socket(AF_INET, SOCK_DGRAM, 0); - if (fd4 < 0) { - syslog(LOG_ERR, "cannot open IPv4 socket: %m"); - exit(EX_OSERR); - } -#ifndef __CYGWIN__ - set_socket_nonblock(fd4, 1); -#endif - memset(&bindaddr4, 0, sizeof bindaddr4); - bindaddr4.sin_family = AF_INET; - bindaddr4.sin_addr.s_addr = INADDR_ANY; - bindaddr4.sin_port = htons(IPPORT_TFTP); -#ifdef HAVE_IPV6 - } - if (ai_fam != AF_INET) { - fd6 = socket(AF_INET6, SOCK_DGRAM, 0); - if (fd6 < 0) { - if (fd4 < 0) { - syslog(LOG_ERR, "cannot open IPv6 socket: %m"); - exit(EX_OSERR); - } else { - syslog(LOG_ERR, - "cannot open IPv6 socket, disable IPv6: %m"); - } - } -#ifndef __CYGWIN__ - set_socket_nonblock(fd6, 1); -#endif - memset(&bindaddr6, 0, sizeof bindaddr6); - bindaddr6.sin6_family = AF_INET6; - bindaddr6.sin6_port = htons(IPPORT_TFTP); - } -#endif - if (address) { - char *portptr = NULL, *eportptr; - int err; - struct servent *servent; - unsigned long port; - - address = tfstrdup(address); - err = split_port(&address, &portptr); - switch (err) { - case AF_INET: -#ifdef HAVE_IPV6 - if (fd6 >= 0) { - close(fd6); - fd6 = -1; - if (ai_fam == AF_INET6) { - syslog(LOG_ERR, - "Address %s is not in address family AF_INET6", - address); - exit(EX_USAGE); - } - ai_fam = AF_INET; - } - break; - case AF_INET6: - if (fd4 >= 0) { - close(fd4); - fd4 = -1; - if (ai_fam == AF_INET) { - syslog(LOG_ERR, - "Address %s is not in address family AF_INET", - address); - exit(EX_USAGE); - } - ai_fam = AF_INET6; - } - break; -#endif - case AF_UNSPEC: - break; - default: - syslog(LOG_ERR, - "Numeric IPv6 addresses need to be enclosed in []"); - exit(EX_USAGE); - } - if (!portptr) - portptr = (char *)"tftp"; - if (*address) { - if (fd4 >= 0) { - bindaddr4.sin_family = AF_INET; - err = set_sock_addr(address, - (union sock_addr *)&bindaddr4, NULL); - if (err) { - syslog(LOG_ERR, - "cannot resolve local IPv4 bind address: %s, %s", - address, gai_strerror(err)); - exit(EX_NOINPUT); - } - } -#ifdef HAVE_IPV6 - if (fd6 >= 0) { - bindaddr6.sin6_family = AF_INET6; - err = set_sock_addr(address, - (union sock_addr *)&bindaddr6, NULL); - if (err) { - if (fd4 >= 0) { - syslog(LOG_ERR, - "cannot resolve local IPv6 bind address: %s" - "(%s); using IPv4 only", - address, gai_strerror(err)); - close(fd6); - fd6 = -1; - } else { - syslog(LOG_ERR, - "cannot resolve local IPv6 bind address: %s" - "(%s)", address, gai_strerror(err)); - exit(EX_NOINPUT); - } - } - } -#endif - } else { - /* Default to using INADDR_ANY */ - } - - if (portptr && *portptr) { - servent = getservbyname(portptr, "udp"); - if (servent) { - if (fd4 >= 0) - bindaddr4.sin_port = servent->s_port; -#ifdef HAVE_IPV6 - if (fd6 >= 0) - bindaddr6.sin6_port = servent->s_port; -#endif - } else if ((port = strtoul(portptr, &eportptr, 0)) - && !*eportptr) { - if (fd4 >= 0) - bindaddr4.sin_port = htons(port); -#ifdef HAVE_IPV6 - if (fd6 >= 0) - bindaddr6.sin6_port = htons(port); -#endif - } else if (!strcmp(portptr, "tftp")) { - /* It's TFTP, we're OK */ - } else { - syslog(LOG_ERR, "cannot resolve local bind port: %s", - portptr); - exit(EX_NOINPUT); - } - } - } - - if (fd4 >= 0) { - if (bind(fd4, (struct sockaddr *)&bindaddr4, - sizeof(bindaddr4)) < 0) { - syslog(LOG_ERR, "cannot bind to local IPv4 socket: %m"); - exit(EX_OSERR); - } - } -#ifdef HAVE_IPV6 - if (fd6 >= 0) { -#if defined(IPV6_V6ONLY) - int on = 1; - if (fd4 >= 0 || force_ipv6) - if (setsockopt(fd6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&on, - sizeof(on))) - syslog(LOG_ERR, "cannot setsockopt IPV6_V6ONLY %m"); -#endif - if (bind(fd6, (struct sockaddr *)&bindaddr6, - sizeof(bindaddr6)) < 0) { - if (fd4 >= 0) { - syslog(LOG_ERR, - "cannot bind to local IPv6 socket," - "IPv6 disabled: %m"); - close(fd6); - fd6 = -1; - } else { - syslog(LOG_ERR, "cannot bind to local IPv6 socket: %m"); - exit(EX_OSERR); - } - } - } -#endif - /* Daemonize this process */ - /* Note: when running in secure mode (-s), we must not chdir, since - we are already in the proper directory. */ - if (!nodaemon && daemon(secure, 0) < 0) { - syslog(LOG_ERR, "cannot daemonize: %m"); - exit(EX_OSERR); - } - set_signal(SIGTERM, handle_exit, 0); - set_signal(SIGINT, handle_exit, 0); - if (pidfile) { - pf = fopen (pidfile, "w"); - if (!pf) { - syslog(LOG_ERR, "cannot open pid file '%s' for writing: %m", pidfile); - pidfile = NULL; - } else { - if (fprintf(pf, "%d\n", getpid()) < 0) - syslog(LOG_ERR, "error writing pid file '%s': %m", pidfile); - if (fclose(pf)) - syslog(LOG_ERR, "error closing pid file '%s': %m", pidfile); - } - } - if (fd6 > fd4) - fdmax = fd6; - else - fdmax = fd4; - } else { - /* 0 is our socket descriptor */ - close(1); - close(2); - fd = 0; - fdmax = 0; - /* Note: on Cygwin, select() on a nonblocking socket becomes - a nonblocking select. */ -#ifndef __CYGWIN__ - set_socket_nonblock(fd, 1); -#endif - } - - /* Disable path MTU discovery */ - pmtu_discovery_off(fd); - - /* This means we don't want to wait() for children */ -#ifdef SA_NOCLDWAIT - set_signal(SIGCHLD, SIG_IGN, SA_NOCLDSTOP | SA_NOCLDWAIT); -#else - set_signal(SIGCHLD, SIG_IGN, SA_NOCLDSTOP); -#endif - - /* Take SIGHUP and use it to set a variable. This - is polled synchronously to make sure we don't - lose packets as a result. */ - set_signal(SIGHUP, handle_sighup, 0); - - if (spec_umask || !unixperms) - umask(my_umask); - - while (1) { - fd_set readset; - struct timeval tv_waittime; - int rv; - - if (exit_signal) { /* happens in standalone mode only */ - if (pidfile && unlink(pidfile)) { - syslog(LOG_WARNING, "error removing pid file '%s': %m", pidfile); - exit(EX_OSERR); - } else { - exit(0); - } + p = strrchr(argv[0], '/'); + __progname = (p && p[1]) ? p+1 : argv[0]; + + openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON); + + while ((c = getopt(argc, argv, "csvla:u:r:t:m:")) != -1) + switch (c) { + case 'c': + cancreate = 1; + break; + case 's': + secure = 1; + break; + case 'l': + standalone = 1; + break; + case 'a': + address = optarg; + break; + case 't': + waittime = atoi(optarg); + break; + case 'u': + user = optarg; + break; + case 'r': + for ( opt = options ; opt->o_opt ; opt++ ) { + if ( !strcasecmp(optarg, opt->o_opt) ) { + opt->o_opt = ""; /* Don't support this option */ + break; } - - if (caught_sighup) { - caught_sighup = 0; - if (standalone) { + } + if ( !opt->o_opt ) { + syslog(LOG_ERR, "Unknown option: %s", optarg); + exit(EX_USAGE); + } + break; #ifdef WITH_REGEX - if (rewrite_file) { - freerules(rewrite_rules); - rewrite_rules = read_remap_rules(rewrite_file); - } + case 'm': + if ( rewrite_file ) { + syslog(LOG_ERR, "Multiple -m options"); + exit(EX_USAGE); + } + rewrite_file = optarg; + break; #endif - } else { - /* Return to inetd for respawn */ - exit(0); - } - } + case 'v': + verbosity++; + break; + default: + usage(); + break; + } - FD_ZERO(&readset); - if (standalone) { - if (fd4 >= 0) { - FD_SET(fd4, &readset); -#ifdef __CYGWIN__ - /* On Cygwin, select() on a nonblocking socket returns - immediately, with a rv of 0! */ - set_socket_nonblock(fd4, 0); -#endif - } - if (fd6 >= 0) { - FD_SET(fd6, &readset); -#ifdef __CYGWIN__ - /* On Cygwin, select() on a nonblocking socket returns - immediately, with a rv of 0! */ - set_socket_nonblock(fd6, 0); -#endif - } - } else { /* fd always 0 */ - fd = 0; -#ifdef __CYGWIN__ - /* On Cygwin, select() on a nonblocking socket returns - immediately, with a rv of 0! */ - set_socket_nonblock(fd, 0); -#endif - FD_SET(fd, &readset); - } - tv_waittime.tv_sec = waittime; - tv_waittime.tv_usec = 0; + dirs = xmalloc((argc-optind+1)*sizeof(char *)); + for ( ndirs = 0 ; optind != argc ; optind++ ) + dirs[ndirs++] = argv[optind]; + dirs[ndirs] = NULL; + + if (secure) { + if (ndirs == 0) { + syslog(LOG_ERR, "no -s directory"); + exit(EX_USAGE); + } + if (ndirs > 1) { + syslog(LOG_ERR, "too many -s directories"); + exit(EX_USAGE); + } + if (chdir(dirs[0])) { + syslog(LOG_ERR, "%s: %m", dirs[0]); + exit(EX_NOINPUT); + } + } + + pw = getpwnam(user); + if (!pw) { + syslog(LOG_ERR, "no user %s: %m", user); + exit(EX_NOUSER); + } + + if (ioctl(fd, FIONBIO, &on) < 0) { + syslog(LOG_ERR, "ioctl(FIONBIO): %m"); + exit(EX_OSERR); + } - /* Never time out if we're in standalone mode */ - rv = select(fdmax + 1, &readset, NULL, NULL, - standalone ? NULL : &tv_waittime); - if (rv == -1 && errno == EINTR) - continue; /* Signal caught, reloop */ - - if (rv == -1) { - syslog(LOG_ERR, "select loop: %m"); - exit(EX_IOERR); - } else if (rv == 0) { - exit(0); /* Timeout, return to inetd */ - } - - if (standalone) { - if ((fd4 >= 0) && FD_ISSET(fd4, &readset)) - fd = fd4; - else if ((fd6 >= 0) && FD_ISSET(fd6, &readset)) - fd = fd6; - else /* not in set ??? */ - continue; - } -#ifdef __CYGWIN__ - /* On Cygwin, select() on a nonblocking socket returns - immediately, with a rv of 0! */ - set_socket_nonblock(fd, 0); +#ifdef WITH_REGEX + if ( rewrite_file ) + rewrite_rules = read_remap_rules(rewrite_file); #endif - n = myrecvfrom(fd, buf, sizeof(buf), 0, &from, &myaddr); + /* If we're running standalone, set up the input port */ + if ( standalone ) { + fd = socket(PF_INET, SOCK_DGRAM, 0); + + memset(&bindaddr, 0, sizeof bindaddr); + bindaddr.sin_addr.s_addr = INADDR_ANY; + bindaddr.sin_port = htons(IPPORT_TFTP); - if (n < 0) { - if (E_WOULD_BLOCK(errno) || errno == EINTR) { - continue; /* Again, from the top */ - } else { - syslog(LOG_ERR, "recvfrom: %m"); - exit(EX_IOERR); - } - } -#ifdef HAVE_IPV6 - if ((from.sa.sa_family != AF_INET) && (from.sa.sa_family != AF_INET6)) { - syslog(LOG_ERR, "received address was not AF_INET/AF_INET6," - " please check your inetd config"); + if ( address ) { + char *portptr, *eportptr; + struct hostent *hostent; + struct servent *servent; + unsigned long port; + + address = tfstrdup(address); + portptr = strrchr(address, ':'); + if ( portptr ) + *portptr++ = '\0'; + + if ( *address ) { + hostent = gethostbyname(address); + if ( !hostent || hostent->h_addrtype != AF_INET ) { + syslog(LOG_ERR, "cannot resolve local bind address: %s", address); + exit(EX_NOINPUT); + } + memcpy(&bindaddr.sin_addr, hostent->h_addr, hostent->h_length); + } else { + /* Default to using INADDR_ANY */ + } + + if ( portptr && *portptr ) { + servent = getservbyname(portptr, "udp"); + if ( servent ) { + bindaddr.sin_port = servent->s_port; + } else if ( (port = strtoul(portptr, &eportptr, 0)) && !*eportptr ) { + bindaddr.sin_port = htons(port); + } else if ( !strcmp(portptr, "tftp") ) { + /* It's TFTP, we're OK */ + } else { + syslog(LOG_ERR, "cannot resolve local bind port: %s", portptr); + exit(EX_NOINPUT); + } + } + } + + if (bind(fd, (struct sockaddr *)&bindaddr, sizeof bindaddr) < 0) { + syslog(LOG_ERR, "cannot bind to local socket: %m"); + exit(EX_OSERR); + } + + /* Daemonize this process */ + { + pid_t f = fork(); + if ( f > 0 ) + exit(0); + if ( f < 0 ) { + syslog(LOG_ERR, "cannot fork: %m"); + exit(EX_OSERR); + } + close(0); close(1); close(2); +#ifdef HAVE_SETSID + setsid(); +#endif + } + } else { + /* 0 is our socket descriptor */ + close(1); close(2); + } + + /* This means we don't want to wait() for children */ +#ifdef SA_NOCLDWAIT + set_signal(SIGCHLD, SIG_IGN, SA_NOCLDSTOP|SA_NOCLDWAIT); #else - if (from.sa.sa_family != AF_INET) { - syslog(LOG_ERR, "received address was not AF_INET," - " please check your inetd config"); + set_signal(SIGCHLD, SIG_IGN, SA_NOCLDSTOP); #endif - exit(EX_PROTOCOL); - } - if (standalone) { - if ((from.sa.sa_family == AF_INET) && - (myaddr.si.sin_addr.s_addr == INADDR_ANY)) { - /* myrecvfrom() didn't capture the source address; but we might - have bound to a specific address, if so we should use it */ - memcpy(SOCKADDR_P(&myaddr), &bindaddr4.sin_addr, - sizeof(bindaddr4.sin_addr)); -#ifdef HAVE_IPV6 - } else if ((from.sa.sa_family == AF_INET6) && - IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *) - SOCKADDR_P(&myaddr))) { - memcpy(SOCKADDR_P(&myaddr), &bindaddr6.sin6_addr, - sizeof(bindaddr6.sin6_addr)); + /* Take SIGHUP and use it to set a variable. This + is polled synchronously to make sure we don't + lose packets as a result. */ + set_signal(SIGHUP, handle_sighup, 0); + + while ( 1 ) { + fd_set readset; + struct timeval tv_waittime; + int rv; + + if ( caught_sighup ) { + caught_sighup = 0; + if ( standalone ) { +#ifdef HAVE_REGEX + if ( rewrite_file ) { + freerules(rewrite_rules); + rewrite_rules = read_remap_rules(rewrite_file); + } #endif - } - } + } else { + /* Return to inetd for respawn */ + exit(0); + } + } + + FD_ZERO(&readset); + FD_SET(fd, &readset); + tv_waittime.tv_sec = waittime; + tv_waittime.tv_usec = 0; + + /* Never time out if we're in standalone mode */ + rv = select(fd+1, &readset, NULL, NULL, standalone ? NULL : &tv_waittime); + if ( rv == -1 && errno == EINTR ) + continue; /* Signal caught, reloop */ + if ( rv == -1 ) { + syslog(LOG_ERR, "select loop: %m"); + exit(EX_OSERR); + } else if ( rv == 0 ) { + exit(0); /* Timeout, return to inetd */ + } + + fromlen = sizeof (from); + n = myrecvfrom(fd, buf, sizeof (buf), 0, + (struct sockaddr *)&from, &fromlen, + &myaddr); - /* - * Now that we have read the request packet from the UDP - * socket, we fork and go back to listening to the socket. - */ - pid = fork(); - if (pid < 0) { - syslog(LOG_ERR, "fork: %m"); - exit(EX_OSERR); /* Return to inetd, just in case */ - } else if (pid == 0) - break; /* Child exit, parent loop */ + if ( standalone && myaddr.sin_addr.s_addr == INADDR_ANY ) { + /* myrecvfrom() didn't capture the source address; but we might + have bound to a specific address, if so we should use it */ + memcpy(&myaddr.sin_addr, &bindaddr.sin_addr, sizeof bindaddr.sin_addr); } - /* Child process: handle the actual request here */ - - /* Ignore SIGHUP */ - set_signal(SIGHUP, SIG_IGN, 0); - - /* Make sure the log socket is still connected. This has to be - done before the chroot, while /dev/log is still accessible. - When not running standalone, there is little chance that the - syslog daemon gets restarted by the time we get here. */ - if (secure && standalone) { - closelog(); - openlog(tftpd_progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + if (n < 0) { + syslog(LOG_ERR, "recvfrom: %m"); + exit(EX_IOERR); } - + + /* + * Now that we have read the request packet from the UDP + * socket, we fork and go back to listening to the socket. + */ + pid = fork(); + if (pid < 0) { + syslog(LOG_ERR, "fork: %m"); + exit(EX_OSERR); /* Return to inetd, just in case */ + } else if ( pid == 0 ) + break; /* Child exit, parent loop */ + } + + /* Child process: handle the actual request here */ + + /* Ignore SIGHUP */ + set_signal(SIGHUP, SIG_IGN, 0); + #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, tftpd_progname, - RQ_FILE, fd, - RQ_CLIENT_SIN, &from, RQ_SERVER_SIN, &myaddr, 0); - sock_methods(&wrap_request); - - tmp_p = (char *)inet_ntop(myaddr.sa.sa_family, SOCKADDR_P(&myaddr), - tmpbuf, INET6_ADDRSTRLEN); - if (!tmp_p) { - tmp_p = tmpbuf; - strcpy(tmpbuf, "???"); - } - if (hosts_access(&wrap_request) == 0) { - if (deny_severity != -1) - syslog(deny_severity, "connection refused from %s", tmp_p); - exit(EX_NOPERM); /* Access denied */ - } else if (allow_severity != -1) { - syslog(allow_severity, "connect from %s", tmp_p); - } + /* 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_FILE, fd, + RQ_CLIENT_SIN, &from, + RQ_SERVER_SIN, &myaddr, + 0); + sock_methods(&wrap_request); + if ( hosts_access(&wrap_request) == 0 ) { + if ( deny_severity != -1 ) + syslog(deny_severity, "connection refused from %s", + inet_ntoa(from.sin_addr)); + exit(EX_NOPERM); /* Access denied */ + } else if ( allow_severity != -1 ) { + syslog(allow_severity, "connect from %s", + inet_ntoa(from.sin_addr)); + } #endif - /* Close file descriptors we don't need */ - close(fd); + /* Close file descriptors we don't need */ + close(fd); + + /* Get a socket. This has to be done before the chroot(), since + some systems require access to /dev to create a socket. */ + + peer = socket(AF_INET, SOCK_DGRAM, 0); + if (peer < 0) { + syslog(LOG_ERR, "socket: %m"); + exit(EX_IOERR); + } - /* Get a socket. This has to be done before the chroot(), since - some systems require access to /dev to create a socket. */ - - peer = socket(myaddr.sa.sa_family, SOCK_DGRAM, 0); - if (peer < 0) { - syslog(LOG_ERR, "socket: %m"); - exit(EX_IOERR); - } - - /* 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) { - die = 0; - } else if (errno != EPERM) { - syslog(LOG_ERR, "cannot set groups for user %s", user); - die = EX_OSERR; - } -#endif - if (die) - exit(die); - - /* Chroot and drop privileges */ - if (secure) { - if (chroot(".")) { - syslog(LOG_ERR, "chroot: %m"); - exit(EX_OSERR); - } -#ifdef __CYGWIN__ - chdir("/"); /* Cygwin chroot() bug workaround */ -#endif - } - -#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); + /* Chroot and drop privileges */ + + if (secure && chroot(".")) { + syslog(LOG_ERR, "chroot: %m"); + exit(EX_OSERR); + } + +#ifdef HAVE_SETREGID + setrv = setregid(pw->pw_gid, pw->pw_gid); #else - setrv = setegid(pw->pw_gid) || setgid(pw->pw_gid); + setrv = setegid(pw->pw_gid) || setgid(pw->pw_gid); #endif - if (setrv && errno == EPERM) { - setrv = 0; /* Assume already restricted by system policy */ - } - -#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); + +#ifdef 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)); + /* 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"); - exit(EX_OSERR); - } - - /* Process the request... */ - if (pick_port_bind(peer, &myaddr, portrange_from, portrange_to) < 0) { - syslog(LOG_ERR, "bind: %m"); - exit(EX_IOERR); - } - - if (connect(peer, &from.sa, SOCKLEN(&from)) < 0) { - syslog(LOG_ERR, "connect: %m"); - exit(EX_IOERR); - } - - /* Disable path MTU discovery */ - pmtu_discovery_off(peer); - - tp = (struct tftphdr *)buf; - tp_opcode = ntohs(tp->th_opcode); - if (tp_opcode == RRQ || tp_opcode == WRQ) - tftp(tp, n); - exit(0); + + if ( setrv ) { + syslog(LOG_ERR, "cannot drop privileges: %m"); + exit(EX_OSERR); + } + + /* Other basic setup */ + from.sin_family = AF_INET; + alarm(0); + + /* Process the request... */ + + myaddr.sin_port = htons(0); /* We want a new local port */ + if (bind(peer, (struct sockaddr *)&myaddr, sizeof myaddr) < 0) { + syslog(LOG_ERR, "bind: %m"); + exit(EX_IOERR); + } + if (connect(peer, (struct sockaddr *)&from, sizeof from) < 0) { + syslog(LOG_ERR, "connect: %m"); + exit(EX_IOERR); + } + tp = (struct tftphdr *)buf; + tp->th_opcode = ntohs(tp->th_opcode); + if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) + tftp(tp, n); + exit(0); } -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); +char *rewrite_access(char *, int); +int validate_access(char *, int, struct formats *); +void tftp_sendfile(struct formats *, struct tftphdr *, int); +void tftp_recvfile(struct formats *, struct tftphdr *, int); -static const struct formats formats[] = { - { - "netascii", rewrite_access, validate_access, tftp_sendfile, - tftp_recvfile, 1}, { - "octet", rewrite_access, validate_access, tftp_sendfile, - tftp_recvfile, 0}, { - NULL, NULL, NULL, NULL, NULL, 0} +struct formats { + const char *f_mode; + char *(*f_rewrite)(char *, int); + int (*f_validate)(char *, int, struct formats *); + void (*f_send)(struct formats *, struct tftphdr *, int); + void (*f_recv)(struct formats *, struct tftphdr *, int); + int f_convert; +} formats[] = { + { "netascii", rewrite_access, validate_access, tftp_sendfile, tftp_recvfile, 1 }, + { "octet", rewrite_access, validate_access, tftp_sendfile, tftp_recvfile, 0 }, + { NULL, NULL, NULL, NULL, NULL, 0 } }; /* * Handle initial connection protocol. */ -int tftp(struct tftphdr *tp, int size) +int +tftp(struct tftphdr *tp, int size) { - char *cp, *end; - int argn, ecode; - const struct formats *pf = NULL; - char *origfilename; - char *filename, *mode = NULL; - const char *errmsgptr; - u_short tp_opcode = ntohs(tp->th_opcode); - - char *val = NULL, *opt = NULL; - char *ap = ackbuf + 2; - - ((struct tftphdr *)ackbuf)->th_opcode = htons(OACK); - - origfilename = cp = (char *)&(tp->th_stuff); - argn = 0; - - end = (char *)tp + size; - - while (cp < end && *cp) { - do { - cp++; - } while (cp < end && *cp); - - if (*cp) { - nak(EBADOP, "Request not null-terminated"); - exit(0); - } - - argn++; - if (argn == 1) { - mode = ++cp; - } else if (argn == 2) { - for (cp = mode; *cp; cp++) - *cp = tolower(*cp); - for (pf = formats; pf->f_mode; pf++) { - if (!strcmp(pf->f_mode, mode)) - break; - } - if (!pf->f_mode) { - nak(EBADOP, "Unknown mode"); - exit(0); - } - 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); - } - if (verbosity >= 1) { - tmp_p = (char *)inet_ntop(from.sa.sa_family, SOCKADDR_P(&from), - tmpbuf, INET6_ADDRSTRLEN); - if (!tmp_p) { - tmp_p = tmpbuf; - strcpy(tmpbuf, "???"); - } - if (filename == origfilename - || !strcmp(filename, origfilename)) - syslog(LOG_NOTICE, "%s from %s filename %s\n", - tp_opcode == WRQ ? "WRQ" : "RRQ", - tmp_p, filename); - else - syslog(LOG_NOTICE, - "%s from %s filename %s remapped to %s\n", - tp_opcode == WRQ ? "WRQ" : "RRQ", - tmp_p, origfilename, - filename); - } - /* - * 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; - } else { - do_opt(opt, val, &ap); - opt = ++cp; - } + char *cp; + int argn, ecode; + struct formats *pf = NULL; + char *origfilename; + char *filename, *mode = NULL; + + char *val = NULL, *opt = NULL; + char *ap = ackbuf + 2; + + ((struct tftphdr *)ackbuf)->th_opcode = ntohs(OACK); + + origfilename = cp = (char *) &(tp->th_stuff); + argn = 0; + + while ( cp < buf + size && *cp ) { + do { + cp++; + } while (cp < buf + size && *cp); + + if ( *cp ) { + nak(EBADOP); /* Corrupt packet - no final NULL */ + exit(0); } - - if (!pf) { - nak(EBADOP, "Missing mode"); - exit(0); - } - - if (ap != (ackbuf + 2)) { - if (tp_opcode == WRQ) - (*pf->f_recv) (pf, (struct tftphdr *)ackbuf, ap - ackbuf); - else - (*pf->f_send) (pf, (struct tftphdr *)ackbuf, ap - ackbuf); + + argn++; + if (argn == 1) { + mode = ++cp; + } else if (argn == 2) { + for (cp = mode; *cp; cp++) + *cp = tolower(*cp); + for (pf = formats; pf->f_mode; pf++) { + if (!strcmp(pf->f_mode, mode)) + break; + } + if (!pf->f_mode) { + nak(EBADOP); + exit(0); + } + if ( !(filename = (*pf->f_rewrite)(origfilename, tp->th_opcode)) ) { + nak(EACCESS); /* File denied by mapping rule */ + exit(0); + } + if ( verbosity >= 1 ) { + if ( filename == origfilename || !strcmp(filename, origfilename) ) + syslog(LOG_NOTICE, "%s from %s filename %s\n", + tp->th_opcode == WRQ ? "WRQ" : "RRQ", + inet_ntoa(from.sin_addr), filename); + else + syslog(LOG_NOTICE, "%s from %s filename %s remapped to %s\n", + tp->th_opcode == WRQ ? "WRQ" : "RRQ", + inet_ntoa(from.sin_addr), origfilename, filename); + } + ecode = (*pf->f_validate)(filename, tp->th_opcode, pf); + if (ecode) { + nak(ecode); + exit(0); + } + opt = ++cp; + } else if ( argn & 1 ) { + val = ++cp; } else { - if (tp_opcode == WRQ) - (*pf->f_recv) (pf, NULL, 0); - else - (*pf->f_send) (pf, NULL, 0); + do_opt(opt, val, &ap); + opt = ++cp; } - exit(0); /* Request completed */ + } + + if (!pf) { + nak(EBADOP); + exit(0); + } + + if ( ap != (ackbuf+2) ) { + if ( tp->th_opcode == WRQ ) + (*pf->f_recv)(pf, (struct tftphdr *)ackbuf, ap-ackbuf); + else + (*pf->f_send)(pf, (struct tftphdr *)ackbuf, ap-ackbuf); + } else { + if (tp->th_opcode == WRQ) + (*pf->f_recv)(pf, NULL, 0); + else + (*pf->f_send)(pf, NULL, 0); + } + exit(0); /* Request completed */ } static int blksize_set; @@ -1207,65 +652,66 @@ static int blksize_set; /* * Set a non-standard block size (c.f. RFC2348) */ -static int set_blksize(uintmax_t *vp) +int +set_blksize(char *val, char **ret) { - uintmax_t sz = *vp; - - if (blksize_set) - return 0; - - if (sz < 8) - return 0; - else if (sz > max_blksize) - sz = max_blksize; - - *vp = segsize = sz; - blksize_set = 1; - return 1; + static char b_ret[6]; + unsigned int sz; + char *vend; + + sz = (unsigned int)strtoul(val, &vend, 10); + + if ( blksize_set || *vend ) + return 0; + + if (sz < 8) + return(0); + else if (sz > MAX_SEGSIZE) + sz = MAX_SEGSIZE; + + segsize = sz; + sprintf(*ret = b_ret, "%u", sz); + + blksize_set = 1; + + return(1); } /* * Set a power-of-two block size (nonstandard) */ -static int set_blksize2(uintmax_t *vp) +int +set_blksize2(char *val, char **ret) { - uintmax_t sz = *vp; - - if (blksize_set) - return 0; - - if (sz < 8) - return (0); - else if (sz > max_blksize) - sz = max_blksize; - else - - /* Convert to a power of two */ - if (sz & (sz - 1)) { - unsigned int sz1 = 1; - /* Not a power of two - need to convert */ - while (sz >>= 1) - sz1 <<= 1; - sz = sz1; - } - - *vp = segsize = sz; - blksize_set = 1; - return 1; -} - -/* - * Set the block number rollover value - */ -static int set_rollover(uintmax_t *vp) -{ - uintmax_t ro = *vp; - - if (ro > 65535) - return 0; - - rollover_val = (uint16_t)ro; - return 1; + static char b_ret[6]; + unsigned int sz; + char *vend; + + sz = (unsigned int)strtoul(val, &vend, 10); + + if ( blksize_set || *vend ) + return 0; + + if (sz < 8) + return(0); + else if (sz > MAX_SEGSIZE) + sz = MAX_SEGSIZE; + + /* Convert to a power of two */ + if ( sz & (sz-1) ) { + unsigned int sz1 = 1; + /* Not a power of two - need to convert */ + while ( sz >>= 1 ) + sz1 <<= 1; + sz = sz1; + } + + segsize = sz; + sprintf(*ret = b_ret, "%u", sz); + + blksize_set = 1; + + return(1); } /* @@ -1273,560 +719,448 @@ static int set_rollover(uintmax_t *vp) * For netascii mode, we don't know the size ahead of time; * so reject the option. */ -static int set_tsize(uintmax_t *vp) +int +set_tsize(char *val, char **ret) { - uintmax_t sz = *vp; + static char b_ret[sizeof(uintmax_t)*CHAR_BIT/3+2]; + uintmax_t sz; + char *vend; - if (!tsize_ok) - return 0; + sz = strtoumax(val, &vend, 10); + + if ( !tsize_ok || *vend ) + return 0; + + if (sz == 0) + sz = (uintmax_t)tsize; - if (sz == 0) - sz = tsize; - - *vp = sz; - return 1; + sprintf(*ret = b_ret, "%"PRIuMAX, sz); + return(1); } /* - * Set the timeout (c.f. RFC2349). This is supposed - * to be the (default) retransmission timeout, but being an - * integer in seconds it seems a bit limited. + * Set the timeout (c.f. RFC2349) */ -static int set_timeout(uintmax_t *vp) +int +set_timeout(char *val, char **ret) { - uintmax_t to = *vp; + static char b_ret[4]; + unsigned long to; + char *vend; - if (to < 1 || to > 255) - return 0; + to = strtoul(val, &vend, 10); - rexmtval = timeout = to * 1000000UL; - maxtimeout = rexmtval * TIMEOUT_LIMIT; - - return 1; -} - -/* Similar, but in microseconds. We allow down to 10 ms. */ -static int set_utimeout(uintmax_t *vp) -{ - uintmax_t to = *vp; - - if (to < 10000UL || to > 255000000UL) - return 0; - - rexmtval = timeout = to; - maxtimeout = rexmtval * TIMEOUT_LIMIT; - - return 1; + if ( to < 1 || to > 255 || *vend ) + return 0; + + timeout = to; + rexmtval = to; + maxtimeout = TIMEOUT_LIMIT*to; + + sprintf(*ret = b_ret, "%lu", to); + return(1); } /* - * Conservative calculation for the size of a buffer which can hold an - * arbitrary integer + * Parse RFC2347 style options */ -#define OPTBUFSIZE (sizeof(uintmax_t) * CHAR_BIT / 3 + 3) - -/* - * Parse RFC2347 style options; we limit the arguments to positive - * integers which matches all our current options. - */ -static void do_opt(const char *opt, const char *val, char **ap) +void +do_opt(char *opt, char *val, char **ap) { - struct options *po; - char retbuf[OPTBUFSIZE]; - char *p = *ap; - size_t optlen, retlen; - char *vend; - uintmax_t v; - - /* Global option-parsing variables initialization */ - blksize_set = 0; - - if (!*opt || !*val) - return; - - errno = 0; - v = strtoumax(val, &vend, 10); - if (*vend || errno == ERANGE) - return; - - for (po = options; po->o_opt; po++) - if (!strcasecmp(po->o_opt, opt)) { - if (po->o_fnc(&v)) { - optlen = strlen(opt); - retlen = sprintf(retbuf, "%"PRIuMAX, v); - - if (p + optlen + retlen + 2 >= ackbuf + sizeof(ackbuf)) { - nak(EOPTNEG, "Insufficient space for options"); - exit(0); - } - - memcpy(p, opt, optlen+1); - p += optlen+1; - memcpy(p, retbuf, retlen+1); - p += retlen+1; - } else { - nak(EOPTNEG, "Unsupported option(s) requested"); - exit(0); - } - break; - } - - *ap = p; -} - -#ifdef WITH_REGEX - -/* - * This is called by the remap engine when it encounters macros such - * as \i. It should write the output in "output" if non-NULL, and - * return the length of the output (generated or not). - * - * Return -1 on failure. - */ -static int rewrite_macros(char macro, char *output) -{ - char *p, tb[INET6_ADDRSTRLEN]; - int l=0; - - switch (macro) { - case 'i': - p = (char *)inet_ntop(from.sa.sa_family, SOCKADDR_P(&from), - tb, INET6_ADDRSTRLEN); - if (output && p) - strcpy(output, p); - if (!p) - return 0; - else - return strlen(p); - - case 'x': - if (output) { - if (from.sa.sa_family == AF_INET) { - sprintf(output, "%08lX", - (unsigned long)ntohl(from.si.sin_addr.s_addr)); - l = 8; -#ifdef HAVE_IPV6 - } else { - unsigned char *c = (unsigned char *)SOCKADDR_P(&from); - p = tb; - for (l = 0; l < 16; l++) { - sprintf(p, "%02X", *c); - c++; - p += 2; - } - strcpy(output, tb); - l = strlen(tb); -#endif - } - } - return l; - - default: - return -1; + struct options *po; + char *ret; + + /* Global option-parsing variables initialization */ + blksize_set = 0; + + if ( !*opt ) + return; + + for (po = options; po->o_opt; po++) + if (!strcasecmp(po->o_opt, opt)) { + if (po->o_fnc(val, &ret)) { + if (*ap + strlen(opt) + strlen(ret) + 2 >= + ackbuf + sizeof(ackbuf)) { + nak(ENOSPACE); /* EOPTNEG? */ + exit(0); + } + *ap = strrchr(strcpy(strrchr(strcpy(*ap, opt),'\0') + 1, + ret),'\0') + 1; + } else { + nak(EOPTNEG); + exit(0); + } + break; } + return; } /* * Modify the filename, if applicable. If it returns NULL, deny the access. */ -static char *rewrite_access(const struct formats *pf, char *filename, - int mode, int af, const char **msg) +char * +rewrite_access(char *filename, int mode) { - if (rewrite_rules) { - char *newname = - rewrite_string(pf, filename, rewrite_rules, mode, af, - rewrite_macros, msg); - filename = newname; - } - return filename; -} - +#ifdef WITH_REGEX + if ( rewrite_rules ) { + char *newname = rewrite_string(filename, rewrite_rules, mode != RRQ); + filename = newname; + } #else -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; -} + (void)mode; /* Suppress unused warning */ #endif + return filename; +} +FILE *file; /* * Validate file access. Since we * have no uid or gid, for now require * file to exist and be publicly - * readable/writable, unless -p specified. + * readable/writable. * If we were invoked with arguments * from inetd then the file must also be * in one of the given directory prefixes. * Note also, full path name must be * given as we have no login directory. */ -static int validate_access(char *filename, int mode, - const struct formats *pf, const char **errmsg) +int +validate_access(char *filename, int mode, struct formats *pf) { - struct stat stbuf; - int i, len; - int fd, wmode, rmode; - char *cp; - const char **dirp; - char stdio_mode[3]; - - tsize_ok = 0; - *errmsg = NULL; - - if (!secure) { - if (*filename != '/') { - *errmsg = "Only absolute filenames allowed"; - return (EACCESS); - } - - /* - * prevent tricksters from getting around the directory - * restrictions - */ - len = strlen(filename); - for (i = 1; i < len - 3; i++) { - cp = filename + i; - if (*cp == '.' && memcmp(cp - 1, "/../", 4) == 0) { - *errmsg = "Reverse path not allowed"; - return (EACCESS); - } - } - - for (dirp = dirs; *dirp; dirp++) - if (strncmp(filename, *dirp, strlen(*dirp)) == 0) - break; - if (*dirp == 0 && dirp != dirs) { - *errmsg = "Forbidden directory"; - return (EACCESS); - } - } - + struct stat stbuf; + int i, len; + int fd, wmode; + char *cp; + const char **dirp; + + tsize_ok = 0; + + if (!secure) { + if (*filename != '/') + return (EACCESS); /* - * We use different a different permissions scheme if `cancreate' is - * set. + * prevent tricksters from getting around the directory + * restrictions */ - wmode = O_WRONLY | (cancreate ? O_CREAT : 0) | (pf->f_convert ? O_TEXT : O_BINARY); - rmode = O_RDONLY | (pf->f_convert ? O_TEXT : O_BINARY); - -#ifndef HAVE_FTRUNCATE - wmode |= O_TRUNC; /* This really sucks on a dupe */ -#endif - - fd = open(filename, mode == RRQ ? rmode : wmode, 0666); - if (fd < 0) { - switch (errno) { - case ENOENT: - case ENOTDIR: - return ENOTFOUND; - case ENOSPC: - return ENOSPACE; - case EEXIST: - return EEXISTS; - default: - return errno + 100; - } + len = strlen(filename); + for ( i = 1 ; i < len-3 ; i++ ) { + cp = filename + i; + if ( *cp == '.' && memcmp(cp-1, "/../", 4) == 0) + return(EACCESS); } - if (fstat(fd, &stbuf) < 0) - exit(EX_OSERR); /* This shouldn't happen */ - - /* A duplicate RRQ or (worse!) WRQ packet could really cause havoc... */ - if (lock_file(fd, mode != RRQ)) - exit(0); - + for (dirp = dirs; *dirp; dirp++) + if (strncmp(filename, *dirp, strlen(*dirp)) == 0) + break; + if (*dirp==0 && dirp!=dirs) + return (EACCESS); + } + + /* + * We use different a different permissions scheme if `cancreate' is + * set. + */ + wmode = O_TRUNC; + if (stat(filename, &stbuf) < 0) { + if (!cancreate) + return (errno == ENOENT ? ENOTFOUND : EACCESS); + else { + if ((errno == ENOENT) && (mode != RRQ)) + wmode |= O_CREAT; + else + return(EACCESS); + } + } else { if (mode == RRQ) { - if (!unixperms && (stbuf.st_mode & (S_IREAD >> 6)) == 0) { - *errmsg = "File must have global read permissions"; - return (EACCESS); - } - tsize = stbuf.st_size; - /* We don't know the tsize if conversion is needed */ - tsize_ok = !pf->f_convert; + if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) + return (EACCESS); + tsize = stbuf.st_size; + /* We don't know the tsize if conversion is needed */ + tsize_ok = !pf->f_convert; } else { - if (!unixperms) { - if ((stbuf.st_mode & (S_IWRITE >> 6)) == 0) { - *errmsg = "File must have global write permissions"; - return (EACCESS); - } - } - -#ifdef HAVE_FTRUNCATE - /* We didn't get to truncate the file at open() time */ - if (ftruncate(fd, (off_t) 0)) { - *errmsg = "Cannot reset file size"; - return (EACCESS); - } -#endif - tsize = 0; - tsize_ok = 1; + if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) + return (EACCESS); + tsize = 0; + tsize_ok = 1; } + } + fd = open(filename, mode == RRQ ? O_RDONLY : (O_WRONLY|wmode), 0666); + if (fd < 0) + return (errno + 100); + /* + * If the file was created, set default permissions. + */ + if ((wmode & O_CREAT) && fchmod(fd, 0666) < 0) { + int serrno = errno; + + close(fd); + unlink(filename); + + return (serrno + 100); + } + file = fdopen(fd, (mode == RRQ)? "r":"w"); + if (file == NULL) + return (errno + 100); + return (0); +} - stdio_mode[0] = (mode == RRQ) ? 'r' : 'w'; - stdio_mode[1] = (pf->f_convert) ? 't' : 'b'; - stdio_mode[2] = '\0'; +int timeout; +sigjmp_buf timeoutbuf; - file = fdopen(fd, stdio_mode); - if (file == NULL) - exit(EX_OSERR); /* Internal error */ - - return (0); +/* Handle timeout signal */ +void +timer(int sig) +{ + (void)sig; /* Suppress unused warning */ + timeout += rexmtval; + if (timeout >= maxtimeout) + exit(0); + siglongjmp(timeoutbuf, 1); } /* * Send the requested file. */ -static void tftp_sendfile(const struct formats *pf, struct tftphdr *oap, int oacklen) +void +tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) { - struct tftphdr *dp; - struct tftphdr *ap; /* ack packet */ - static u_short block = 1; /* Static to avoid longjmp funnies */ - u_short ap_opcode, ap_block; - unsigned long r_timeout; - int size, n; - - if (oap) { - timeout = rexmtval; - (void)sigsetjmp(timeoutbuf, 1); - oack: - r_timeout = timeout; - if (send(peer, oap, oacklen, 0) != oacklen) { - syslog(LOG_WARNING, "tftpd: oack: %m\n"); - goto abort; - } - for (;;) { - n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &r_timeout); - if (n < 0) { - syslog(LOG_WARNING, "tftpd: read: %m\n"); - goto abort; - } - ap = (struct tftphdr *)ackbuf; - ap_opcode = ntohs((u_short) ap->th_opcode); - ap_block = ntohs((u_short) ap->th_block); - - if (ap_opcode == ERROR) { - syslog(LOG_WARNING, - "tftp: client does not accept options\n"); - goto abort; - } - if (ap_opcode == ACK) { - if (ap_block == 0) - break; - /* Resynchronize with the other side */ - (void)synchnet(peer); - goto oack; - } - } + struct tftphdr *dp; + struct tftphdr *ap; /* ack packet */ + static u_short block = 1; /* Static to avoid longjmp funnies */ + int size, n; + + ap = (struct tftphdr *)ackbuf; + + if (oap) { + timeout = 0; + (void)sigsetjmp(timeoutbuf,1); + oack: + if (send(peer, oap, oacklen, 0) != oacklen) { + syslog(LOG_ERR, "tftpd: oack: %m\n"); + goto abort; } + for ( ; ; ) { + set_signal(SIGALRM, timer, SA_RESTART); + alarm(rexmtval); + n = recv(peer, ackbuf, sizeof(ackbuf), 0); + alarm(0); + if (n < 0) { + syslog(LOG_ERR, "tftpd: read: %m\n"); + goto abort; + } + ap->th_opcode = ntohs((u_short)ap->th_opcode); + ap->th_block = ntohs((u_short)ap->th_block); + + if (ap->th_opcode == ERROR) { + syslog(LOG_ERR, "tftp: client does not accept " + "options\n"); + goto abort; + } + if (ap->th_opcode == ACK) { + if (ap->th_block == 0) + break; + /* Resynchronize with the other side */ + (void)synchnet(peer); + goto oack; + } + } + } - dp = r_init(); - do { - size = readit(file, &dp, pf->f_convert); - if (size < 0) { - nak(errno + 100, NULL); - goto abort; - } - dp->th_opcode = htons((u_short) DATA); - dp->th_block = htons((u_short) block); - timeout = rexmtval; - (void)sigsetjmp(timeoutbuf, 1); - - r_timeout = timeout; - if (send(peer, dp, size + 4, 0) != size + 4) { - syslog(LOG_WARNING, "tftpd: write: %m"); - goto abort; - } - read_ahead(file, pf->f_convert); - for (;;) { - n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &r_timeout); - if (n < 0) { - syslog(LOG_WARNING, "tftpd: read(ack): %m"); - goto abort; - } - ap = (struct tftphdr *)ackbuf; - ap_opcode = ntohs((u_short) ap->th_opcode); - ap_block = ntohs((u_short) ap->th_block); - - if (ap_opcode == ERROR) - goto abort; - - if (ap_opcode == ACK) { - if (ap_block == block) { - break; - } - /* Re-synchronize with the other side */ - (void)synchnet(peer); - /* - * RFC1129/RFC1350: We MUST NOT re-send the DATA - * packet in response to an invalid ACK. Doing so - * would cause the Sorcerer's Apprentice bug. - */ - } - - } - if (!++block) - block = rollover_val; - } while (size == segsize); - abort: - (void)fclose(file); + dp = r_init(); + do { + size = readit(file, &dp, pf->f_convert); + if (size < 0) { + nak(errno + 100); + goto abort; + } + dp->th_opcode = htons((u_short)DATA); + dp->th_block = htons((u_short)block); + timeout = 0; + (void) sigsetjmp(timeoutbuf,1); + + send_data: + if (send(peer, dp, size + 4, 0) != size + 4) { + syslog(LOG_ERR, "tftpd: write: %m"); + goto abort; + } + read_ahead(file, pf->f_convert); + for ( ; ; ) { + set_signal(SIGALRM, timer, SA_RESTART); + alarm(rexmtval); /* read the ack */ + n = recv(peer, ackbuf, sizeof (ackbuf), 0); + alarm(0); + if (n < 0) { + syslog(LOG_ERR, "tftpd: read(ack): %m"); + goto abort; + } + ap->th_opcode = ntohs((u_short)ap->th_opcode); + ap->th_block = ntohs((u_short)ap->th_block); + + if (ap->th_opcode == ERROR) + goto abort; + + if (ap->th_opcode == ACK) { + if (ap->th_block == block) { + break; + } + /* Re-synchronize with the other side */ + (void) synchnet(peer); + if (ap->th_block == (block -1)) { + goto send_data; + } + } + + } + block++; + } while (size == segsize); + abort: + (void) fclose(file); } +/* Bail out signal handler */ +void +justquit(int sig) +{ + (void)sig; /* Suppress unused warning */ + exit(0); +} + + /* * Receive a file. */ -static void tftp_recvfile(const struct formats *pf, - struct tftphdr *oack, int oacklen) +void +tftp_recvfile(struct formats *pf, struct tftphdr *oap, 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; + struct tftphdr *dp; + int n, size; + /* These are "static" to avoid longjmp funnies */ + static struct tftphdr *ap; /* ack buffer */ + static u_short block = 0; + static int acksize; - oap = oack; - - dp = w_init(); - do { - timeout = rexmtval; - - if (!block && oap) { - ap = (struct tftphdr *)ackbuf; - acksize = oacklen; - } else { - ap = (struct tftphdr *)ackbuf; - ap->th_opcode = htons((u_short) ACK); - ap->th_block = htons((u_short) block); - acksize = 4; - /* If we're sending a regular ACK, that means we have successfully - * sent the OACK. Clear oap so that we won't try to send another - * OACK when the block number wraps back to 0. */ - oap = NULL; - } - if (!++block) - block = rollover_val; - (void)sigsetjmp(timeoutbuf, 1); - send_ack: - r_timeout = timeout; - if (send(peer, ackbuf, acksize, 0) != acksize) { - syslog(LOG_WARNING, "tftpd: write(ack): %m"); - goto abort; - } - write_behind(file, pf->f_convert); - for (;;) { - n = recv_time(peer, dp, PKTSIZE, 0, &r_timeout); - if (n < 0) { /* really? */ - syslog(LOG_WARNING, "tftpd: read: %m"); - goto abort; - } - dp_opcode = ntohs((u_short) dp->th_opcode); - dp_block = ntohs((u_short) dp->th_block); - if (dp_opcode == ERROR) - goto abort; - if (dp_opcode == DATA) { - if (dp_block == block) { - break; /* normal */ - } - /* Re-synchronize with the other side */ - (void)synchnet(peer); - if (dp_block == (block - 1)) - goto send_ack; /* rexmit */ - } - } - /* size = write(file, dp->th_data, n - 4); */ - size = writeit(file, &dp, n - 4, pf->f_convert); - if (size != (n - 4)) { /* ahem */ - if (size < 0) - nak(errno + 100, NULL); - else - nak(ENOSPACE, NULL); - goto abort; - } - } while (size == segsize); - write_behind(file, pf->f_convert); - (void)fclose(file); /* close data file */ - - ap->th_opcode = htons((u_short) ACK); /* send the "final" ack */ - ap->th_block = htons((u_short) (block)); - (void)send(peer, ackbuf, 4, 0); - - timeout_quit = 1; /* just quit on timeout */ - n = recv_time(peer, buf, sizeof(buf), 0, &timeout); /* normally times out and quits */ - timeout_quit = 0; - - if (n >= 4 && /* if read some data */ - dp_opcode == DATA && /* and got a data block */ - block == dp_block) { /* then my last ack was lost */ - (void)send(peer, ackbuf, 4, 0); /* resend final ack */ + dp = w_init(); + do { + timeout = 0; + + if (!block && oap) { + ap = (struct tftphdr *)ackbuf; + acksize = oacklen; + } else { + ap = (struct tftphdr *)ackbuf; + ap->th_opcode = htons((u_short)ACK); + ap->th_block = htons((u_short)block); + acksize = 4; } - abort: - return; + block++; + (void) sigsetjmp(timeoutbuf,1); + set_signal(SIGALRM, timer, SA_RESTART); + send_ack: + if (send(peer, ackbuf, acksize, 0) != acksize) { + syslog(LOG_ERR, "tftpd: write(ack): %m"); + goto abort; + } + write_behind(file, pf->f_convert); + for ( ; ; ) { + set_signal(SIGALRM, timer, SA_RESTART); + alarm(rexmtval); + n = recv(peer, dp, PKTSIZE, 0); + alarm(0); + if (n < 0) { /* really? */ + syslog(LOG_ERR, "tftpd: read: %m"); + goto abort; + } + dp->th_opcode = ntohs((u_short)dp->th_opcode); + dp->th_block = ntohs((u_short)dp->th_block); + if (dp->th_opcode == ERROR) + goto abort; + if (dp->th_opcode == DATA) { + if (dp->th_block == block) { + break; /* normal */ + } + /* Re-synchronize with the other side */ + (void) synchnet(peer); + if (dp->th_block == (block-1)) + goto send_ack; /* rexmit */ + } + } + /* size = write(file, dp->th_data, n - 4); */ + size = writeit(file, &dp, n - 4, pf->f_convert); + if (size != (n-4)) { /* ahem */ + if (size < 0) nak(errno + 100); + else nak(ENOSPACE); + goto abort; + } + } while (size == segsize); + write_behind(file, pf->f_convert); + (void) fclose(file); /* close data file */ + + ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ + ap->th_block = htons((u_short)(block)); + (void) send(peer, ackbuf, 4, 0); + + set_signal(SIGALRM, justquit, SA_RESETHAND); /* just quit on timeout */ + alarm(rexmtval); + n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ + alarm(0); + if (n >= 4 && /* if read some data */ + dp->th_opcode == DATA && /* and got a data block */ + block == dp->th_block) { /* then my last ack was lost */ + (void) send(peer, ackbuf, 4, 0); /* resend final ack */ + } + abort: + return; } -static const char *const errmsgs[] = { - "Undefined error code", /* 0 - EUNDEF */ - "File not found", /* 1 - ENOTFOUND */ - "Access denied", /* 2 - EACCESS */ - "Disk full or allocation exceeded", /* 3 - ENOSPACE */ - "Illegal TFTP operation", /* 4 - EBADOP */ - "Unknown transfer ID", /* 5 - EBADID */ - "File already exists", /* 6 - EEXISTS */ - "No such user", /* 7 - ENOUSER */ - "Failure to negotiate RFC2347 options" /* 8 - EOPTNEG */ +struct errmsg { + int e_code; + const char *e_msg; +} errmsgs[] = { + { EUNDEF, "Undefined error code" }, + { ENOTFOUND, "File not found" }, + { EACCESS, "Access violation" }, + { ENOSPACE, "Disk full or allocation exceeded" }, + { EBADOP, "Illegal TFTP operation" }, + { EBADID, "Unknown transfer ID" }, + { EEXISTS, "File already exists" }, + { ENOUSER, "No such user" }, + { EOPTNEG, "Failure to negotiate RFC2347 options" }, + { -1, 0 } }; -#define ERR_CNT (sizeof(errmsgs)/sizeof(const char *)) - /* * Send a nak packet (error message). * Error code passed in is one of the * standard TFTP codes, or a UNIX errno * offset by 100. */ -static void nak(int error, const char *msg) +void +nak(int error) { - struct tftphdr *tp; - int length; - - tp = (struct tftphdr *)buf; - tp->th_opcode = htons((u_short) ERROR); - - if (error >= 100) { - /* This is a Unix errno+100 */ - if (!msg) - msg = strerror(error - 100); - error = EUNDEF; - } else { - if ((unsigned)error >= ERR_CNT) - error = EUNDEF; - - if (!msg) - msg = errmsgs[error]; - } - - tp->th_code = htons((u_short) error); - - length = strlen(msg) + 1; - memcpy(tp->th_msg, msg, length); - length += 4; /* Add space for header */ - - if (verbosity >= 2) { - tmp_p = (char *)inet_ntop(from.sa.sa_family, SOCKADDR_P(&from), - tmpbuf, INET6_ADDRSTRLEN); - if (!tmp_p) { - tmp_p = tmpbuf; - strcpy(tmpbuf, "???"); - } - syslog(LOG_INFO, "sending NAK (%d, %s) to %s", - error, tp->th_msg, tmp_p); - } - - if (send(peer, buf, length, 0) != length) - syslog(LOG_WARNING, "nak: %m"); + struct tftphdr *tp; + int length; + struct errmsg *pe; + + tp = (struct tftphdr *)buf; + tp->th_opcode = htons((u_short)ERROR); + tp->th_code = htons((u_short)error); + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + break; + if (pe->e_code < 0) { + pe->e_msg = strerror(error - 100); + tp->th_code = EUNDEF; /* set 'undef' errorcode */ + } + strcpy(tp->th_msg, pe->e_msg); + length = strlen(pe->e_msg); + tp->th_msg[length] = '\0'; + length += 5; + + if ( verbosity >= 2 ) { + syslog(LOG_INFO, "sending NAK (%d, %s) to %s", + error, tp->th_msg, inet_ntoa(from.sin_addr)); + } + + if (send(peer, buf, length, 0) != length) + syslog(LOG_ERR, "nak: %m"); } diff --git a/tftpd/tftpd.h b/tftpd/tftpd.h index 277e5d2..789ee94 100644 --- a/tftpd/tftpd.h +++ b/tftpd/tftpd.h @@ -1,5 +1,6 @@ +/* $Id$ */ /* ----------------------------------------------------------------------- * - * + * * Copyright 2001 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license @@ -23,13 +24,4 @@ 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 deleted file mode 100644 index d346e2a..0000000 --- a/version +++ /dev/null @@ -1 +0,0 @@ -5.3