diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..544d21f
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[{*.c,*.h}]
+indent_style = space
+indent_size = 4
+tab_width = 8
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bb95251
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+/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 2ae9114..6b7b53c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,255 @@
-$Id$
+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.
+
Changes in 0.24:
Fix bugs in both client and server dealing with block number
diff --git a/INSTALL.tftp b/INSTALL.tftp
index 4bd87a5..914094f 100644
--- a/INSTALL.tftp
+++ b/INSTALL.tftp
@@ -1,5 +1,3 @@
-$Id$
-
Specific installation instructions
==================================
diff --git a/Makefile b/Makefile
index 4c2369e..2e790fd 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 tftp tftpd
+SUB = lib common tftp tftpd
-%.build:
+%.build: config/MCONFIG config/config.h version.h
$(MAKE) -C $(patsubst %.build, %, $@)
-%.install:
+%.install: config/MCONFIG config/config.h version.h
$(MAKE) -C $(patsubst %.install, %, $@) install
%.clean:
@@ -15,39 +15,55 @@ SUB = lib tftp tftpd
%.distclean:
$(MAKE) -C $(patsubst %.distclean, %, $@) distclean
-all: MCONFIG $(patsubst %, %.build, $(SUB))
+all: config/MCONFIG $(patsubst %, %.build, $(SUB))
-install: MCONFIG $(patsubst %, %.install, $(SUB))
+tftp.build: lib.build common.build
+tftpd.build: lib.build common.build
-clean: $(patsubst %, %.clean, $(SUB))
+install: config/MCONFIG $(patsubst %, %.install, $(SUB))
-distclean: $(patsubst %, %.distclean, $(SUB))
- rm -f MCONFIG config.status config.log acconfig.h *~ \#*
+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 *~ \#*
rm -rf *.cache
- find . -type f \( -name \*.orig -o -name \*.rej \) | xargs -r rm -f
+ find . -type f \( -name \*.orig -o -name \*.rej \) | xargs rm -f
spotless: distclean
- rm -f configure acconfig.h.in
+ rm -f configure config/config.h.in tftp.spec
-autoconf: configure acconfig.h.in
+autoconf: configure config/config.h.in
-config: MCONFIG acconfig.h
+config: config/MCONFIG config/config.h
release:
$(MAKE) autoconf
+ $(MAKE) tftp.spec
$(MAKE) distclean
-MCONFIG: configure MCONFIG.in acconfig.h.in
- ./configure
+config/MCONFIG: configure config/MCONFIG.in config/config.h.in
+ if test -x config.status; then \
+ ./config.status --recheck && ./config.status ; \
+ else \
+ ./configure ; \
+ fi
-acconfig.h: MCONFIG
+config/config.h: config/MCONFIG
: Generated by side effect
-acconfig.h.in: configure.in aclocal.m4
- autoheader -f
- touch -c acconfig.h.in
- rm -f acconfig.h
+configure: configure.ac
+ sh autogen.sh
-configure: configure.in aclocal.m4
- autoconf
- rm -f MCONFIG config.cache config.log 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 $@
diff --git a/README b/README
index fc25395..91afcb3 100644
--- a/README
+++ b/README
@@ -21,5 +21,7 @@ installation instructions.
This software can be discussed on the SYSLINUX mailing list. To
-subscribe, send a message containing the word "subscribe" in the body
-to .
+subscribe, go to the list subscription page at:
+
+ http://www.zytor.com/mailman/listinfo/syslinux
+
diff --git a/README.security b/README.security
index 7db6f2e..644babb 100644
--- a/README.security
+++ b/README.security
@@ -1,3 +1,26 @@
+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
deleted file mode 100644
index 310d5b0..0000000
--- a/aclocal.m4
+++ /dev/null
@@ -1,172 +0,0 @@
-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
new file mode 100644
index 0000000..26d55c4
--- /dev/null
+++ b/autoconf/m4/pa_add_cflags.m4
@@ -0,0 +1,9 @@
+dnl --------------------------------------------------------------------------
+dnl PA_ADD_CFLAGS(variable, flag [,actual_flag [,success [,failure]]]])
+dnl
+dnl Attempt to add the given option to xFLAGS, if it doesn't break
+dnl compilation. If the option to be tested is different than the
+dnl option that should actually be added, add the option to be
+dnl actually added as a second argument.
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_ADD_CFLAGS], [PA_ADD_FLAGS(CFLAGS, [$1], [$2], [$3], [$4])])
diff --git a/autoconf/m4/pa_add_flags.m4 b/autoconf/m4/pa_add_flags.m4
new file mode 100644
index 0000000..23f96f1
--- /dev/null
+++ b/autoconf/m4/pa_add_flags.m4
@@ -0,0 +1,39 @@
+dnl --------------------------------------------------------------------------
+dnl PA_ADD_FLAGS(flagvar, flags)
+dnl
+dnl Add [flags] to the variable [flagvar] if and only if it is accepted
+dnl by all languages affected by [flagvar], if those languages have
+dnl been previously seen in the script.
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_ADD_FLAGS],
+[
+ AS_VAR_PUSHDEF([old], [_$0_$1_orig])
+ AS_VAR_PUSHDEF([ok], [_$0_$1_ok])
+ AS_VAR_PUSHDEF([flags], [$1])
+
+ AS_VAR_COPY([old], [flags])
+ AS_VAR_SET([flags], ["$flags $2"])
+ AS_VAR_SET([ok], [yes])
+
+ PA_LANG_FOREACH(PA_FLAGS_LANGLIST($1),
+ [AS_VAR_IF([ok], [yes],
+ [AC_MSG_CHECKING([if $]_AC_CC[ accepts $2])
+ PA_BUILD_IFELSE([],
+ [AC_MSG_RESULT([yes])],
+ [AC_MSG_RESULT([no])
+ AS_VAR_SET([ok], [no])])])
+ ])
+
+ AS_VAR_IF([ok], [yes],
+ [m4_ifnblank([$3],[AS_VAR_SET([flags], ["$old $3"])])
+ m4_foreach_w([_pa_add_flags_flag], [m4_ifblank([$3],[$2],[$3])],
+ [AC_DEFINE(PA_SYM([$1_]_pa_add_flags_flag), 1,
+ [Define to 1 if compiled with the ]_pa_add_flags_flag[ compiler flag])])
+ $4],
+ [AS_VAR_SET([flags], ["$old"])
+ $5])
+
+ AS_VAR_POPDEF([flags])
+ AS_VAR_POPDEF([ok])
+ AS_VAR_POPDEF([old])
+])
diff --git a/autoconf/m4/pa_add_headers.m4 b/autoconf/m4/pa_add_headers.m4
new file mode 100644
index 0000000..d3c478a
--- /dev/null
+++ b/autoconf/m4/pa_add_headers.m4
@@ -0,0 +1,13 @@
+dnl --------------------------------------------------------------------------
+dnl PA_ADD_HEADERS(headers...)
+dnl
+dnl Call AC_CHECK_HEADERS(), and add to ac_includes_default if found
+dnl --------------------------------------------------------------------------
+AC_DEFUN([_PA_ADD_HEADER],
+[AC_CHECK_HEADERS([$1],[ac_includes_default="$ac_includes_default
+#include <$1>"
+])
+])
+
+AC_DEFUN([PA_ADD_HEADERS],
+[m4_map_args_w([$1],[_PA_ADD_HEADER(],[)])])
diff --git a/autoconf/m4/pa_add_langflags.m4 b/autoconf/m4/pa_add_langflags.m4
new file mode 100644
index 0000000..05c3114
--- /dev/null
+++ b/autoconf/m4/pa_add_langflags.m4
@@ -0,0 +1,27 @@
+dnl --------------------------------------------------------------------------
+dnl PA_ADD_LANGFLAGS(flag...)
+dnl
+dnl Attempt to add the option in the given list to each compiler flags
+dnl (CFLAGS, CXXFLAGS, ...), if it doesn't break compilation.
+dnl --------------------------------------------------------------------------
+m4_defun([_PA_LANGFLAG_VAR],
+[m4_case([$1],
+ [C], [CFLAGS],
+ [C++], [CXXFLAGS],
+ [Fortran 77], [FFLAGS],
+ [Fortran], [FCFLAGS],
+ [Erlang], [ERLCFLAGS],
+ [Objective C], [OBJCFLAGS],
+ [Objective C++], [OBJCXXFLAGS],
+ [Go], [GOFLAGS],
+ [m4_fatal([PA_ADD_LANGFLAGS: Unknown language: $1])])])
+
+AC_DEFUN([PA_ADD_LANGFLAGS],
+[m4_pushdef([_pa_langflags],m4_dquote($1))dnl
+m4_set_foreach(_PA_LANG_SEEN_SET,[_pa_lang],dnl
+[_pa_flag_found=no
+ m4_foreach_w([_pa_flag], _pa_langflags,
+ [AS_IF([test $_pa_flag_found = no],
+ [PA_ADD_FLAGS(_PA_LANGFLAG_VAR(_pa_lang),_pa_flag,[],[_pa_flag_found=yes])])
+ ])])
+m4_popdef([_pa_langflags])])
diff --git a/autoconf/m4/pa_arg_bool.m4 b/autoconf/m4/pa_arg_bool.m4
new file mode 100644
index 0000000..5289ed4
--- /dev/null
+++ b/autoconf/m4/pa_arg_bool.m4
@@ -0,0 +1,19 @@
+dnl --------------------------------------------------------------------------
+dnl PA_ARG_BOOL(option,helptext,default,enabled_action,disabled_action)
+dnl
+dnl The last three arguments are optional; default can be yes or no.
+dnl
+dnl Simpler-to-use versions of AC_ARG_ENABLED, that include the
+dnl test for $enableval and the AS_HELP_STRING definition. This is only
+dnl to be used for boolean options.
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_ARG_BOOL],
+[m4_pushdef([pa_default],m4_default(m4_normalize([$3]),[no]))
+ m4_pushdef([pa_option],m4_case(pa_default,[yes],[disable],[enable]))
+ AC_ARG_ENABLE([$1],
+ [AS_HELP_STRING([--]m4_defn([pa_option])[-$1],[$2])],
+ [pa_arg_bool_enableval="$enableval"],
+ [pa_arg_bool_enableval="]m4_defn([pa_default])["])
+ m4_popdef([pa_option], [pa_default])
+ AS_IF([test x"$pa_arg_bool_enableval" != xno], [$4], [$5])
+])
diff --git a/autoconf/m4/pa_arg_disabled.m4 b/autoconf/m4/pa_arg_disabled.m4
new file mode 100644
index 0000000..42f4ce8
--- /dev/null
+++ b/autoconf/m4/pa_arg_disabled.m4
@@ -0,0 +1,4 @@
+dnl --------------------------------------------------------------------------
+dnl PA_ARG_DISABLED(option,helptext,disabled_action,enabled_action)
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_ARG_DISABLED],[PA_ARG_BOOL([$1],[$2],yes,[$4],[$3])])
diff --git a/autoconf/m4/pa_arg_enabled.m4 b/autoconf/m4/pa_arg_enabled.m4
new file mode 100644
index 0000000..7d66a21
--- /dev/null
+++ b/autoconf/m4/pa_arg_enabled.m4
@@ -0,0 +1,4 @@
+dnl --------------------------------------------------------------------------
+dnl PA_ARG_ENABLED(option,helptext,enabled_action,disabled_action)
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_ARG_ENABLED],[PA_ARG_BOOL([$1],[$2],no,[$3],[$4])])
diff --git a/autoconf/m4/pa_build_ifelse.m4 b/autoconf/m4/pa_build_ifelse.m4
new file mode 100644
index 0000000..1aacfcd
--- /dev/null
+++ b/autoconf/m4/pa_build_ifelse.m4
@@ -0,0 +1,16 @@
+dnl --------------------------------------------------------------------------
+dnl PA_BUILD_IFELSE(input [,success [,failure]])
+dnl
+dnl Same as AC_LINK_IFELSE for languages where linking is applicable,
+dnl otherwise AC_COMPILE_IFELSE.
+dnl
+dnl If the first argument is empty, use _AC_LANG_IO_PROGRAM.
+dnl --------------------------------------------------------------------------
+m4_defun([_PA_BUILD_IFELSE],
+[m4_case(_AC_LANG,
+ [Erlang], [AC_COMPILE_IFELSE($@)],
+ [AC_LINK_IFELSE($@)])])
+
+AC_DEFUN([PA_BUILD_IFELSE],
+[_PA_BUILD_IFELSE([m4_ifblank([$1],[AC_LANG_SOURCE(_AC_LANG_IO_PROGRAM)],
+ [$1])],[$2],[$3])])
diff --git a/autoconf/m4/pa_c_typeof.m4 b/autoconf/m4/pa_c_typeof.m4
new file mode 100644
index 0000000..909b171
--- /dev/null
+++ b/autoconf/m4/pa_c_typeof.m4
@@ -0,0 +1,32 @@
+dnl --------------------------------------------------------------------------
+dnl PA_C_TYPEOF
+dnl
+dnl Find if typeof() exists, or an equivalent (__typeof__, decltype,
+dnl __decltype__)
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_C_TYPEOF],
+[AC_CACHE_CHECK([if $CC supports typeof], [pa_cv_typeof],
+ [pa_cv_typeof=no
+ for pa_typeof_try in typeof __typeof __typeof__ decltype __decltype __decltype__ _Decltype
+ do
+ AS_IF([test $pa_cv_typeof = no],
+ [AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+AC_INCLUDES_DEFAULT
+int testme(int x);
+int testme(int x)
+{
+ $pa_typeof_try(x) y = x*x;
+ return y;
+}
+])],
+ [pa_cv_typeof=$pa_typeof_try])])
+ done
+ ])
+ AS_IF([test $pa_cv_typeof = no],
+ [],
+ [AC_DEFINE([HAVE_TYPEOF], 1,
+ [Define to 1 if you have some version of the typeof operator.])
+ AS_IF([test $pa_cv_typeof = typeof],
+ [],
+ [AC_DEFINE_UNQUOTED([typeof], [$pa_cv_typeof],
+ [Define if your typeof operator is not named `typeof'.])])])])
diff --git a/autoconf/m4/pa_check_bad_stdc_inline.m4 b/autoconf/m4/pa_check_bad_stdc_inline.m4
new file mode 100644
index 0000000..3fbc53a
--- /dev/null
+++ b/autoconf/m4/pa_check_bad_stdc_inline.m4
@@ -0,0 +1,26 @@
+dnl --------------------------------------------------------------------------
+dnl PA_CHECK_BAD_STDC_INLINE
+dnl
+dnl Some versions of gcc seem to apply -Wmissing-prototypes to C99
+dnl inline functions, which means we need to use GNU inline syntax
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_CHECK_BAD_STDC_INLINE],
+[AC_MSG_CHECKING([if $CC supports C99 external inlines])
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+AC_INCLUDES_DEFAULT
+
+/* Don't mistake GNU inlines for c99 */
+#if defined(__GNUC__) && !defined(__GNUC_STDC_INLINE__)
+# error "Using gnu inline standard"
+#endif
+
+inline int foo(int x)
+{
+ return x+1;
+}
+ ])],
+ [AC_MSG_RESULT([yes])
+ AC_DEFINE([HAVE_STDC_INLINE], 1,
+ [Define to 1 if your compiler supports C99 extern inline])],
+ [AC_MSG_RESULT([no])
+ PA_ADD_CFLAGS([-fgnu89-inline])])])
diff --git a/autoconf/m4/pa_check_inttypes_h_sane.m4 b/autoconf/m4/pa_check_inttypes_h_sane.m4
new file mode 100644
index 0000000..589e0a2
--- /dev/null
+++ b/autoconf/m4/pa_check_inttypes_h_sane.m4
@@ -0,0 +1,17 @@
+dnl ------------------------------------------------------------------------
+dnl PA_CHECK_INTTYPES_H_SANE
+dnl
+dnl At least some versions of AIX 4 have macros which are
+dnl completely broken. Try to detect those.
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_CHECK_INTTYPES_H_SANE],
+[AC_CHECK_HEADERS_ONCE(inttypes.h)
+AS_IF([test "x$ac_cv_header_inttypes_h" = xyes],[
+ AC_MSG_CHECKING([if inttypes.h is sane])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+ [uintmax_t max = UINTMAX_C(0);
+ printf("%"PRIuMAX"\n", max);])],
+ [AC_MSG_RESULT([yes])
+ AC_DEFINE(INTTYPES_H_IS_SANE, 1,
+ [Define if the macros in are usable])],
+ [AC_MSG_RESULT([no (AIX, eh?)])])])])
diff --git a/autoconf/m4/pa_cross_compile.m4 b/autoconf/m4/pa_cross_compile.m4
new file mode 100644
index 0000000..f082973
--- /dev/null
+++ b/autoconf/m4/pa_cross_compile.m4
@@ -0,0 +1,41 @@
+dnl --------------------------------------------------------------------------
+dnl PA_CROSS_COMPILE
+dnl
+dnl Get the canonical name for the build and host (runtime) systems;
+dnl then figure out if this is cross-compilation. Specifically, this
+dnl disables invoking WINE on non-Windows systems which are configured
+dnl to run WINE automatically.
+dnl
+dnl Use PA_CROSS_COMPILE_TOOL if the target system (output of a code-
+dnl generation tool) is applicable.
+dnl
+dnl This doesn't explicitly print any messages as that is automatically
+dnl done elsewhere.
+dnl --------------------------------------------------------------------------
+AC_DEFUN_ONCE([PA_CROSS_COMPILE],
+[
+ AC_BEFORE([$0], [AC_LANG_COMPILER])
+ AC_BEFORE([$0], [AC_LANG])
+ AC_BEFORE([$0], [AC_PROG_CC])
+ AC_BEFORE([$0], [AC_PROG_CPP])
+ AC_BEFORE([$0], [AC_PROG_CXX])
+ AC_BEFORE([$0], [AC_PROG_CXXCPP])
+ AC_BEFORE([$0], [AC_PROG_OBJC])
+ AC_BEFORE([$0], [AC_PROG_OBJCPP])
+ AC_BEFORE([$0], [AC_PROG_OBJCXX])
+ AC_BEFORE([$0], [AC_PROG_OBJCXXCPP])
+ AC_BEFORE([$0], [AC_PROG_F77])
+ AC_BEFORE([$0], [AC_PROG_FC])
+ AC_BEFORE([$0], [AC_PROG_GO])
+
+ # Disable WINE
+ WINELOADER=/dev/null
+ export WINELOADER
+ WINESERVER=/dev/null
+ export WINESERVER
+ WINEPREFIX=/dev/null
+ export WINEPREFIX
+
+ AC_CANONICAL_BUILD
+ AC_CANONICAL_HOST
+])
diff --git a/autoconf/m4/pa_flags_langlist.m4 b/autoconf/m4/pa_flags_langlist.m4
new file mode 100644
index 0000000..2ef5ded
--- /dev/null
+++ b/autoconf/m4/pa_flags_langlist.m4
@@ -0,0 +1,19 @@
+dnl --------------------------------------------------------------------------
+dnl PA_FLAGS_LANGLIST(flagvar)
+dnl
+dnl Return a list of languages affected by the variable flagvar.
+dnl If flagvar is unknown, assume it affects the current language.
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_FLAGS_LANGLIST],
+[m4_dquote(m4_case([$1],
+ [CPPFLAGS], [[C],[C++],[Objective C],[Objective C++]],
+ [CFLAGS], [[C]],
+ [CXXFLAGS], [[C++]],
+ [FFLAGS], [[Fortran 77]],
+ [FCFLAGS], [[Fortran]],
+ [ERLCFLAGS], [[Erlang]],
+ [OBJCFLAGS], [[Objective C]],
+ [OBJCXXFLAGS], [[Objective C++]],
+ [GOFLAGS], [[Go]],
+ [LDFLAGS], [[C],[C++],[Fortran 77],[Fortran],[Objective C],[Objective C++],[Go]],
+ m4_dquote(_AC_LANG)))])
diff --git a/autoconf/m4/pa_have_tcpwrappers.m4 b/autoconf/m4/pa_have_tcpwrappers.m4
new file mode 100644
index 0000000..fba87ce
--- /dev/null
+++ b/autoconf/m4/pa_have_tcpwrappers.m4
@@ -0,0 +1,26 @@
+dnl --------------------------------------------------------------------------
+dnl PA_HAVE_TCPWRAPPERS
+dnl
+dnl Do we have the tcpwrappers -lwrap? This can't be done using AC_CHECK_LIBS
+dnl due to the need to provide "allow_severity" and "deny_severity" variables
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_HAVE_TCPWRAPPERS],
+[AC_CHECK_LIB([wrap], [main])
+ AC_MSG_CHECKING([for tcpwrappers])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [[
+#include
+int allow_severity = 0;
+int deny_severity = 0;
+ ]],
+ [[
+ hosts_ctl("sample_daemon", STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN);
+ ]])],
+ [
+ AC_DEFINE(HAVE_TCPWRAPPERS, 1,
+ [Define if we have tcpwrappers (-lwrap) and .])
+ AC_MSG_RESULT([yes])
+ ],
+ [
+ AC_MSG_RESULT([no])
+ ])])
diff --git a/autoconf/m4/pa_lang_foreach.m4 b/autoconf/m4/pa_lang_foreach.m4
new file mode 100644
index 0000000..650913d
--- /dev/null
+++ b/autoconf/m4/pa_lang_foreach.m4
@@ -0,0 +1,15 @@
+dnl --------------------------------------------------------------------------
+dnl PA_LANG_FOREACH(subset, body)
+dnl
+dnl Expand [body] for each language encountered in the configure script also
+dnl present in [subset], or all if [subset] is empty
+dnl --------------------------------------------------------------------------
+AC_DEFUN([_PA_LANG_DO],dnl
+[AC_LANG([$2])dnl
+$1])
+
+AC_DEFUN([PA_LANG_FOREACH],dnl
+[m4_pushdef([_pa_lang_foreach_current],[_AC_LANG])dnl
+m4_map_args([m4_curry([_PA_LANG_DO],[$2])],m4_unquote(PA_LANG_SEEN_LIST($1)))dnl
+AC_LANG(_pa_lang_foreach_current)dnl
+m4_popdef([_pa_lang_foreach_current])])
diff --git a/autoconf/m4/pa_lang_seen_list.m4 b/autoconf/m4/pa_lang_seen_list.m4
new file mode 100644
index 0000000..d524013
--- /dev/null
+++ b/autoconf/m4/pa_lang_seen_list.m4
@@ -0,0 +1,20 @@
+dnl --------------------------------------------------------------------------
+dnl PA_LANG_SEEN_LIST(subset)
+dnl
+dnl List of the language lang has been used in the configuration
+dnl script so far, possibly subset by [subset].
+dnl
+dnl This relies on overriding _AC_LANG_SET(from, to),
+dnl the internal implementation of _AC_LANG.
+dnl --------------------------------------------------------------------------
+m4_ifndef([_PA_LANG_SET],
+[m4_rename([_AC_LANG_SET], [_PA_LANG_SET])dnl
+m4_defun([_AC_LANG_SET], [m4_set_add([_PA_LANG_SEEN_SET],[$2])dnl
+_PA_LANG_SET($@)])])
+
+AC_DEFUN([PA_LANG_SEEN_LIST],
+[m4_set_delete([_pa_lang_seen_subset])dnl
+m4_pushdef([_pa_lang_seen_subset_list],m4_ifnblank([$1],[$1],m4_dquote(m4_set_list([_PA_LANG_SEEN_SET]))))dnl
+m4_set_add_all([_pa_lang_seen_subset],_pa_lang_seen_subset_list)dnl
+m4_cdr(m4_set_intersection([_pa_lang_seen_subset],[_PA_LANG_SEEN_SET]))dnl
+m4_popdef([_pa_lang_seen_subset_list])])
diff --git a/autoconf/m4/pa_option_debug.m4 b/autoconf/m4/pa_option_debug.m4
new file mode 100644
index 0000000..ae7d9db
--- /dev/null
+++ b/autoconf/m4/pa_option_debug.m4
@@ -0,0 +1,13 @@
+dnl --------------------------------------------------------------------------
+dnl PA_OPTION_DEBUG(with_debug, without_debug)
+dnl
+dnl Set debug flags and optimization flags depending on if
+dnl --enable-debug is set or not. Some flags are set regardless...
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_OPTION_DEBUG],
+[PA_ARG_DISABLED([gdb], [disable gdb debug extensions],
+ [PA_ADD_LANGFLAGS([-g3])], [PA_ADD_LANGFLAGS([-ggdb3 -g3])])
+ PA_ARG_ENABLED([debug], [optimize for debugging],
+ [PA_ADD_LANGFLAGS([-Og -O0])
+ $1],
+ [$2])])
diff --git a/autoconf/m4/pa_option_profiling.m4 b/autoconf/m4/pa_option_profiling.m4
new file mode 100644
index 0000000..39a3f6c
--- /dev/null
+++ b/autoconf/m4/pa_option_profiling.m4
@@ -0,0 +1,8 @@
+dnl --------------------------------------------------------------------------
+dnl PA_OPTION_PROFILING(with_profiling, without_profiling)
+dnl
+dnl Try to enable profiling if --enable-profiling is set.
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_OPTION_PROFILING],
+[PA_ARG_ENABLED([profiling], [compile with profiling (-pg option)],
+[PA_ADD_LANGFLAGS([-pg])])])
diff --git a/autoconf/m4/pa_prog_cc.m4 b/autoconf/m4/pa_prog_cc.m4
new file mode 100644
index 0000000..0debe5d
--- /dev/null
+++ b/autoconf/m4/pa_prog_cc.m4
@@ -0,0 +1,13 @@
+dnl --------------------------------------------------------------------------
+dnl PA_PROG_CC()
+dnl
+dnl Similar to AC_PROG_CC, but add a prototype for main() to
+dnl AC_INCLUDES_DEFAULT to avoid -Werror from breaking compilation.
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_PROG_CC],
+[AC_PROG_CC
+ AS_IF([test x$ac_cv_prog != xno],
+ [ac_includes_default="$ac_includes_default
+#ifndef __cplusplus
+extern int main(void);
+#endif"])])
diff --git a/autoconf/m4/pa_search_libs_and_add.m4 b/autoconf/m4/pa_search_libs_and_add.m4
new file mode 100644
index 0000000..b006128
--- /dev/null
+++ b/autoconf/m4/pa_search_libs_and_add.m4
@@ -0,0 +1,22 @@
+dnl --------------------------------------------------------------------------
+dnl PA_SEARCH_LIBS_AND_ADD
+dnl
+dnl PA_SEARCH_LIBS_AND_ADD(function, libraries [,function to add])
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_SEARCH_LIBS_AND_ADD],
+ [
+ AH_TEMPLATE(AS_TR_CPP(HAVE_$1), [Define if $1 function was found])
+ AC_SEARCH_LIBS($1, $2,
+ [
+ AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1))
+ pa_add_$1=false;
+ ],
+ [
+ XTRA=true;
+ if test $# -eq 3; then
+ AC_LIBOBJ($3)
+ else
+ AC_LIBOBJ($1)
+ fi
+ pa_add_$1=true;
+ ])])
diff --git a/autoconf/m4/pa_sigsetjmp.m4 b/autoconf/m4/pa_sigsetjmp.m4
new file mode 100644
index 0000000..898b562
--- /dev/null
+++ b/autoconf/m4/pa_sigsetjmp.m4
@@ -0,0 +1,25 @@
+dnl --------------------------------------------------------------------------
+dnl PA_SIGSETJMP
+dnl
+dnl Do we have sigsetjmp/siglongjmp? (AC_CHECK_FUNCS doesn't seem to work
+dnl for these particular functions.)
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_SIGSETJMP],
+[AC_MSG_CHECKING([for sigsetjmp])
+ AC_LINK_IFELSE([AC_LANG_SOURCE(
+[
+AC_INCLUDES_DEFAULT
+#include
+
+int main(void) {
+ sigjmp_buf buf;
+ if (sigsetjmp(buf,1))
+ return 0;
+ siglongjmp(buf,2);
+ return 1;
+ }
+])],
+ [AC_MSG_RESULT([yes])
+ AC_DEFINE([HAVE_SIGSETJMP], 1,
+ [Define to 1 if your system has sigsetjmp/siglongjmp])],
+ [AC_MSG_RESULT([no])])])
diff --git a/autoconf/m4/pa_sym.m4 b/autoconf/m4/pa_sym.m4
new file mode 100644
index 0000000..d3a8965
--- /dev/null
+++ b/autoconf/m4/pa_sym.m4
@@ -0,0 +1,11 @@
+dnl --------------------------------------------------------------------------
+dnl PA_SYM(prefix, string)
+dnl
+dnl Convert a (semi-) arbitrary string to a CPP symbol
+dnl Compact underscores and convert non-C characters to underscore,
+dnl except + which is converted to X (so C++ -> CXX).
+dnl --------------------------------------------------------------------------
+AC_DEFUN([PA_SYM],
+[m4_bpatsubsts(m4_quote(m4_toupper([$*])),
+ [,],[],[\+],[X],[[^ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]+],[_],dnl
+[^._?\(.*\)_.$],[[\1]])])
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..0c892cc
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,83 @@
+#!/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
new file mode 100644
index 0000000..89e351d
--- /dev/null
+++ b/common/Makefile
@@ -0,0 +1,25 @@
+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
new file mode 100644
index 0000000..ccd4af8
--- /dev/null
+++ b/common/signal.c
@@ -0,0 +1,19 @@
+/*
+ * signal.c
+ *
+ * User-friendly wrapper around sigaction().
+ */
+
+#include "config.h"
+
+int tftp_signal(int signum, sighandler_t handler, int flags)
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof sa);
+ sa.sa_handler = handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = flags;
+
+ return sigaction(signum, &sa, NULL);
+}
diff --git a/common/tftpsubs.c b/common/tftpsubs.c
new file mode 100644
index 0000000..8c999f6
--- /dev/null
+++ b/common/tftpsubs.c
@@ -0,0 +1,406 @@
+/*
+ * 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/tftp/tftpsubs.h b/common/tftpsubs.h
similarity index 55%
rename from tftp/tftpsubs.h
rename to common/tftpsubs.h
index 645c6b9..b3a3bf3 100644
--- a/tftp/tftpsubs.h
+++ b/common/tftpsubs.h
@@ -1,8 +1,3 @@
-/* $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.
@@ -34,8 +29,6 @@
* 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
*/
/*
@@ -47,30 +40,82 @@
#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
-/*
- * Prototype for xmalloc/xstrdup
- */
-extern void *xmalloc(size_t);
-extern char *xstrdup(const char *);
-
-/*
- * Signal-related stuff
- */
-void (*bsd_signal(int, void (*)(int)))(int);
+int pick_port_bind(int sockfd, union sock_addr *myaddr,
+ unsigned int from, unsigned int to);
#endif
diff --git a/config.h b/config.h
index 7585381..a8c55ac 100644
--- a/config.h
+++ b/config.h
@@ -1,13 +1,12 @@
/* -*- c -*- ------------------------------------------------------------- *
- *
- * Copyright 2001 H. Peter Anvin - All Rights Reserved
+ *
+ * Copyright 2001-2024 H. Peter Anvin - All Rights Reserved
*
* This program is free software available under the same license
* as the "OpenBSD" operating system, distributed at
* http://www.openbsd.org/.
*
* ----------------------------------------------------------------------- */
-/* $Id$ */
/*
* config.h
@@ -18,21 +17,23 @@
#ifndef CONFIG_H
#define CONFIG_H 1
+/* Feature enables for specific environments */
+#ifdef __APPLE__
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1070
+#define __APPLE_USE_RFC_3542 1
+#endif
+#endif
+
/* Must be included before we include any system headers! */
-#include "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
+#include "config/config.h" /* autogenerated configuration header */
/* Standard includes */
#include
+#include
+#include
+#include
+#include
#ifdef HAVE_SYS_TYPES_H
#include
@@ -42,31 +43,18 @@
#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
@@ -77,9 +65,62 @@
#include
#endif
+#ifdef HAVE_SETJMP_H
#include
-#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
+#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 */
@@ -87,9 +128,9 @@
#ifdef HAVE_LONG_LONG
typedef long long intmax_t;
typedef unsigned long long uintmax_t;
-#define PRIdMAX "Ld"
-#define PRIuMAX "Lu"
-#define PRIxMAX "Lx"
+#define PRIdMAX "lld"
+#define PRIuMAX "llu"
+#define PRIxMAX "llx"
#define INTMAX_C(x) (x##LL)
#define UINTMAX_C(x) (x##ULL)
#else
@@ -103,6 +144,37 @@ 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)
@@ -132,6 +204,11 @@ typedef unsigned long u_long;
#endif
#endif
+/* socklen_t */
+#ifndef HAVE_SOCKLEN_T
+typedef int socklen_t;
+#endif
+
/* sysexits.h */
#ifdef HAVE_SYSEXITS_H
@@ -172,12 +249,118 @@ typedef unsigned long u_long;
#endif
#endif
-/* Sometimes IPPORT_TFTP isn't defined */
+/* netinet/in.h, and possible missing pieces */
-#ifndef HAVE_IPPORT_TFTP_DEFINITION
-#ifndef IPPORT_TFTP
+#include
+
+#if !HAVE_DECL_IPPORT_TFTP && !defined(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/MCONFIG.in b/config/MCONFIG.in
similarity index 73%
rename from MCONFIG.in
rename to config/MCONFIG.in
index d9d5718..2660f7e 100644
--- a/MCONFIG.in
+++ b/config/MCONFIG.in
@@ -1,13 +1,12 @@
## -*- makefile -*- ------------------------------------------------------
##
-## Copyright 2001 H. Peter Anvin - All Rights Reserved
+## Copyright 2001-2007 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
@@ -15,6 +14,10 @@
## Basic Makefile definitions
##
+# Source and object root
+SRCROOT = @SRCROOT@
+OBJROOT = @OBJROOT@
+
# Prefixes
prefix = @prefix@
exec_prefix = @exec_prefix@
@@ -28,9 +31,19 @@ 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@
@@ -43,8 +56,9 @@ CFLAGS = @CFLAGS@ -I$(SRCROOT)
# Link flags
LDFLAGS = @LDFLAGS@
-# Libraries
-LIBS = @LIBS@
+# Libraries (client and server)
+TFTP_LIBS = ../common/libcommon.a @TFTP_LIBS@
+TFTPD_LIBS = ../common/libcommon.a @TFTPD_LIBS@
# Additional library we need to build
LIBOBJS = @LIBOBJS@
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..deaa4fc
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,283 @@
+dnl Process this file with autoconf 2.71 or later to produce
+dnl a configure script.
+AC_PREREQ([2.71])
+AC_INIT
+AC_CONFIG_SRCDIR([MRULES])
+AC_PREFIX_DEFAULT([/usr])
+AC_CONFIG_AUX_DIR([autoconf/helpers])
+
+dnl This prevents us from running Wine and thinking we are not
+dnl cross-compiling when in fact we are; running Wine here is at
+dnl the best very slow and doesn't buy us a single thing at all.
+PA_CROSS_COMPILE
+
+dnl Enable any available C extensions
+PA_PROG_CC
+AC_USE_SYSTEM_EXTENSIONS
+
+dnl Options for debugging and profiling
+PA_OPTION_DEBUG
+PA_OPTION_PROFILING
+
+dnl LLVM doesn't error out on invalid -W options unless this option is
+dnl specified first. Enable this so this script can actually discover
+dnl which -W options are possible for this compiler.
+PA_ADD_CFLAGS([-Werror=unknown-warning-option])
+
+dnl Force gcc and gcc-compatible compilers treat signed integers
+dnl as 2's complement
+PA_ADD_CFLAGS([-fwrapv])
+
+dnl Force clang to behave in a predictable manner, in order to make bugs
+dnl possible to track down. gcc appears to have this behavior by default.
+PA_ADD_CFLAGS([-ftrivial-auto-var-init=zero])
+
+dnl Some environments abuse __STRICT_ANSI__ to disable some
+dnl function declarations
+PA_ADD_CFLAGS([-U__STRICT_ANSI__])
+
+dnl Don't put things in common if we can avoid it. We don't want to
+dnl assume all compilers support common, and this will help find those
+dnl problems. This also works around an OSX linker problem.
+PA_ADD_CFLAGS([-fno-common])
+
+dnl Tests which may trigger warnings on some compilers
+AC_C_CONST
+AC_C_INLINE
+AC_C_RESTRICT
+
+dnl Checks for header files.
+AC_CHECK_INCLUDES_DEFAULT
+
+dnl See if we need extra libraries
+XTRA=false
+
+AC_SEARCH_LIBS([strerror],[cposix])
+
+AC_CHECK_HEADERS_ONCE(inttypes.h)
+AC_CHECK_HEADERS_ONCE(stdint.h)
+AC_CHECK_HEADERS_ONCE(grp.h)
+AC_CHECK_HEADERS_ONCE(libgen.h)
+AC_CHECK_HEADERS_ONCE(setjmp.h)
+AC_CHECK_HEADERS_ONCE(strings.h)
+AC_CHECK_HEADERS_ONCE(sysexits.h)
+AC_CHECK_HEADERS_ONCE(unistd.h)
+AC_CHECK_HEADERS_ONCE(sys/filio.h)
+AC_CHECK_HEADERS_ONCE(sys/stat.h)
+AC_CHECK_HEADERS_ONCE(sys/time.h)
+PA_CHECK_INTTYPES_H_SANE
+
+dnl This is needed on some versions of FreeBSD...
+AC_CHECK_HEADERS_ONCE(machine/param.h)
+
+dnl Windows...
+PA_ADD_HEADERS(windows.h)
+PA_ADD_HEADERS(winsock2.h)
+AS_IF([test "x$ac_cv_header_winsock2_h" != xyes],
+ [PA_ADD_HEADERS(winsock.h)])
+
+PA_ADD_HEADERS(fcntl.h)
+PA_ADD_HEADERS(sys/types.h)
+PA_ADD_HEADERS(arpa/inet.h)
+PA_ADD_HEADERS(sys/socket.h)
+PA_ADD_HEADERS(sys/file.h)
+PA_ADD_HEADERS(netinet/in.h)
+PA_ADD_HEADERS(sys/uio.h)
+PA_ADD_HEADERS(netdb.h)
+
+AC_TYPE_OFF_T
+AC_TYPE_PID_T
+AC_TYPE_MODE_T
+AC_TYPE_SIZE_T
+AC_CHECK_TYPES(intmax_t)
+AC_CHECK_TYPES(long long)
+AC_CHECK_TYPES(uint16_t)
+AC_CHECK_TYPES(uint32_t)
+AC_CHECK_TYPES(u_short)
+AC_CHECK_TYPES(u_long)
+
+AC_CHECK_TYPES(socklen_t)
+
+AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], ,
+ [AC_MSG_ERROR(socket library not found)])
+
+AC_CHECK_FUNCS(fcntl)
+AC_CHECK_FUNCS(flock)
+AC_CHECK_FUNCS(setsid)
+AC_CHECK_FUNCS(recvmsg)
+AC_CHECK_FUNCS(ftruncate)
+AC_CHECK_FUNCS(setresuid)
+AC_CHECK_FUNCS(setreuid)
+AC_CHECK_FUNCS(setresgid)
+AC_CHECK_FUNCS(setregid)
+AC_CHECK_FUNCS(initgroups)
+AC_CHECK_FUNCS(setgroups)
+AC_CHECK_TYPES(sighandler_t)
+
+dnl Solaris 8 has [u]intmax_t but not strtoumax(). How utterly braindamaged.
+AC_CHECK_FUNCS(strtoumax)
+AC_CHECK_FUNCS(strtoull)
+
+AC_CHECK_MEMBERS(struct msghdr.msg_control)
+AC_CHECK_MEMBERS(struct in_pktinfo.ipi_addr)
+AC_CHECK_MEMBERS(struct addrinfo.ai_addr)
+
+AC_CHECK_DECLS([O_NONBLOCK, O_BINARY, O_TEXT])
+AC_CHECK_DECLS([F_SETLK])
+AC_CHECK_DECLS([LOCK_SH, LOCK_EX])
+
+PA_SIGSETJMP
+
+dnl
+dnl Get common paths
+dnl
+SRCROOT=`cd $srcdir && pwd`
+OBJROOT=`pwd`
+
+PA_SEARCH_LIBS_AND_ADD(xmalloc, iberty)
+PA_SEARCH_LIBS_AND_ADD(xstrdup, iberty)
+PA_SEARCH_LIBS_AND_ADD(getopt_long, getopt, getopt_long)
+PA_SEARCH_LIBS_AND_ADD(getaddrinfo, [nsl resolv])
+AS_IF([$pa_add_getaddrinfo],
+[AC_SEARCH_LIBS(gethostbyname, [nsl resolv],
+ [AC_SEARCH_LIBS(herror, [nsl resolv], ,
+ [AC_MSG_ERROR(herror not found)])],
+ [AC_MSG_ERROR(gethostbyname not found)])],
+[AC_SEARCH_LIBS(freeaddrinfo, [nsl resolv], ,
+ [AC_MSG_ERROR(getaddrinfo but not freeaddrinfo found)])
+ AC_SEARCH_LIBS(gai_strerror, [nsl resolv], ,
+ [AC_MSG_ERROR(getaddrinfo but not gai_strerror found)])])
+
+PA_SEARCH_LIBS_AND_ADD(inet_ntop, [nsl resolv])
+AS_IF([$pa_add_inet_ntop],
+ [AC_SEARCH_LIBS(inet_ntoa, [nsl resolv], ,
+ [AC_MSG_ERROR(inet_ntoa not found)])])
+
+AC_SEARCH_LIBS(inet_aton, [nsl resolv], ,[AC_MSG_ERROR(inet_aton not found)])
+
+PA_SEARCH_LIBS_AND_ADD(daemon)
+PA_SEARCH_LIBS_AND_ADD(dup2)
+
+AS_IF([$XTRA], [XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS"])
+
+dnl
+dnl These libraries apply to the server only
+dnl
+
+common_libs="$LIBS"
+
+AC_CHECK_DECLS(IPPORT_TFTP)
+
+PA_ARG_DISABLED([tcpwrappers],
+ [disable tcpwrapper permissions checking], [],
+ [
+ AC_SEARCH_LIBS(yp_get_default_domain, [nsl resolv])
+ PA_HAVE_TCPWRAPPERS
+ ])
+
+AC_CHECK_HEADERS_ONCE([regex.h])
+PA_ARG_DISABLED([remap],
+ [disable regex-based filename remapping], [],
+ [AS_IF([test x"$ac_cv_header_regex_h" = xyes],
+ [AC_SEARCH_LIBS(regcomp, [regex rx],
+ [AC_DEFINE([WITH_REGEX], 1,
+ [Define if we are compiling with regex filename remapping.])
+ TFTPDOBJS="remap.\$(O) $TFTPOBJS"])])])
+
+TFTPD_LIBS="$LIBS $XTRALIBS"
+LIBS="$common_libs"
+
+dnl
+dnl These libraries apply to the client only
+dnl
+
+AH_TEMPLATE([WITH_READLINE],
+[Define if we are compiling with readline/editline command-line editing.])
+
+PA_ARG_DISABLED([readline],
+ [disable the use of readline command-line editing], [],
+ [
+ AC_CHECK_HEADER([readline/readline.h],
+ [
+ dnl readline may need libtermcap or somesuch...
+ AC_SEARCH_LIBS(tputs, [termcap terminfo])
+
+ AC_SEARCH_LIBS(readline, [readline history],
+ [AC_DEFINE(WITH_READLINE)])
+ AC_CHECK_HEADERS(readline/history.h)
+ ],
+ [AC_CHECK_HEADER([editline/readline.h],
+ [
+ dnl editline may need libtermcap or somesuch...
+ AC_SEARCH_LIBS(tputs, [termcap terminfo])
+
+ AC_SEARCH_LIBS(editline, [edit],
+ [AC_DEFINE(WITH_READLINE)])
+ ])])
+],:)
+
+TFTP_LIBS="$LIBS $XTRALIBS"
+LIBS="$common_libs"
+
+dnl
+dnl Check for IPV6 and disable-ipv6
+dnl
+
+AC_CHECK_MEMBERS(struct sockaddr_in6.sin6_addr)
+AC_MSG_CHECKING([for IPv6 support])
+PA_ARG_DISABLED([ipv6],
+ [disable support for IPv6],
+ [AC_MSG_RESULT(disabled)],
+ [AS_IF([test x"$ac_cv_member_struct_sockaddr_in6_sin6_addr$ac_cv_member_struct_addrinfo_ai_addr" = xyesyes],
+ [AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_IPV6, 1, [define if IPv6 support is enabled])],
+ [AC_MSG_RESULT(no)
+ AC_MSG_WARN([*** we do not have required IPv6 structs - IPv6 will be disabled])])])
+
+AC_SUBST(SRCROOT)
+AC_SUBST(OBJROOT)
+
+AC_SUBST(TFTP_LIBS)
+AC_SUBST(TFTPD_LIBS)
+AC_SUBST(TFTPDOBJS)
+
+AC_PROG_LN_S
+AC_PROG_RANLIB
+
+dnl
+dnl Make sure the install program has an absolute path if it
+dnl has a path at all. autoconf doesn't do this "in order
+dnl to not pollute the cache." Sigh.
+dnl Note: the $ needs to be double-quoted for reasons unknown.
+dnl
+AC_PROG_INSTALL
+[if echo "$INSTALL" | grep '^[^/].*/' > /dev/null 2>&1; then
+ INSTALL='\${SRCROOT}'/"$INSTALL"
+fi]
+
+PA_ADD_CFLAGS(-W)
+PA_ADD_CFLAGS(-Wall)
+PA_ADD_CFLAGS(-Wpointer-arith)
+PA_ADD_CFLAGS(-Wbad-function-cast)
+PA_ADD_CFLAGS(-Wcast-equal)
+PA_ADD_CFLAGS(-Wstrict-prototypes)
+PA_ADD_CFLAGS(-Wmissing-prototypes)
+PA_ADD_CFLAGS(-Wmissing-declarations)
+PA_ADD_CFLAGS(-Wnested-externs)
+PA_ADD_CFLAGS(-Winline)
+PA_ADD_CFLAGS(-Wwrite-strings)
+PA_ADD_CFLAGS(-Wundef)
+PA_ADD_CFLAGS(-Wshadow)
+PA_ADD_CFLAGS(-Wsign-compare)
+PA_ADD_CFLAGS(-fno-strict-aliasing)
+
+dnl
+dnl Test compiler features. On some compilers, this can be affected
+dnl by -Werror options, so run this *after* those options are added.
+dnl
+PA_CHECK_BAD_STDC_INLINE
+PA_C_TYPEOF
+
+AC_CONFIG_HEADERS([config/config.h])
+AC_CONFIG_FILES([config/MCONFIG])
+AC_OUTPUT
diff --git a/configure.in b/configure.in
deleted file mode 100644
index e76decf..0000000
--- a/configure.in
+++ /dev/null
@@ -1,141 +0,0 @@
-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 4e20ad3..a7fd057 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -4,15 +4,19 @@
SRCROOT = ..
--include ../MCONFIG
+-include ../config/MCONFIG
include ../MRULES
+ifeq ($(LIBOBJS),)
+all:
+else
all: libxtra.a
+endif
install:
clean:
- -rm -f *.a *.o
+ -rm -f *.a *.o *.obj *.exe
distclean: clean
-rm -f *~
@@ -21,5 +25,3 @@ libxtra.a: $(LIBOBJS)
-rm -f libxtra.a
$(AR) libxtra.a $(LIBOBJS)
$(RANLIB) libxtra.a
-
-
diff --git a/lib/bsdsignal.c b/lib/bsdsignal.c
deleted file mode 100644
index bc3ceb5..0000000
--- a/lib/bsdsignal.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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
new file mode 100644
index 0000000..0eb39c9
--- /dev/null
+++ b/lib/daemon.c
@@ -0,0 +1,36 @@
+/*
+ * 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
new file mode 100644
index 0000000..bba45c4
--- /dev/null
+++ b/lib/dup2.c
@@ -0,0 +1,23 @@
+/*
+ * 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
new file mode 100644
index 0000000..ef7c9ae
--- /dev/null
+++ b/lib/getaddrinfo.c
@@ -0,0 +1,121 @@
+/*
+ * 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
new file mode 100644
index 0000000..c1ad561
--- /dev/null
+++ b/lib/getopt.h
@@ -0,0 +1,23 @@
+#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
new file mode 100644
index 0000000..49d1274
--- /dev/null
+++ b/lib/getopt_long.c
@@ -0,0 +1,150 @@
+/*
+ * 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
new file mode 100644
index 0000000..fe8e560
--- /dev/null
+++ b/lib/inet_ntop.c
@@ -0,0 +1,52 @@
+/*
+ * 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 6e8ae24..30704f3 100644
--- a/lib/xmalloc.c
+++ b/lib/xmalloc.c
@@ -5,17 +5,16 @@
*
*/
-#include
-#include
+#include "config.h"
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 5d65b7e..05e3054 100644
--- a/lib/xstrdup.c
+++ b/lib/xstrdup.c
@@ -5,18 +5,16 @@
*
*/
-#include
-#include
-#include
+#include "config.h"
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
new file mode 100755
index 0000000..1f72922
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,57 @@
+#!/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
new file mode 100644
index 0000000..982fe09
--- /dev/null
+++ b/tftp-xinetd
@@ -0,0 +1,18 @@
+# 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
new file mode 100644
index 0000000..cd5e4cc
--- /dev/null
+++ b/tftp.spec.in
@@ -0,0 +1,228 @@
+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 ca91196..9faa62f 100644
--- a/tftp/Makefile
+++ b/tftp/Makefile
@@ -1,24 +1,28 @@
-all: tftp
-
SRCROOT = ..
+VERSION = $(shell cat ../version)
--include ../MCONFIG
+-include ../config/MCONFIG
include ../MRULES
-OBJS = tftp.o main.o tftpsubs.o
+OBJS = tftp.$(O) main.$(O)
-tftp: $(OBJS)
- $(CC) $(LDFLAGS) $^ $(LIBS) -o $@
+all: tftp$(X) tftp.1
-$(OBJS): tftpsubs.h
+tftp$(X): $(OBJS)
+ $(CC) $(LDFLAGS) $^ $(TFTP_LIBS) -o $@
-install: tftp
+$(OBJS): ../common/tftpsubs.h
+
+tftp.1: tftp.1.in ../version
+ sed -e 's/@@VERSION@@/$(VERSION)/g' < $< > $@
+
+install: all
mkdir -p $(INSTALLROOT)$(BINDIR) $(INSTALLROOT)$(MANDIR)/man1
- $(INSTALL_PROGRAM) -s tftp $(INSTALLROOT)$(BINDIR)
- $(INSTALL_DATA) tftp.1 $(INSTALLROOT)$(MANDIR)/man1
+ $(INSTALL_PROGRAM) tftp$(X) $(INSTALLROOT)$(BINDIR)
+ $(INSTALL_DATA) tftp.1 $(INSTALLROOT)$(MANDIR)/man1
clean:
- rm -f *.o tftp
+ rm -f *.o *.obj *.exe tftp tftp.1
distclean: clean
rm -f *~
diff --git a/tftp/extern.h b/tftp/extern.h
index 60ad0e9..9c578e6 100644
--- a/tftp/extern.h
+++ b/tftp/extern.h
@@ -1,8 +1,3 @@
-/* $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.
@@ -34,14 +29,15 @@
* 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 RECVFILE_H
-#define RECVFILE_H
+#ifndef EXTERN_H
+#define EXTERN_H
-void tftp_recvfile (int, char *, char *);
-void tftp_sendfile (int, char *, char *);
+#include "config.h"
+
+void tftp_recvfile(int, const char *, const char *);
+void tftp_sendfile(int, const char *, const char *);
+extern sigjmp_buf toplevel;
#endif
diff --git a/tftp/main.c b/tftp/main.c
index 7ff7d2e..ecdc0e0 100644
--- a/tftp/main.c
+++ b/tftp/main.c
@@ -1,6 +1,3 @@
-/* $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.
@@ -34,745 +31,916 @@
* SUCH DAMAGE.
*/
-#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 */
+#include "common/tftpsubs.h"
/* 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 sockaddr_in peeraddr;
-int f;
-short port;
-int trace;
-int verbose;
-int connected;
-char mode[32];
-#ifdef WITH_READLINE
-char *line = NULL;
+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
-char line[LBUFLEN];
+int ai_fam = AF_INET;
+int ai_fam_sock = AF_INET;
#endif
-int margc;
-char *margv[20];
+
+union sock_addr peeraddr;
+int f = -1;
+u_short port;
+int trace;
+int verbose;
+int literal;
+int connected;
+const struct modes *mode;
+#ifdef WITH_READLINE
+char *line = NULL;
+#else
+char line[LBUFLEN];
+#endif
+int margc;
+char *margv[20];
const char *prompt = "tftp> ";
-sigjmp_buf toplevel;
-void intr(int);
-struct servent *sp;
+sigjmp_buf toplevel;
+void intr(int);
+struct servent *sp;
+int portrange = 0;
+unsigned int portrange_from = 0;
+unsigned int portrange_to = 0;
-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 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 **);
-static void command (void);
+static void command(void);
-static void getusage (char *);
-static void makeargv (void);
-static void putusage (char *);
-static void settftpmode (const char *);
+static void getusage(char *);
+static void makeargv(void);
+static void putusage(char *);
+static void settftpmode(const struct modes *);
#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 },
- { "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},
+ {"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}
};
-struct cmd *getcmd(char *);
-char *tail(char *);
+struct cmd *getcmd(char *);
+char *tail(char *);
char *xstrdup(const char *);
-int
-main(int argc, char *argv[])
+const char *program;
+
+static void usage(int errcode)
{
- struct sockaddr_in s_in;
-
- 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');
-
-#ifdef WITH_READLINE
- using_history();
+ 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
-
- command();
-
- return 0; /* Never reached */
+ program);
+ exit(errcode);
}
-char *hostname;
+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];
+
+ 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
+#endif
+
+ if (sigsetjmp(toplevel, 1) != 0)
+ (void)putchar('\n');
+ command();
+
+ return 0; /* Never reached */
+}
+
+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;
-
- len = strlen(partial);
- eline = readline(mprompt);
- if (!eline)
- exit(0); /* EOF */
-
- elen = strlen(eline);
+ char *eline;
+ int len, elen;
- if (line)
- free(line);
- line = xmalloc(len+elen+1);
- strcpy(line, partial);
- strcpy(line+len, eline);
+ len = strlen(partial);
+ eline = readline(mprompt);
+ if (!eline)
+ exit(0); /* EOF */
- add_history(line);
+ 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
#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[])
{
- struct hostent *host;
+ int err;
- 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;
+ 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;
}
-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[])
+void modecmd(int argc, char *argv[])
{
- struct modes *p;
- const char *sep;
+ const struct modes *p;
+ const char *sep;
- 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 */
- }
+ 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 */
+ }
- 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("octet");
-}
-
-void
-setascii(int argc, char *argv[])
+void setbinary(int argc, char *argv[])
{
- (void)argc; (void)argv; /* Quiet unused warning */
- settftpmode("netascii");
+ (void)argc;
+ (void)argv; /* Quiet unused warning */
+ settftpmode(MODE_OCTET);
}
-static void
-settftpmode(const char *newmode)
+void setascii(int argc, char *argv[])
{
- strcpy(mode, newmode);
- if (verbose)
- printf("mode set to %s\n", mode);
+ (void)argc;
+ (void)argv; /* Quiet unused warning */
+ settftpmode(MODE_NETASCII);
}
+static void settftpmode(const struct modes *newmode)
+{
+ mode = newmode;
+ if (verbose)
+ printf("mode set to %s\n", mode->m_mode);
+}
/*
* Send file(s).
*/
-void
-put(int argc, char *argv[])
+void put(int argc, char *argv[])
{
- int fd;
- int n;
- char *cp, *targ;
+ int fd;
+ int n, err;
+ 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 (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);
- }
+ 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);
+ }
}
-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 (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;
+ 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;
- *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);
- }
+ *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);
+ }
}
-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
-status(int argc, char *argv[])
+void setliteral(int argc, char *argv[])
{
- (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)argc;
+ (void)argv; /* Quiet unused warning */
+ literal = !literal;
+ printf("Literal mode %s.\n", literal ? "on" : "off");
}
-void
-intr(int sig)
+void status(int argc, char *argv[])
{
- (void)sig; /* Quiet unused warning */
-
- bsd_signal(SIGALRM, SIG_IGN);
- alarm(0);
- siglongjmp(toplevel, -1);
+ (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);
}
-char *
-tail(char *filename)
+void intr(int sig)
{
- char *s;
-
- while (*filename) {
- s = strrchr(filename, '/');
- if (s == NULL)
- break;
- if (s[1])
- return (s + 1);
- *s = '\0';
- }
- return (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);
}
/*
* 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 = readline(prompt);
- if ( !line )
- exit(0); /* EOF */
+ if (line) {
+ free(line);
+ line = NULL;
+ }
+ line = readline(prompt);
+ if (!line)
+ exit(0); /* EOF */
#else
- fputs(prompt, stdout);
- if (fgets(line, LBUFLEN, stdin) == 0) {
- if (feof(stdin)) {
- exit(0);
- } else {
- continue;
- }
- }
+ fputs(prompt, stdout);
+ fflush(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
- add_history(line);
+#ifdef HAVE_READLINE_HISTORY_H
+ add_history(line);
#endif
- makeargv();
- if (margc == 0)
- continue;
+#endif
+ 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;
- 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);
- }
+ 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);
+ }
}
-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
deleted file mode 100644
index 41685c8..0000000
--- a/tftp/tftp.1
+++ /dev/null
@@ -1,177 +0,0 @@
-.\" $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
new file mode 100644
index 0000000..b41f7b5
--- /dev/null
+++ b/tftp/tftp.1.in
@@ -0,0 +1,207 @@
+.\" -*- 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 fe8a88d..9c9c5af 100644
--- a/tftp/tftp.c
+++ b/tftp/tftp.c
@@ -1,8 +1,3 @@
-/* $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.
@@ -36,58 +31,26 @@
* SUCH DAMAGE.
*/
-#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 */
+#include "common/tftpsubs.h"
/*
* TFTP User Program -- Protocol Machines
*/
-#include
-#include
-#include
-
-#include
-
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-
#include "extern.h"
-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
+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;
#define PKTSIZE SEGSIZE+4
-char ackbuf[PKTSIZE];
-int timeout;
-sigjmp_buf toplevel;
-sigjmp_buf timeoutbuf;
+char ackbuf[PKTSIZE];
+int timeout;
+static sigjmp_buf timeoutbuf;
-static void nak(int);
+static void nak(int, const char *);
static int makerequest(int, const char *, struct tftphdr *, const char *);
static void printstats(const char *, unsigned long);
static void startclock(void);
@@ -98,363 +61,365 @@ static void tpacket(const char *, struct tftphdr *, int);
/*
* Send the requested file.
*/
-void
-tftp_sendfile(int fd, char *name, char *mode)
+void tftp_sendfile(int fd, const char *name, const 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;
- struct sockaddr_in from;
- int fromlen;
- FILE *file;
+ 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;
- 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;
+ 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;
- 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;
+ 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);
- 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);
+ 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);
}
/*
* Receive a file.
*/
-void
-tftp_recvfile(int fd, char *name, char *mode)
+void tftp_recvfile(int fd, const char *name, const char *mode)
{
- 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 */
+ 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;
- startclock();
- dp = w_init();
- ap = (struct tftphdr *)ackbuf;
- file = fdopen(fd, "w");
- convert = !strcmp(mode, "netascii");
- block = 1;
- firsttrip = 1;
- amount = 0;
+ startclock();
+ dp = w_init();
+ ap = (struct tftphdr *)ackbuf;
+ convert = !strcmp(mode, "netascii");
+ file = fdopen(fd, convert ? "wt" : "wb");
+ block = 1;
+ firsttrip = 1;
+ amount = 0;
- 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;
+ 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;
- 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);
+ 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);
}
static int
makerequest(int request, const char *name,
- struct tftphdr *tp, const char *mode)
+ struct tftphdr *tp, const char *mode)
{
- char *cp;
+ char *cp;
+ size_t len;
- 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);
+ 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);
}
-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 }
+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 */
};
+#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)
+static void nak(int error, const char *msg)
{
- struct errmsg *pe;
- struct tftphdr *tp;
- int length;
+ struct tftphdr *tp;
+ int length;
- 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");
+ 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");
}
-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(tp->th_opcode);
+ static const char *opcodes[] =
+ { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
+ char *cp, *file;
+ u_short op = ntohs((u_short) 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;
- /* 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');
+ 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');
+ }
}
-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
deleted file mode 100644
index c358ccd..0000000
--- a/tftp/tftpsubs.c
+++ /dev/null
@@ -1,282 +0,0 @@
-/* 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/tftpd/Makefile b/tftpd/Makefile
index f538c97..5963581 100644
--- a/tftpd/Makefile
+++ b/tftpd/Makefile
@@ -1,30 +1,29 @@
-all: tftpd
-
SRCROOT = ..
+VERSION = $(shell cat ../version)
--include ../MCONFIG
+-include ../config/MCONFIG
include ../MRULES
-OBJS = tftpd.o tftpsubs.o recvfrom.o misc.o $(TFTPDOBJS)
+OBJS = tftpd.$(O) recvfrom.$(O) misc.$(O) $(TFTPDOBJS)
-tftpd: $(OBJS)
- $(CC) $(LDFLAGS) $^ $(LIBS) -o $@
+all: tftpd$(X) tftpd.8
-tftpsubs.c:
- ln -sf ../tftp/tftpsubs.c .
-tftpsubs.h:
- ln -sf ../tftp/tftpsubs.h .
+tftpd$(X): $(OBJS)
+ $(CC) $(LDFLAGS) $^ $(TFTPD_LIBS) -o $@
-$(OBJS): tftpsubs.h
+$(OBJS): ../common/tftpsubs.h
-install: tftpd
+tftpd.8: tftpd.8.in ../version
+ sed -e 's/@@VERSION@@/$(VERSION)/g' < $< > $@
+
+install: all
mkdir -p $(INSTALLROOT)$(SBINDIR) $(INSTALLROOT)$(MANDIR)/man8
- $(INSTALL_PROGRAM) tftpd $(INSTALLROOT)$(SBINDIR)/in.tftpd
+ $(INSTALL_PROGRAM) tftpd$(X) $(INSTALLROOT)$(SBINDIR)/in.tftpd
$(INSTALL_DATA) tftpd.8 $(INSTALLROOT)$(MANDIR)/man8/in.tftpd.8
- ln -sf in.tftpd.8 $(INSTALLROOT)$(MANDIR)/man8/tftpd.8
+ cd $(INSTALLROOT)$(MANDIR)/man8 && $(LN_S) -f in.tftpd.8 tftpd.8
clean:
- rm -f *.o tftpd tftpsubs.c tftpsubs.h
+ rm -f *.o *.obj *.exe tftpd tftpsubs.c tftpsubs.h tftpd.8
distclean: clean
rm -f *~
diff --git a/tftpd/misc.c b/tftpd/misc.c
index 62f2c07..9070f96 100644
--- a/tftpd/misc.c
+++ b/tftpd/misc.c
@@ -1,7 +1,6 @@
-/* $Id$ */
/* ----------------------------------------------------------------------- *
- *
- * Copyright 2001 H. Peter Anvin - All Rights Reserved
+ *
+ * Copyright 2001-2007 H. Peter Anvin - All Rights Reserved
*
* This program is free software available under the same license
* as the "OpenBSD" operating system, distributed at
@@ -15,28 +14,19 @@
* 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. Basically a user-friendly
- * wrapper around sigaction().
+ * Set the signal handler and flags, and error out on failure.
*/
-void set_signal(int signum, void (*handler)(int), int flags)
+void set_signal(int signum, sighandler_t handler, int flags)
{
- struct sigaction sa;
-
- memset(&sa, 0, sizeof sa);
- sa.sa_handler = handler;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = flags;
-
- if ( sigaction(signum, &sa, NULL) ) {
- syslog(LOG_ERR, "sigaction: %m");
- exit(EX_OSERR);
- }
+ if (tftp_signal(signum, handler, flags)) {
+ syslog(LOG_ERR, "sigaction: %m");
+ exit(EX_OSERR);
+ }
}
/*
@@ -44,14 +34,14 @@ void set_signal(int signum, void (*handler)(int), 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;
}
/*
@@ -59,13 +49,12 @@ 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 d094a69..320678d 100644
--- a/tftpd/recvfrom.c
+++ b/tftpd/recvfrom.c
@@ -1,7 +1,6 @@
-/* $Id$ */
/* ----------------------------------------------------------------------- *
- *
- * Copyright 2001 H. Peter Anvin - All Rights Reserved
+ *
+ * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved
*
* This program is free software available under the same license
* as the "OpenBSD" operating system, distributed at
@@ -18,28 +17,30 @@
*
*/
-#include "config.h" /* Must be included first! */
+#include "config.h" /* Must be included first! */
+#include "common/tftpsubs.h"
#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)
-#include
+#ifdef HAVE_SYS_UIO_H
+# include
+#endif
#ifdef IP_PKTINFO
-# ifndef HAVE_STRUCT_IN_PKTINFO
+# ifndef HAVE_STRUCT_IN_PKTINFO_IPI_ADDR
# ifdef __linux__
/* Assume this version of glibc simply lacks the definition */
struct in_pktinfo {
- 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
@@ -51,102 +52,246 @@ struct in_pktinfo {
# define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size))
#endif
-int
-myrecvfrom(int s, void *buf, int len, unsigned int flags,
- struct sockaddr *from, int *fromlen,
- struct sockaddr_in *myaddr)
+/*
+ * 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)
{
- 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
- char control[CMSG_SPACE(sizeof(struct in_addr))];
-#endif
- } control_un;
- int on = 1;
-#ifdef IP_PKTINFO
- struct in_pktinfo pktinfo;
-#endif
+ union sock_addr sa1, sa2;
+ int sockfd = -1;
+ int e;
+ int rv = 0;
+ socklen_t addrlen;
- /* 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
+ 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;
- return n;
+ 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;
}
-#else /* pointless... */
+static void normalize_ip6_compat(union sock_addr *myaddr)
+{
+#ifdef HAVE_IPV6
+ static const uint8_t ip6_compat_prefix[12] =
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
+ struct sockaddr_in in;
+
+ if (myaddr->sa.sa_family == AF_INET6 &&
+ !memcmp(&myaddr->s6.sin6_addr, ip6_compat_prefix,
+ sizeof ip6_compat_prefix)) {
+ bzero(&in, sizeof in);
+ in.sin_family = AF_INET;
+ in.sin_port = myaddr->s6.sin6_port;
+ memcpy(&in.sin_addr, (const char *)&myaddr->s6.sin6_addr +
+ sizeof ip6_compat_prefix, sizeof in.sin_addr);
+ memcpy(&myaddr->si, &in, sizeof in);
+ }
+#else
+ (void)myaddr;
+#endif
+}
int
myrecvfrom(int s, void *buf, int len, unsigned int flags,
- struct sockaddr *from, int *fromlen,
- struct sockaddr_in *myaddr)
+ union sock_addr *from, union sock_addr *myaddr)
{
- /* There is no way we can get the local address, fudge it */
+ 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
- bzero(myaddr, sizeof(struct sockaddr_in));
- myaddr->sin_family = AF_INET;
+ /* 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;
- myaddr->sin_port = htons(IPPORT_TFTP);
- bzero(&myaddr->sin_addr, sizeof(myaddr->sin_addr));
+ 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;
- return recvfrom(s,buf,len,flags,from,fromlen);
+ 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);
}
#endif
diff --git a/tftpd/recvfrom.h b/tftpd/recvfrom.h
index 1de145e..7773a0d 100644
--- a/tftpd/recvfrom.h
+++ b/tftpd/recvfrom.h
@@ -1,7 +1,6 @@
-/* $Id$ */
/* ----------------------------------------------------------------------- *
- *
- * Copyright 2001 H. Peter Anvin - All Rights Reserved
+ *
+ * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved
*
* This program is free software available under the same license
* as the "OpenBSD" operating system, distributed at
@@ -16,14 +15,8 @@
*
*/
-#define _XPG4_2 /* Needed on Solaris */
-#include
-#include
-#include
-#include
-#include
+#include "config.h"
int
myrecvfrom(int s, void *buf, int len, unsigned int flags,
- struct sockaddr *from, int *fromlen,
- struct sockaddr_in *myaddr);
+ union sock_addr *from, union sock_addr *myaddr);
diff --git a/tftpd/remap.c b/tftpd/remap.c
index ee93c98..413d117 100644
--- a/tftpd/remap.c
+++ b/tftpd/remap.c
@@ -1,7 +1,6 @@
-/* $Id$ */
/* ----------------------------------------------------------------------- *
- *
- * Copyright 2001 H. Peter Anvin - All Rights Reserved
+ *
+ * Copyright 2001-2024 H. Peter Anvin - All Rights Reserved
*
* This program is free software available under the same license
* as the "OpenBSD" operating system, distributed at
@@ -15,7 +14,7 @@
* Perform regular-expression based filename remapping.
*/
-#include "config.h" /* Must be included first! */
+#include "config.h" /* Must be included first! */
#include
#include
#include
@@ -23,317 +22,524 @@
#include "tftpd.h"
#include "remap.h"
-#define DEADMAN_MAX_STEPS 1024 /* Timeout after this many steps */
-#define MAXLINE 16384 /* Truncate a line at this many bytes */
+#define DEADMAN_MAX_STEPS 4096 /* Timeout after this many steps */
+#define MAXLINE 16384 /* Truncate a line at this many bytes */
-#define RULE_REWRITE 0x01 /* This is a rewrite rule */
-#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 */
+#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;
struct rule {
- struct rule *next;
- int nrule;
- int rule_flags;
- regex_t rx;
- const char *pattern;
+ struct rule *next;
+ int nrule;
+ unsigned int rule_flags;
+ regex_t rx;
+ const char *pattern;
};
-/* 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)
+static int xform_null(int c)
{
- int len = 0;
- int n, mlen;
- int endbytes;
-
- /* 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;
- }
-
- /* 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++;
- }
- }
-
- /* Copy section after match */
- if ( string ) {
- memcpy(string, input+pmatch[0].rm_eo, endbytes);
- string[endbytes] = '\0';
- }
-
- return len;
+ return c;
}
-/* Extract a string terminated by non-escaped whitespace; ignore leading whitespace */
-/* Consider an unescaped # to be a comment marker, functionally \n */
+static int xform_toupper(int c)
+{
+ return toupper(c);
+}
+
+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) {
+ mlen = 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;
+}
+
+/*
+ * 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.
+ */
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++;
+ 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++;
+ }
- 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++;
- }
-
- *buf = '\0';
- *str = p;
- return len;
+ 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':
- 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;
+ 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;
+ }
}
- }
- /* RULE_GLOBAL only applies when RULE_REWRITE specified */
- if ( !(r->rule_flags & RULE_REWRITE) )
- r->rule_flags &= ~RULE_GLOBAL;
+ if (r->rule_flags & RULE_REWRITE) {
+ if (r->rule_flags & RULE_INVERSE) {
+ syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n",
+ lineno, line);
+ return -1; /* Error */
+ }
+ if ((r->rule_flags & (RULE_GLOBAL|RULE_SEDG|RULE_HASFILE))
+ == (RULE_GLOBAL|RULE_HASFILE)) {
+ syslog(LOG_ERR, "E rules cannot be combined with g (but gg is OK), line %d: %s\n",
+ lineno, line);
+ return -1; /* Error */
+ }
+ } else {
+ /* RULE_GLOBAL and RULE_SEDG are meaningless without RULE_REWRITE */
+ r->rule_flags &= ~(RULE_GLOBAL|RULE_SEDG);
+ }
- /* Read and compile the regex */
- 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);
-
- free(r);
+ /* "" patterns aren't allocated by malloc() */
+ if (r->pattern && *r->pattern)
+ free((void *)r->pattern);
- r = next;
- }
+ free(r);
+
+ r = next;
+ }
}
/* Execute a rule set on a string; returns a malloc'd new string. */
-char *rewrite_string(const char *input, const struct rule *rules, int is_put)
+char *rewrite_string(const struct formats *pf,
+ const char *input, const struct rule *rules,
+ int mode, int af, match_pattern_callback macrosub,
+ const char **errmsg)
{
- char *current = tfstrdup(input);
- char *newstr;
- const struct rule *ruleptr = rules;
- regmatch_t pmatch[10];
- int len;
- int was_match = 0;
- int deadman = DEADMAN_MAX_STEPS;
+ 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;
- if ( verbosity >= 3 ) {
- syslog(LOG_INFO, "remap: input: %s", current);
- }
+ /* Default error */
+ *errmsg = "Remap table failure";
- 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 */
+ if (verbosity >= 3) {
+ syslog(LOG_INFO, "remap: input: %s", current);
}
- if ( ! deadman-- ) {
- syslog(LOG_WARNING, "remap: Breaking loop, input = %s, last = %s",
- input, current);
- free(current);
- return NULL; /* Did not terminate! */
+ 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);
+ }
+ }
}
- 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;
-
- 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;
- if ( verbosity >= 3 ) {
- syslog(LOG_INFO, "remap: done");
- }
- return current;
+dead: /* Deadman expired */
+ syslog(LOG_ERR,
+ "remap: Breaking loop after %d steps, input = %s, last = %s",
+ deadman_max_steps, input, current);
+ free(current);
+ return NULL; /* Did not terminate! */
}
diff --git a/tftpd/remap.h b/tftpd/remap.h
index a562df5..c8d76ff 100644
--- a/tftpd/remap.h
+++ b/tftpd/remap.h
@@ -1,7 +1,6 @@
-/* $Id$ */
/* ----------------------------------------------------------------------- *
- *
- * Copyright 2001 H. Peter Anvin - All Rights Reserved
+ *
+ * Copyright 2001-2007 H. Peter Anvin - All Rights Reserved
*
* This program is free software available under the same license
* as the "OpenBSD" operating system, distributed at
@@ -18,15 +17,17 @@
#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 *);
@@ -34,8 +35,13 @@ struct rule *parserulefile(FILE *);
void freerules(struct rule *);
/* Execute a rule set on a string; returns a malloc'd new string. */
-char *rewrite_string(const char *, const struct rule *, int);
+struct formats;
+char *rewrite_string(const struct formats *, const char *,
+ const struct rule *, int, int,
+ match_pattern_callback, const char **);
-#endif /* WITH_REGEX */
-#endif /* TFTPD_REMAP_H */
+/* Remapping deadman counter */
+extern int deadman_max_steps;
+#endif /* WITH_REGEX */
+#endif /* TFTPD_REMAP_H */
diff --git a/tftpd/sample.rules b/tftpd/sample.rules
index 7b59e70..55b56be 100644
--- a/tftpd/sample.rules
+++ b/tftpd/sample.rules
@@ -1,4 +1,3 @@
-# $Id$
#
# Sample rule file for the -m (remapping option)
#
diff --git a/tftpd/tftpd.8 b/tftpd/tftpd.8
deleted file mode 100644
index 505bacf..0000000
--- a/tftpd/tftpd.8
+++ /dev/null
@@ -1,262 +0,0 @@
-.\" 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
new file mode 100644
index 0000000..3d43325
--- /dev/null
+++ b/tftpd/tftpd.8.in
@@ -0,0 +1,457 @@
+.\" -*- 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 c019591..e0041dc 100644
--- a/tftpd/tftpd.c
+++ b/tftpd/tftpd.c
@@ -1,9 +1,7 @@
-/* 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
@@ -35,616 +33,1173 @@
* 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
-#include
-#include
-#include
#include
-#include
-#include
#include
-#include
#include
+#include
-#include "tftpsubs.h"
+#include "common/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 */
-struct request_info wrap_request;
+static struct request_info wrap_request;
#endif
-#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
+#ifdef HAVE_IPV6
+static int ai_fam = AF_UNSPEC;
+#else
+static int ai_fam = AF_INET;
#endif
-char *__progname;
-int peer;
-int timeout = TIMEOUT;
-int rexmtval = TIMEOUT;
-int maxtimeout = TIMEOUT_LIMIT*TIMEOUT;
+#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;
#define PKTSIZE MAX_SEGSIZE+4
-char buf[PKTSIZE];
-char ackbuf[PKTSIZE];
-struct sockaddr_in from;
-int fromlen;
-off_t tsize;
-int tsize_ok;
+static char buf[PKTSIZE];
+static char ackbuf[PKTSIZE];
+static unsigned int max_blksize = MAX_SEGSIZE;
-int ndirs;
-const char **dirs;
+static char tmpbuf[INET6_ADDRSTRLEN], *tmp_p;
-int secure = 0;
-int cancreate = 0;
+static union sock_addr from;
+static off_t tsize;
+static int tsize_ok;
+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
-int tftp(struct tftphdr *, int);
-void nak(int);
-void timer(int);
-void justquit(int);
-void do_opt(char *, char *, char **);
+static FILE *file;
-int set_blksize(char *, char **);
-int set_blksize2(char *, char **);
-int set_tsize(char *, char **);
-int set_timeout(char *, char **);
+int tftp(struct tftphdr *, int);
+static void nak(int, const char *);
+static void timer(int);
+static void do_opt(const char *, const 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 *);
struct options {
- const char *o_opt;
- int (*o_fnc)(char *, char **);
+ const char *o_opt;
+ int (*o_fnc)(uintmax_t *);
} options[] = {
- { "blksize", set_blksize },
- { "blksize2", set_blksize2 },
- { "tsize", set_tsize },
- { "timeout", set_timeout },
- { NULL, NULL }
+ {"blksize", set_blksize},
+ {"blksize2", set_blksize2},
+ {"tsize", set_tsize},
+ {"timeout", set_timeout},
+ {"utimeout", set_utimeout},
+ {"rollover", set_rollover},
+ {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;
}
-
-static void
-usage(void)
+/* Handle exit requests by SIGTERM and SIGINT */
+static volatile sig_atomic_t exit_signal = 0;
+static void handle_exit(int sig)
{
- syslog(LOG_ERR, "Usage: %s [-vcl][-a address][-m mappings][-u user][-t timeout][-r option...] [-s] [directory ...]",
- __progname);
- exit(EX_USAGE);
+ exit_signal = sig;
}
+/* 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 *file)
+static struct rule *read_remap_rules(const char *rulefile)
{
- FILE *f;
- struct rule *rulep;
+ FILE *f;
+ struct rule *rulep;
- f = fopen(file, "rt");
- if ( !f ) {
- syslog(LOG_ERR, "Cannot open map file: %s: %m", file);
- exit(EX_NOINPUT);
- }
- rulep = parserulefile(f);
- fclose(f);
+ f = fopen(rulefile, "rt");
+ if (!f) {
+ syslog(LOG_ERR, "Cannot open map file: %s: %m", rulefile);
+ exit(EX_NOINPUT);
+ }
+ rulep = parserulefile(f);
+ fclose(f);
- return rulep;
+ return rulep;
}
#endif
-int
-main(int argc, char **argv)
+/*
+ * Rules for locking files; return 0 on success, -1 on failure
+ */
+static int lock_file(int fd, int lock_write)
{
- 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;
+ (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
+}
- /* basename() is way too much of a pain from a portability standpoint */
-
- 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 ( !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;
+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
- case 'v':
- verbosity++;
- break;
- default:
- usage();
- break;
+ if (err) {
+ syslog(LOG_ERR, "Cannot set nonblock flag on socket: %m");
+ exit(EX_OSERR);
}
+}
- dirs = xmalloc((argc-optind+1)*sizeof(char *));
- for ( ndirs = 0 ; optind != argc ; optind++ )
- dirs[ndirs++] = argv[optind];
+static void pmtu_discovery_off(int fd)
+{
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+ int pmtu = IP_PMTUDISC_DONT;
- 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);
- }
-
-#ifdef WITH_REGEX
- if ( rewrite_file )
- rewrite_rules = read_remap_rules(rewrite_file);
+ setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
#endif
+}
- /* 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);
+/*
+ * 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;
- if ( address ) {
- char *portptr, *eportptr;
- struct hostent *hostent;
- struct servent *servent;
- unsigned long port;
+ gettimeofday(&t0, NULL);
+ timeout_left = timeout_us;
- 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);
- }
- }
+ 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;
+ }
}
+}
- if (bind(fd, (struct sockaddr *)&bindaddr, sizeof bindaddr) < 0) {
- syslog(LOG_ERR, "cannot bind to local socket: %m");
- exit(EX_OSERR);
- }
+static int split_port(char **ap, char **pp)
+{
+ char *a, *p;
+ int ret = AF_UNSPEC;
- /* Daemonize this process */
+ 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
{
- 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
+ struct in_addr in;
+
+ p = strrchr(a, ':');
+ if (p)
+ *p++ = 0;
+ if (inet_aton(a, &in))
+ ret = AF_INET;
}
- } 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
- 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);
-
- 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);
-
- 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);
- }
-
- 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, __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);
-
- /* 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);
- }
-
- /* 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);
-#endif
-
-#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));
-#endif
-
- 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);
+ *pp = p;
+ return ret;
}
-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);
+enum long_only_options {
+ OPT_VERBOSITY = 256,
+ OPT_MAP_STEPS
+};
-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 }
+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;
+#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);
+#endif
+
+ if (pidfile && !standalone) {
+ syslog(LOG_WARNING, "not in standalone mode, ignoring pid file");
+ pidfile = NULL;
+ }
+
+ /* 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);
+ }
+ }
+
+ if (caught_sighup) {
+ caught_sighup = 0;
+ if (standalone) {
+#ifdef WITH_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);
+ 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;
+
+
+ /* 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);
+#endif
+
+ n = myrecvfrom(fd, buf, sizeof(buf), 0, &from, &myaddr);
+
+ 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");
+#else
+ if (from.sa.sa_family != AF_INET) {
+ syslog(LOG_ERR, "received address was not AF_INET,"
+ " please check your inetd config");
+#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));
+#endif
+ }
+ }
+
+ /*
+ * 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);
+
+ /* 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);
+ }
+
+#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);
+ }
+#endif
+
+ /* 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(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);
+#else
+ setrv = setegid(pw->pw_gid) || setgid(pw->pw_gid);
+#endif
+ if (setrv && errno == EPERM) {
+ setrv = 0; /* Assume already restricted by system policy */
+ }
+
+#ifdef HAVE_SETRESUID
+ setrv = setrv || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid);
+#elif defined(HAVE_SETREUID)
+ setrv = setrv || setreuid(pw->pw_uid, pw->pw_uid);
+#else
+ /* Important: setuid() must come first */
+ setrv = setrv || setuid(pw->pw_uid) ||
+ (geteuid() != pw->pw_uid && seteuid(pw->pw_uid));
+#endif
+ if (setrv && errno == EPERM) {
+ setrv = 0; /* Assume already restricted by system policy */
+ }
+
+ if (setrv) {
+ syslog(LOG_ERR, "cannot drop privileges: %m");
+ 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);
+}
+
+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);
+
+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}
};
/*
* Handle initial connection protocol.
*/
-int
-tftp(struct tftphdr *tp, int size)
+int tftp(struct tftphdr *tp, int size)
{
- 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);
+ 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;
+ }
}
-
- 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;
+
+ 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);
} else {
- do_opt(opt, val, &ap);
- opt = ++cp;
+ if (tp_opcode == WRQ)
+ (*pf->f_recv) (pf, NULL, 0);
+ else
+ (*pf->f_send) (pf, NULL, 0);
}
- }
-
- 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 */
+ exit(0); /* Request completed */
}
static int blksize_set;
@@ -652,66 +1207,65 @@ static int blksize_set;
/*
* Set a non-standard block size (c.f. RFC2348)
*/
-int
-set_blksize(char *val, char **ret)
+static int set_blksize(uintmax_t *vp)
{
- 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);
+ 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;
}
/*
* Set a power-of-two block size (nonstandard)
*/
-int
-set_blksize2(char *val, char **ret)
+static int set_blksize2(uintmax_t *vp)
{
- 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);
+ 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;
}
/*
@@ -719,448 +1273,560 @@ set_blksize2(char *val, char **ret)
* For netascii mode, we don't know the size ahead of time;
* so reject the option.
*/
-int
-set_tsize(char *val, char **ret)
+static int set_tsize(uintmax_t *vp)
{
- static char b_ret[sizeof(uintmax_t)*CHAR_BIT/3+2];
- uintmax_t sz;
- char *vend;
+ uintmax_t sz = *vp;
- sz = strtoumax(val, &vend, 10);
-
- if ( !tsize_ok || *vend )
- return 0;
-
- if (sz == 0)
- sz = (uintmax_t)tsize;
+ if (!tsize_ok)
+ return 0;
- sprintf(*ret = b_ret, "%"PRIuMAX, sz);
- return(1);
+ if (sz == 0)
+ sz = tsize;
+
+ *vp = sz;
+ return 1;
}
/*
- * Set the timeout (c.f. RFC2349)
+ * 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.
*/
-int
-set_timeout(char *val, char **ret)
+static int set_timeout(uintmax_t *vp)
{
- static char b_ret[4];
- unsigned long to;
- char *vend;
+ uintmax_t to = *vp;
- to = strtoul(val, &vend, 10);
+ if (to < 1 || to > 255)
+ return 0;
- if ( to < 1 || to > 255 || *vend )
- return 0;
-
- timeout = to;
- rexmtval = to;
- maxtimeout = TIMEOUT_LIMIT*to;
-
- sprintf(*ret = b_ret, "%lu", to);
- return(1);
+ 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;
}
/*
- * Parse RFC2347 style options
+ * Conservative calculation for the size of a buffer which can hold an
+ * arbitrary integer
*/
-void
-do_opt(char *opt, char *val, char **ap)
+#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)
{
- 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;
+ 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;
}
- return;
}
/*
* Modify the filename, if applicable. If it returns NULL, deny the access.
*/
-char *
-rewrite_access(char *filename, int mode)
+static char *rewrite_access(const struct formats *pf, char *filename,
+ int mode, int af, const char **msg)
{
-#ifdef WITH_REGEX
- if ( rewrite_rules ) {
- char *newname = rewrite_string(filename, rewrite_rules, mode != RRQ);
- filename = newname;
- }
-#else
- (void)mode; /* Suppress unused warning */
-#endif
- return filename;
+ if (rewrite_rules) {
+ char *newname =
+ rewrite_string(pf, filename, rewrite_rules, mode, af,
+ rewrite_macros, msg);
+ filename = newname;
+ }
+ return filename;
}
-FILE *file;
+#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;
+}
+#endif
+
/*
* Validate file access. Since we
* have no uid or gid, for now require
* file to exist and be publicly
- * readable/writable.
+ * readable/writable, unless -p specified.
* 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.
*/
-int
-validate_access(char *filename, int mode, struct formats *pf)
+static int validate_access(char *filename, int mode,
+ const struct formats *pf, const char **errmsg)
{
- struct stat stbuf;
- int i, len;
- int fd, wmode;
- char *cp;
- const char **dirp;
-
- tsize_ok = 0;
-
- if (!secure) {
- if (*filename != '/')
- return (EACCESS);
+ 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);
+ }
+ }
+
/*
- * prevent tricksters from getting around the directory
- * restrictions
+ * We use different a different permissions scheme if `cancreate' is
+ * set.
*/
- len = strlen(filename);
- for ( i = 1 ; i < len-3 ; i++ ) {
- cp = filename + i;
- if ( *cp == '.' && memcmp(cp-1, "/../", 4) == 0)
- return(EACCESS);
+ 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;
+ }
}
- 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 (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);
+
if (mode == RRQ) {
- 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;
+ 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;
} else {
- if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
- return (EACCESS);
- tsize = 0;
- tsize_ok = 1;
+ 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;
}
- }
- 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);
-}
-int timeout;
-sigjmp_buf timeoutbuf;
+ stdio_mode[0] = (mode == RRQ) ? 'r' : 'w';
+ stdio_mode[1] = (pf->f_convert) ? 't' : 'b';
+ stdio_mode[2] = '\0';
-/* Handle timeout signal */
-void
-timer(int sig)
-{
- (void)sig; /* Suppress unused warning */
- timeout += rexmtval;
- if (timeout >= maxtimeout)
- exit(0);
- siglongjmp(timeoutbuf, 1);
+ file = fdopen(fd, stdio_mode);
+ if (file == NULL)
+ exit(EX_OSERR); /* Internal error */
+
+ return (0);
}
/*
* Send the requested file.
*/
-void
-tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen)
+static void tftp_sendfile(const 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 */
- 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;
- }
- }
- }
+ 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;
- dp = r_init();
- do {
- size = readit(file, &dp, pf->f_convert);
- if (size < 0) {
- nak(errno + 100);
- goto abort;
+ 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;
+ }
+ }
}
- 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);
+
+ 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);
}
-/* Bail out signal handler */
-void
-justquit(int sig)
-{
- (void)sig; /* Suppress unused warning */
- exit(0);
-}
-
-
/*
* Receive a file.
*/
-void
-tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen)
+static void tftp_recvfile(const struct formats *pf,
+ struct tftphdr *oack, int oacklen)
{
- struct tftphdr *dp;
- int n, size;
- /* These are "static" to avoid longjmp funnies */
- static struct tftphdr *ap; /* ack buffer */
- static u_short block = 0;
- static int acksize;
+ 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;
- 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;
- }
- 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;
- }
+ 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);
- 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 */
- }
+ (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 */
}
- /* 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;
+ abort:
+ return;
}
-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 }
+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 */
};
+#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.
*/
-void
-nak(int error)
+static void nak(int error, const char *msg)
{
- 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");
+ 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");
}
diff --git a/tftpd/tftpd.h b/tftpd/tftpd.h
index 789ee94..277e5d2 100644
--- a/tftpd/tftpd.h
+++ b/tftpd/tftpd.h
@@ -1,6 +1,5 @@
-/* $Id$ */
/* ----------------------------------------------------------------------- *
- *
+ *
* Copyright 2001 H. Peter Anvin - All Rights Reserved
*
* This program is free software available under the same license
@@ -24,4 +23,13 @@ char *tfstrdup(const char *);
extern int verbosity;
+struct formats {
+ const char *f_mode;
+ char *(*f_rewrite) (const struct formats *, char *, int, int, const char **);
+ int (*f_validate) (char *, int, const struct formats *, const char **);
+ void (*f_send) (const struct formats *, struct tftphdr *, int);
+ void (*f_recv) (const struct formats *, struct tftphdr *, int);
+ int f_convert;
+};
+
#endif
diff --git a/version b/version
new file mode 100644
index 0000000..d346e2a
--- /dev/null
+++ b/version
@@ -0,0 +1 @@
+5.3