This commit is contained in:
Alexander Zhirov 2023-05-10 01:18:22 +03:00
commit 7db43a3e87
46 changed files with 8462 additions and 0 deletions

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
/MCONFIG
/aconfig.h
/aconfig.h.in
/autom4te.cache
/config.log
/config.status
/configure
/version.h
/tftp/tftp
/tftpd/tftpd
*.1
*.8
*.a
*.o
*~
\#*

402
CHANGES Normal file
View File

@ -0,0 +1,402 @@
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/).
(<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
<http://fink.sourceforge.net/>). 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
wraparound, usually manifesting themselves as failure to
handle files over 32 MB in size.
Officially make the client a part of the tftp-hpa project.
Changes in 0.23:
Correct memory overwrite bug in the tftp client when compiled
with readline.
Changes in 0.22:
Even more portability improvements: FreeBSD and
Tru64/Digital Unix.
Fix tsize option on systems on which off_t is "long long".
Support large files on systems which need _LARGE_FILE_BITS or
similar.
Some source cleanups; change to autoconf 2.52.
Add support for readline command-line editing in tftp.
Changes in 0.21:
Support running in standalone mode, without inetd.
Even more portability improvements. Now known to compile and
run on Linux, Solaris 5, 5.1, 6, 7 and 8, and AIX. Reports of
success or failure on other modern systems always appreciated.
Clean and modernize some really ugly old code.
Fix a potential illegal memory access when running in "totally
insecure mode" - no -s, no directories listed.
Changes in 0.20:
Portability improvements. Now known to compile and run on
Solaris 8.
Changes in 0.19:
Fork before performing tcpwrappers check.
Don't rely on nonstandard bsd_signal() function, instead
require that the platform has sigaction(). This is 2001,
after all. This may resolve some potential portability
problems.
Log a message if memory allocation fails, instead of dying
silently.
Clean up the main dispatch loop.
Use <sysexits.h> for exit codes, if it exists.
Add support for debugging remapping rulefiles; if logging with
-vvv tftpd will log all rules actions.
Correct the error code issued by an "abort" rule.
Changes in 0.18:
Support (almost) arbitrary filename remappings via regular
expression-based rulesets.
Added -v option for more verbose logging.
Changes in 0.17:
Add support for tcpwrapper checking (/etc/hosts.allow;
/etc/hosts.deny) in tftpd.
Compile correctly on glibc 2.1.2.
Add -u option to specify the user id to run as (default
"nobody".)
Operate in "daemon mode" as long as we keep getting requests.
This should speed up handling large amounts of requests at
once, as can happen when a client starts up, and avoids inetd
misconfiguration problems.
Changes in 0.16:
Correct massive lossage from 0.15: apparently 0.15 was based
on an out-of-date CVS repository, somehow.
Fix for ACKs in TFTP PUT; patch by Roger Venning.
Changes in 0.15:
If the operating system allows, try to obtain the local
address used for the request packet, and reply using the same
local IP address. Some embedded TFTP clients are (probably
incorrectly) picky about this.
Changes in 0.14:
Hacks to signal handling to avoid "zombie servers."
Changes in 0.13:
Added the non-standard option "blksize2". The "blksize"
option is limited in its usability, since TFTP is designed to
be implemented in a ROM, and ROM code might find it painful to
deal with packets that don't meet certain alignment
restrictions.
The "blksize2" option tells the server that the block size
must be a power of 2 to be usable to the client. The server
SHALL respond with a block size that is a power of two, up to
a maximum of 32768, or reject the option. Furthermore, the
server SHALL grant a block size that is no smaller than 512
bytes unless the client explicitly requested a smaller block
size. If the client request both options, the server MAY
accept one or the other, but not both. At some point I will
probably write up an IETF draft for this option.
General information on the tftp-hpa series:
The core software was taken from OpenBSD (CVS source as of
1999-09-21). I believe this was the most secure source base available
at the time I obtained this code, and it included support for the -s
and -c options.
The un-BSD-ized Makefiles and a lot of the configure macros were taken
from netkit-tftp-0.10 by David Holland; I also followed this example
and modernized the code style throughout.
Patches by Markus Gutschke and Gero Kuhlmann were the basis for the
option negotiation as well as the "blksize" and "tsize" option
support, although I made a fair amount of mostly stylistic changes to
their code.
Adding the -r option (disable a specific option), the "timeout"
option, converting to using autoconf for setup, and any additions
listed in the Changes list above, has all been my own code, as are any
bugs introduced in the merge.

227
INSTALL Normal file
View File

@ -0,0 +1,227 @@
Basic Installation
==================
These are generic installation instructions. See the file
INSTALL.tftp for specific install instructions for this package.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. (Caching is
disabled by default to prevent problems with accidental use of stale
cache files.)
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You only need
`configure.ac' if you want to change it or regenerate `configure' using
a newer version of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes awhile. While running, it prints some
messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. Run `./configure --help'
for details on some of the pertinent environment variables.
You can give `configure' initial values for variables by setting
them in the environment. You can do that on the command line like this:
./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
*Note Environment Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
If you have to use a `make' that does not support the `VPATH'
variable, you have to compile the package for one architecture at a time
in the source code directory. After you have installed the package for
one architecture, use `make distclean' before reconfiguring for another
architecture.
Installation Names
==================
By default, `make install' will install the package's files in
`/usr/local/bin', `/usr/local/man', etc. You can specify an
installation prefix other than `/usr/local' by giving `configure' the
option `--prefix=PATH'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
give `configure' the option `--exec-prefix=PATH', the package will use
PATH as the prefix for installing programs and libraries.
Documentation and other data files will still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=PATH' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out
automatically, but needs to determine by the type of host the package
will run on. Usually `configure' can figure that out, but if it prints
a message saying it cannot guess the host type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS
KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the host type.
If you are _building_ compiler tools for cross-compiling, you should
use the `--target=TYPE' option to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the host
platform (i.e., that on which the generated programs will eventually be
run) with `--host=TYPE'. In this case, you should also specify the
build platform with `--build=TYPE', because, in this case, it may not
be possible to guess the build platform (it sometimes involves
compiling and running simple test programs, and this can't be done if
the compiler is a cross compiler).
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Environment Variables
=====================
Variables not defined in a site shell script can be set in the
environment passed to configure. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
will cause the specified gcc to be used as the C compiler (unless it is
overridden in the site shell script).
`configure' Invocation
======================
`configure' recognizes the following options to control how it
operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

36
INSTALL.tftp Normal file
View File

@ -0,0 +1,36 @@
Specific installation instructions
==================================
In addition to what is described in the INSTALL file, the following
specifics apply to the tftp-hpa package:
The tftp-hpa package supports the following options to ./configure:
--without-tcpwrappers
Disables the use of the tcp wrapper library. This is
recommended, for performance reasons, on high-use sites.
Kernel-based firewalling is, in general, a better alternative.
--without-remap
Disables the use of regular-expression-based filename
remapping (the -m option to tftpd).
--without-readline
Disables the use of the readline command-line editing library
in the tftp client.
The default prefix for the tftp-hpa package is /usr, with the tftp
client installing as /usr/bin/tftp and the tftpd server installing as
/usr/sbin/in.tftpd on most systems. This can be overridden by setting
--bindir and --sbindir.
"make install" supports specifying an INSTALLROOT, which points to
what will be the root of the filesystem at runtime; this is typically
used when preparing packages for package-management systems.
You almost certainly will need GNU make to build tftp-hpa. If you
don't already have it, you can find GNU make at:
ftp://ftp.gnu.org/pub/make/
or
ftp://mirrors.kernel.org/gnu/make/

71
MCONFIG.in Normal file
View File

@ -0,0 +1,71 @@
## -*- makefile -*- ------------------------------------------------------
##
## 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/.
##
## -----------------------------------------------------------------------
##
## MCONFIG.in
##
## Basic Makefile definitions
##
# Source and object root
SRCROOT = @SRCROOT@
OBJROOT = @OBJROOT@
# Prefixes
prefix = @prefix@
exec_prefix = @exec_prefix@
# Directory for user binaries
BINDIR = @bindir@
# Man page tree
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@
INSTALL_DATA = @INSTALL_DATA@
# Compiler and compiler flags
CC = @CC@
CFLAGS = @CFLAGS@ -I$(SRCROOT)
# Link flags
LDFLAGS = @LDFLAGS@
# Libraries (client and server)
TFTP_LIBS = ../common/libcommon.a @TFTP_LIBS@
TFTPD_LIBS = ../common/libcommon.a @TFTPD_LIBS@
# Additional library we need to build
LIBOBJS = @LIBOBJS@
# Additional tftpd objects we need to build
TFTPDOBJS = @TFTPDOBJS@
# ar and ranlib (for making libraries)
AR = ar cq
RANLIB = @RANLIB@

22
MRULES Normal file
View File

@ -0,0 +1,22 @@
# Standard compilation rules (don't use make builtins)
.SUFFIXES: .c .cc .o .s .S .i
.c.o:
$(CC) $(CFLAGS) -c $<
.c.s:
$(CC) $(CFLAGS) -S -o $@ $<
.c.i:
$(CC) $(CFLAGS) -E -o $@ $<
.cc.o:
$(CXX) $(CXXFLAGS) -c $<
.cc.s:
$(CXX) $(CXXFLAGS) -S -o $@ $<
.cc.i:
$(CXX) $(CXXFLAGS) -E -o $@ $<

75
Makefile Normal file
View File

@ -0,0 +1,75 @@
# You can do "make SUB=blah" to make only a few, or edit here, or both
# You can also run make directly in the subdirs you want.
SUB = lib common tftp tftpd
%.build: MCONFIG aconfig.h version.h
$(MAKE) -C $(patsubst %.build, %, $@)
%.install: MCONFIG aconfig.h version.h
$(MAKE) -C $(patsubst %.install, %, $@) install
%.clean:
$(MAKE) -C $(patsubst %.clean, %, $@) clean
%.distclean:
$(MAKE) -C $(patsubst %.distclean, %, $@) distclean
all: MCONFIG $(patsubst %, %.build, $(SUB))
tftp.build: lib.build common.build
tftpd.build: lib.build common.build
install: MCONFIG $(patsubst %, %.install, $(SUB))
clean: localclean $(patsubst %, %.clean, $(SUB))
localclean:
rm -f version.h
distclean: localdistclean $(patsubst %, %.distclean, $(SUB))
localdistclean: localclean
rm -f MCONFIG config.status config.log aconfig.h *~ \#*
rm -rf *.cache
find . -type f \( -name \*.orig -o -name \*.rej \) | xargs rm -f
spotless: distclean
rm -f configure aconfig.h.in tftp.spec
autoconf: configure aconfig.h.in
config: MCONFIG aconfig.h
release:
$(MAKE) autoconf
$(MAKE) tftp.spec
$(MAKE) distclean
MCONFIG: configure MCONFIG.in aconfig.h.in
if test -x config.status; then \
./config.status --recheck && ./config.status ; \
else \
./configure ; \
fi
aconfig.h: MCONFIG
: Generated by side effect
# Adding "configure" to the dependencies serializes this with running
# autoconf, because there are apparently race conditions between
# autoconf and autoheader.
aconfig.h.in: configure.in configure aclocal.m4
rm -f aconfig.h.in aconfig.h
autoheader
configure: configure.in aclocal.m4
rm -rf MCONFIG configure config.log aconfig.h *.cache
autoconf
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 $@

27
README Normal file
View File

@ -0,0 +1,27 @@
This is tftp-hpa, a conglomerate of a number of versions of the BSD
TFTP code, changed around to port to a whole collection of operating
systems. The goal is to work on any reasonably modern Unix with
sockets.
The tftp-hpa series is maintained by H. Peter Anvin <hpa@zytor.com>.
The latest version of this collection can be found at:
ftp://ftp.kernel.org/pub/software/network/tftp/
See the file CHANGES for a list of changes between versions.
Please see the INSTALL and INSTALL.tftp files for compilation and
installation instructions.
===> IMPORTANT: IF YOU ARE UPGRADING FROM ANOTHER TFTP SERVER, OR FROM
===> A VERSION OF TFTP-HPA OLDER THAN 0.17 SEE THE FILE
===> "README.security" FOR IMPORTANT SECURITY MODEL CHANGES!
This software can be discussed on the SYSLINUX mailing list. To
subscribe, go to the list subscription page at:
http://www.zytor.com/mailman/listinfo/syslinux

56
README.security Normal file
View File

@ -0,0 +1,56 @@
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
subsequent request, which apparently has been a problem in the past,
resulting in "request storms" as the client keeps retrying, resulting
in multiple connections on the server which the client has already
abandoned.
This also means that spawning tftp via tcpd is useless (in fact, this
indirection seems to be part of the reason for these "request
storms.") Instead, tftp-hpa supports calling the tcpwrapper library
directly. Thus, if your /etc/inetd.conf looks like this (all on one
line):
tftp dgram udp wait root /usr/sbin/tcpd
/usr/sbin/in.tftpd -s /tftpboot -r blksize
... it's better to change to ...
tftp dgram udp wait root /usr/sbin/in.tftpd
in.tftpd -s /tftpboot -r blksize
You should make sure that you are using "wait" option in tftpd; you
also need to have tftpd spawned as root in order for chroot (-s) to
work. tftpd automatically drops privilege and changes user ID to
"nobody" by default; the appropriate user ID for tftpd can be
specified with the -u option (e.g. "-u tftpuser").
If you are running a busy boot server, I would suggest to instead use
kernel-based firewalling rules, and to compile tftpd without
tcpwrapper support, in order to provide significantly better
performance. To do so, specify the --without-tcpwrappers option to
configure when compiling; see the INSTALL.tftp file for more information.

280
aclocal.m4 vendored Normal file
View File

@ -0,0 +1,280 @@
dnl -----------------------------------------------------------------------
dnl
dnl Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
dnl
dnl This program is free software; you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
dnl the Free Software Foundation, Inc., 53 Temple Place Ste 330,
dnl Bostom MA 02111-1307, USA; either version 2 of the License, or
dnl (at your option) any later version; incorporated herein by reference.
dnl
dnl -----------------------------------------------------------------------
dnl --------------------------------------------------------------------------
dnl PA_ADD_CFLAGS()
dnl
dnl Attempt to add the given option to CFLAGS, if it doesn't break compilation
dnl --------------------------------------------------------------------------
AC_DEFUN(PA_ADD_CFLAGS,
[AC_MSG_CHECKING([if $CC accepts $1])
pa_add_cflags__old_cflags="$CFLAGS"
CFLAGS="$CFLAGS $1"
AC_TRY_LINK([#include <stdio.h>],
[printf("Hello, World!\n");],
AC_MSG_RESULT([yes]),
AC_MSG_RESULT([no])
CFLAGS="$pa_add_cflags__old_cflags")])
dnl --------------------------------------------------------------------------
dnl PA_SIGSETJMP
dnl
dnl Do we have sigsetjmp/siglongjmp? (AC_CHECK_FUNCS doesn't seem to work
dnl for these particular functions.)
dnl --------------------------------------------------------------------------
AC_DEFUN(PA_SIGSETJMP,
[AC_MSG_CHECKING([for sigsetjmp])
AC_TRY_LINK([
#ifdef HAVE_SETJMP_H
#include <setjmp.h>
#endif],
[sigjmp_buf buf;
sigsetjmp(buf,1);
siglongjmp(buf,2);],
AC_MSG_RESULT([yes])
$1,
AC_MSG_RESULT([no])
$2)])
dnl --------------------------------------------------------------------------
dnl PA_MSGHDR_MSG_CONTROL
dnl
dnl Does struct msghdr have the msg_control field?
dnl --------------------------------------------------------------------------
AH_TEMPLATE([HAVE_MSGHDR_MSG_CONTROL],
[Define if struct msghdr has the msg_control field.])
AC_DEFUN(PA_MSGHDR_MSG_CONTROL,
[AC_CHECK_MEMBER(struct msghdr.msg_control,
[AC_DEFINE(HAVE_MSGHDR_MSG_CONTROL)],
[],
[
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
])])
dnl ------------------------------------------------------------------------
dnl PA_STRUCT_IN_PKTINFO
dnl
dnl Look for definition of struct in_pktinfo, which at least has an
dnl ipi_addr member. Some versions of glibc lack struct in_pktinfo;
dnl if so we need to include the definition ourselves -- but we only
dnl want to do that if absolutely necessary!
dnl ------------------------------------------------------------------------
AH_TEMPLATE([HAVE_STRUCT_IN_PKTINFO],
[Define if struct in_pktinfo is defined.])
AC_DEFUN(PA_STRUCT_IN_PKTINFO,
[AC_CHECK_MEMBER(struct in_pktinfo.ipi_addr,
[AC_DEFINE(HAVE_STRUCT_IN_PKTINFO)],
[],
[
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/uio.h>
])])
dnl ------------------------------------------------------------------------
dnl PA_STRUCT_SOCKADDR_IN6
dnl
dnl Look for definition of struct sockaddr_in6, which at least has an
dnl sin6_addr member
dnl
AH_TEMPLATE([HAVE_STRUCT_SOCKADDR_IN6],
[Define if struct sockaddr_in6 is defined.])
AC_DEFUN(PA_STRUCT_SOCKADDR_IN6,
[AC_CHECK_MEMBER(struct sockaddr_in6.sin6_addr,
[
AC_DEFINE(HAVE_STRUCT_SOCKADDR_IN6)
HAVE_INET6=true;
],
[
HAVE_INET6=false;
],
[
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
])])
dnl ------------------------------------------------------------------------
dnl PA_STRUCT_ADDRINFO
dnl
dnl Look for definition of struct addrinfo, which at least has an
dnl ai_addr member
dnl
AH_TEMPLATE([HAVE_STRUCT_ADDRINFO],
[Define if struct addrinfo is defined.])
AC_DEFUN(PA_STRUCT_ADDRINFO,
[AC_CHECK_MEMBER(struct addrinfo.ai_addr,
[AC_DEFINE(HAVE_STRUCT_ADDRINFO)],
[],
[
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
])])
dnl ------------------------------------------------------------------------
dnl PA_STRUCT_IN6_PKTINFO
dnl
dnl Look for definition of struct in6_pktinfo, which at least has an
dnl ipi6_addr member
dnl
AH_TEMPLATE([HAVE_STRUCT_IN6_PKTINFO],
[Define if struct in6_pktinfo is defined.])
AC_DEFUN(PA_STRUCT_IN6_PKTINFO,
[AC_CHECK_MEMBER(struct in6_pktinfo.ipi6_addr,
[AC_DEFINE(HAVE_STRUCT_IN6_PKTINFO)],
[],
[
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
])])
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 <tcpd.h>.])
AC_DEFUN(PA_HAVE_TCPWRAPPERS,
[AC_CHECK_LIB([wrap], [main])
AC_MSG_CHECKING([for tcpwrappers])
AC_TRY_LINK(
[
#include <tcpd.h>
int allow_severity = 0;
int deny_severity = 0;
],
[
hosts_ctl("sample_daemon", STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN);
],
[
AC_DEFINE(HAVE_TCPWRAPPERS)
AC_MSG_RESULT([yes])
],
[
AC_MSG_RESULT([no])
])])
dnl ------------------------------------------------------------------------
dnl PA_CHECK_INTTYPES_H_SANE
dnl
dnl At least some versions of AIX 4 have <inttypes.h> macros which are
dnl completely broken. Try to detect those.
dnl --------------------------------------------------------------------------
AH_TEMPLATE([INTTYPES_H_IS_SANE],
[Define if the macros in <inttypes.h> are usable])
AC_DEFUN(PA_CHECK_INTTYPES_H_SANE,
[AC_CHECK_HEADERS(inttypes.h,
[
AC_MSG_CHECKING([if inttypes.h is sane])
AC_TRY_LINK(
[
#include <inttypes.h>
#include <stdio.h>
],
[uintmax_t max = UINTMAX_C(0);
printf("%"PRIuMAX"\n", max);],
AC_MSG_RESULT([yes])
AC_DEFINE(INTTYPES_H_IS_SANE),
AC_MSG_RESULT([no (AIX, eh?)]))
])
])
dnl ------------------------------------------------------------------------
dnl PA_WITH_BOOL
dnl
dnl PA_WITH_BOOL(option, default, help, enable, disable)
dnl
dnl Provides a more convenient way to specify --with-option and
dnl --without-option, with a default. default should be either 0 or 1.
dnl ------------------------------------------------------------------------
AC_DEFUN(PA_WITH_BOOL,
[AC_ARG_WITH([$1], [$3],
if test ["$withval"] != no; then
[$4]
else
[$5]
fi,
if test [$2] -ne 0; then
[$4]
else
[$5]
fi)])
dnl --------------------------------------------------------------------------
dnl PA_HEADER_DEFINES
dnl
dnl PA_HEADER_DEFINES(header, type, value)
dnl --------------------------------------------------------------------------
AC_DEFUN(PA_HEADER_DEFINES,
[AC_MSG_CHECKING([if $1 defines $3])
AH_TEMPLATE([HAVE_$3_DEFINITION], [Define if $1 defines $3])
AC_TRY_COMPILE([
#include <$1>
],
[
int main()
{
$2 dummy = $3;
return 0;
}
],
[
pa_header_define=`echo HAVE_$3_DEFINITION | tr '[a-z]' '[A-Z]'`
AC_DEFINE_UNQUOTED($pa_header_define)
AC_MSG_RESULT(yes)
],
[
AC_MSG_RESULT(no)
])])
dnl --------------------------------------------------------------------------
dnl PA_SEARCH_LIBS_AND_ADD
dnl
dnl PA_SEARCH_LIBS_AND_ADD(function, libraries [,function to add])
dnl --------------------------------------------------------------------------
AC_DEFUN(PA_SEARCH_LIBS_AND_ADD,
[
AH_TEMPLATE(AS_TR_CPP(HAVE_$1), [Define if $1 function was found])
AC_SEARCH_LIBS($1, $2,
[
AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1))
pa_add_$1=false;
],
[
XTRA=true;
if test $# -eq 3; then
AC_LIBOBJ($3)
else
AC_LIBOBJ($1)
fi
pa_add_$1=true;
])])

2
autogen.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
make autoconf

25
common/Makefile Normal file
View File

@ -0,0 +1,25 @@
SRCROOT = ..
VERSION = $(shell cat ../version)
-include ../MCONFIG
include ../MRULES
OBJS = tftpsubs.$(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 *~

406
common/tftpsubs.c Normal file
View File

@ -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 <sys/ioctl.h>
#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, &notime) <= 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

121
common/tftpsubs.h Normal file
View File

@ -0,0 +1,121 @@
/*
* Copyright (c) 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.
*/
/*
* Prototypes for read-ahead/write-behind subroutines for tftp user and
* server.
*/
#ifndef TFTPSUBS_H
#define TFTPSUBS_H
#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);
int synchnet(int);
struct tftphdr *w_init(void);
int write_behind(FILE *, int);
int writeit(FILE *, struct tftphdr **, int, int);
extern int segsize;
#define MAX_SEGSIZE 65464
int pick_port_bind(int sockfd, union sock_addr *myaddr,
unsigned int from, unsigned int to);
#endif

380
config.h Normal file
View File

@ -0,0 +1,380 @@
/* -*- c -*- ------------------------------------------------------------- *
*
* 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
* http://www.openbsd.org/.
*
* ----------------------------------------------------------------------- */
/*
* config.h
*
* Sets up a common baseline environment, based on "autoconf" findings...
*/
#ifndef CONFIG_H
#define CONFIG_H 1
/* Must be included before we include any system headers! */
#include "aconfig.h" /* autogenerated configuration header */
/* Standard includes */
#include <stdio.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef STDC_HEADERS
#include <stdlib.h>
#include <stddef.h>
#else
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#endif
#ifdef HAVE_MEMORY_H
#ifndef STDC_HEADERS
#include <memory.h>
#endif
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#ifdef HAVE_INTTYPES_H
#ifdef INTTYPES_H_IS_SANE
#include <inttypes.h>
#endif
#else
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SETJMP_H
#include <setjmp.h>
#endif
#ifdef TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <errno.h>
#include <signal.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#else
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#else
#ifdef HAVE_WINSOCK_H
#include <winsock.h>
#endif
#endif
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_GETOPT_LONG
#include <getopt.h>
#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... */
#ifndef HAVE_O_TEXT_DEFINITION
#define O_TEXT 0
#endif
#ifndef HAVE_O_BINARY_DEFINITION
#define O_BINARY 0
#endif
/* If we don't have intmax_t, try creating it */
#ifndef HAVE_INTMAX_T
#ifdef HAVE_LONG_LONG
typedef long long intmax_t;
typedef unsigned long long uintmax_t;
#define PRIdMAX "lld"
#define PRIuMAX "llu"
#define PRIxMAX "llx"
#define INTMAX_C(x) (x##LL)
#define UINTMAX_C(x) (x##ULL)
#else
typedef long intmax_t;
typedef unsigned long uintmax_t;
#define PRIdMAX "ld"
#define PRIuMAX "lu"
#define PRIxMAX "lx"
#define INTMAX_C(x) (x##L)
#define UINTMAX_C(x) (x##UL)
#endif
#endif
/* On some version of AIX, <inttypes.h> 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)
#define strtoumax(p,e,b) ((uintmax_t)strtoull(p,e,b))
#else
#define strtoumax(p,e,b) ((uintmax_t)strtoul(p,e,b))
#endif
#endif
/* A lot of this is old BSD code. Some newer systems don't approve. */
/* The type used by htons(), ntohs() */
#ifndef HAVE_U_SHORT
#ifdef HAVE_UINT16_T
typedef uint16_t u_short;
#else
typedef unsigned short u_short;
#endif
#endif
/* The type used to htonl(), ntohl() */
#ifndef HAVE_U_LONG
#ifdef HAVE_UINT32_T
typedef uint32_t u_long;
#else
typedef unsigned long u_long;
#endif
#endif
/* socklen_t */
#ifndef HAVE_SOCKLEN_T
typedef int socklen_t;
#endif
/* sysexits.h */
#ifdef HAVE_SYSEXITS_H
#include <sysexits.h>
#else
#define EX_USAGE 64 /* command line usage error */
#define EX_DATAERR 65 /* data format error */
#define EX_NOINPUT 66 /* cannot open input */
#define EX_NOUSER 67 /* addressee unknown */
#define EX_NOHOST 68 /* host name unknown */
#define EX_UNAVAILABLE 69 /* service unavailable */
#define EX_SOFTWARE 70 /* internal software error */
#define EX_OSERR 71 /* system error (e.g., can't fork) */
#define EX_OSFILE 72 /* critical OS file missing */
#define EX_CANTCREAT 73 /* can't create (user) output file */
#define EX_IOERR 74 /* input/output error */
#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
#define EX_PROTOCOL 76 /* remote error in protocol */
#define EX_NOPERM 77 /* permission denied */
#define EX_CONFIG 78 /* configuration error */
#endif
/* If we don't have sigsetjmp() et all, setjmp() will have to do */
#ifndef HAVE_SIGSETJMP
#define sigsetjmp(x,y) setjmp(x)
#define siglongjmp(x,y) longjmp(x,y)
#define sigjmp_buf jmp_buf
#endif
/* How do we annotate unused data items? */
#ifndef UNUSED
#ifdef __GNUC__
#define UNUSED __attribute__((unused))
#else
#define UNUSED
#endif
#endif
/* netinet/in.h, and possible missing pieces */
#include <netinet/in.h>
#ifndef HAVE_IPPORT_TFTP_DEFINITION
#ifndef IPPORT_TFTP
#define IPPORT_TFTP 69
#endif
#endif
/* arpa/{inet,tftp}.h, and possible missing pieces */
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
/* If we don't have arpa/tftp.h we have problems... */
#include <arpa/tftp.h>
#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_BSD_SIGNAL
void (*bsd_signal(int, void (*)(int))) (int);
#endif
#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

303
configure.in Normal file
View File

@ -0,0 +1,303 @@
dnl
dnl autoconf input file to generate MCONFIG
dnl
AC_PREREQ(2.61)
AC_INIT(MCONFIG.in)
AC_PREFIX_DEFAULT(/usr)
AC_USE_SYSTEM_EXTENSIONS
AC_ISC_POSIX
AC_PROG_CC
AC_C_CONST
AC_C_INLINE
PA_ADD_CFLAGS(-W)
PA_ADD_CFLAGS(-Wall)
PA_ADD_CFLAGS(-Wpointer-arith)
PA_ADD_CFLAGS(-Wbad-function-cast)
PA_ADD_CFLAGS(-Wcast-equal)
PA_ADD_CFLAGS(-Wstrict-prototypes)
PA_ADD_CFLAGS(-Wmissing-prototypes)
PA_ADD_CFLAGS(-Wmissing-declarations)
PA_ADD_CFLAGS(-Wnested-externs)
PA_ADD_CFLAGS(-Winline)
PA_ADD_CFLAGS(-Wwrite-strings)
PA_ADD_CFLAGS(-Wundef)
PA_ADD_CFLAGS(-Wshadow)
PA_ADD_CFLAGS(-Wsign-compare)
PA_ADD_CFLAGS(-pipe)
PA_ADD_CFLAGS(-fno-strict-aliasing)
AC_HEADER_STDC
AC_CHECK_HEADERS(inttypes.h)
AC_CHECK_HEADERS(stdint.h)
PA_CHECK_INTTYPES_H_SANE
AC_CHECK_HEADERS(fcntl.h)
AC_CHECK_HEADERS(grp.h)
AC_CHECK_HEADERS(libgen.h)
AC_CHECK_HEADERS(memory.h)
AC_CHECK_HEADERS(setjmp.h)
AC_CHECK_HEADERS(stddef.h)
AC_CHECK_HEADERS(stdlib.h)
AC_CHECK_HEADERS(string.h)
AC_CHECK_HEADERS(strings.h)
AC_CHECK_HEADERS(sysexits.h)
AC_CHECK_HEADERS(time.h)
AC_CHECK_HEADERS(unistd.h)
AC_CHECK_HEADERS(sys/file.h)
AC_CHECK_HEADERS(sys/filio.h)
AC_CHECK_HEADERS(sys/stat.h)
AC_CHECK_HEADERS(sys/time.h)
AC_CHECK_HEADERS(sys/types.h)
AC_CHECK_HEADERS(arpa/inet.h)
AC_CHECK_HEADERS(netdb.h)
AC_HEADER_TIME
dnl This is needed on some versions of FreeBSD...
AC_CHECK_HEADERS(machine/param.h)
AC_CHECK_HEADERS(sys/socket.h)
AC_CHECK_HEADERS(winsock2.h)
AC_CHECK_HEADERS(winsock.h)
AC_SYS_LARGEFILE
AC_TYPE_OFF_T
AC_TYPE_PID_T
AC_TYPE_MODE_T
AC_TYPE_SIZE_T
AC_CHECK_TYPES(intmax_t)
AC_CHECK_TYPES(long long)
AC_CHECK_TYPES(uint16_t)
AC_CHECK_TYPES(uint32_t)
AC_CHECK_TYPES(u_short)
AC_CHECK_TYPES(u_long)
dnl
dnl <sys/socket.h> isn't among the list of standard headers that autoconf checks,
dnl but POSIX requires <sys/socket.h> for socklen_t to be defined.
dnl
AC_CHECK_TYPES(socklen_t,,,
[
#include <stdio.h>
#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#if HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#if STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# if HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
#if HAVE_STRING_H
# if !STDC_HEADERS && HAVE_MEMORY_H
# include <memory.h>
# endif
# include <string.h>
#endif
#if HAVE_STRINGS_H
# include <strings.h>
#endif
#if HAVE_INTTYPES_H
# include <inttypes.h>
#else
# if HAVE_STDINT_H
# include <stdint.h>
# endif
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
])
AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , [AC_MSG_ERROR(socket library not found)])
AC_CHECK_FUNCS(fcntl)
AC_CHECK_FUNCS(setsid)
AC_CHECK_FUNCS(recvmsg)
AC_CHECK_FUNCS(ftruncate)
AC_CHECK_FUNCS(setreuid)
AC_CHECK_FUNCS(setregid)
AC_CHECK_FUNCS(initgroups)
AC_CHECK_FUNCS(setgroups)
dnl Solaris 8 has [u]intmax_t but not strtoumax(). How utterly braindamaged.
AC_CHECK_FUNCS(strtoumax)
AC_CHECK_FUNCS(strtoull)
PA_MSGHDR_MSG_CONTROL
PA_STRUCT_IN_PKTINFO
PA_STRUCT_ADDRINFO
PA_HEADER_DEFINES(fcntl.h, int, O_NONBLOCK)
PA_HEADER_DEFINES(fcntl.h, int, O_BINARY)
PA_HEADER_DEFINES(fcntl.h, int, O_TEXT)
PA_HEADER_DEFINES(fcntl.h, int, F_SETLK)
PA_HEADER_DEFINES(sys/file.h, int, LOCK_SH)
PA_HEADER_DEFINES(sys/file.h, int, LOCK_EX)
AH_TEMPLATE([HAVE_SIGSETJMP],
[Define if we have sigsetjmp, siglongjmp and sigjmp_buf.])
PA_SIGSETJMP([AC_DEFINE(HAVE_SIGSETJMP)])
dnl
dnl Get common paths
dnl
SRCROOT=`cd $srcdir && pwd`
OBJROOT=`pwd`
XTRA=false
PA_SEARCH_LIBS_AND_ADD(xmalloc, iberty)
PA_SEARCH_LIBS_AND_ADD(xstrdup, iberty)
PA_SEARCH_LIBS_AND_ADD(bsd_signal, bsd, bsdsignal)
PA_SEARCH_LIBS_AND_ADD(getopt_long, getopt, getopt_long)
PA_SEARCH_LIBS_AND_ADD(getaddrinfo, [nsl resolv])
if $pa_add_getaddrinfo
then
AC_SEARCH_LIBS(gethostbyname, [nsl resolv],
[AC_SEARCH_LIBS(herror, [nsl resolv], ,
[AC_MSG_ERROR(herror not found)])],
[AC_MSG_ERROR(gethostbyname not found)])
else
AC_SEARCH_LIBS(freeaddrinfo, [nsl resolv], ,
[AC_MSG_ERROR(getaddrinfo but not freeaddrinfo found)])
AC_SEARCH_LIBS(gai_strerror, [nsl resolv], ,
[AC_MSG_ERROR(getaddrinfo but not gai_strerror found)])
fi
PA_SEARCH_LIBS_AND_ADD(inet_ntop, [nsl resolv])
if $pa_add_inet_ntop
then
AC_SEARCH_LIBS(inet_ntoa, [nsl resolv], ,
[AC_MSG_ERROR(inet_ntoa not found)])
fi
AC_SEARCH_LIBS(inet_aton, [nsl resolv], ,[AC_MSG_ERROR(inet_aton not found)])
AC_CHECK_FUNCS(daemon, , [XTRA=true; AC_LIBOBJ(daemon)])
AC_CHECK_FUNCS(dup2, , [XTRA=true; AC_LIBOBJ(dup2)])
if $XTRA
then
XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS"
fi
dnl
dnl These libraries apply to the server only
dnl
common_libs="$LIBS"
PA_HEADER_DEFINES(netinet/in.h, int, IPPORT_TFTP)
PA_WITH_BOOL(tcpwrappers, 1,
[ --without-tcpwrappers disable tcpwrapper permissions checking],
[
AC_SEARCH_LIBS(yp_get_default_domain, [nsl resolv])
PA_HAVE_TCPWRAPPERS
],:)
AH_TEMPLATE([WITH_REGEX],
[Define if we are compiling with regex filename remapping.])
PA_WITH_BOOL(remap, 1,
[ --without-remap disable regex-based filename remapping],
[
AC_CHECK_HEADER(regex.h,
[
AC_SEARCH_LIBS(regcomp, [regex rx],
[
AC_DEFINE(WITH_REGEX)
TFTPDOBJS="remap.${OBJEXT} $TFTPDOBJS"
])
])
],:)
TFTPD_LIBS="$LIBS $XTRALIBS"
LIBS="$common_libs"
dnl
dnl These libraries apply to the client only
dnl
AH_TEMPLATE([WITH_READLINE],
[Define if we are compiling with readline/editline command-line editing.])
PA_WITH_BOOL(readline, 1,
[ --without-readline disable the use of readline command-line editing],
[
AC_CHECK_HEADER(readline/readline.h,
[
dnl readline may need libtermcap or somesuch...
AC_SEARCH_LIBS(tputs, [termcap terminfo])
AC_SEARCH_LIBS(readline, [readline history],
[AC_DEFINE(WITH_READLINE)])
AC_CHECK_HEADERS(readline/history.h)
],
[AC_CHECK_HEADER(editline/readline.h,
[
dnl editline may need libtermcap or somesuch...
AC_SEARCH_LIBS(tputs, [termcap terminfo])
AC_SEARCH_LIBS(editline, [edit],
[AC_DEFINE(WITH_READLINE)])
])])
],:)
TFTP_LIBS="$LIBS $XTRALIBS"
LIBS="$common_libs"
dnl
dnl Check for IPV6 and disable-ipv6
dnl
PA_STRUCT_SOCKADDR_IN6
AC_MSG_CHECKING([for IPv6 support])
PA_WITH_BOOL(ipv6, 1,
[ --without-ipv6 disable the support for IPv6],
[
if $HAVE_INET6
then
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_IPV6, 1, [Define if IPv6 support is enabled.])
PA_STRUCT_IN6_PKTINFO
else
AC_MSG_RESULT(no)
AC_MSG_WARN([*** we do not have required IPv6 structs - IPv6 will be disabled])
fi
],
[AC_MSG_RESULT(disabled)])
AC_SUBST(SRCROOT)
AC_SUBST(OBJROOT)
AC_SUBST(TFTP_LIBS)
AC_SUBST(TFTPD_LIBS)
AC_SUBST(TFTPDOBJS)
AC_PROG_LN_S
AC_PROG_RANLIB
dnl
dnl Make sure the install program has an absolute path if it
dnl has a path at all. autoconf doesn't do this "in order
dnl to not pollute the cache." Sigh.
dnl Note: the $ needs to be double-quoted for reasons unknown.
dnl
AC_PROG_INSTALL
[if echo "$INSTALL" | grep '^[^/].*/' > /dev/null 2>&1; then
INSTALL='\${SRCROOT}'/"$INSTALL"
fi]
AC_CONFIG_HEADERS(aconfig.h)
AC_OUTPUT(MCONFIG)

251
install-sh Executable file
View File

@ -0,0 +1,251 @@
#!/bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch. It can only install one file at a time, a restriction
# shared with many OS's install programs.
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
transformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
while [ x"$1" != x ]; do
case $1 in
-c) instcmd="$cpprog"
shift
continue;;
-d) dir_arg=true
shift
continue;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
-s) stripcmd="$stripprog"
shift
continue;;
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
shift
continue;;
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
shift
continue;;
*) if [ x"$src" = x ]
then
src=$1
else
# this colon is to work around a 386BSD /bin/sh bug
:
dst=$1
fi
shift
continue;;
esac
done
if [ x"$src" = x ]
then
echo "install: no input file specified"
exit 1
else
:
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
chmodcmd=""
else
instcmd=$mkdirprog
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
then
:
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
:
fi
# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic
if [ -d $dst ]
then
dst="$dst"/`basename $src`
else
:
fi
fi
## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# this part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ] ;
then
$mkdirprog "${pathcomp}"
else
:
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else : ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else : ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else : ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else : ; fi
else
# If we're going to rename the final executable, determine the name now.
if [ x"$transformarg" = x ]
then
dstfile=`basename $dst`
else
dstfile=`basename $dst $transformbasename |
sed $transformarg`$transformbasename
fi
# don't allow the sed command to completely eliminate the filename
if [ x"$dstfile" = x ]
then
dstfile=`basename $dst`
else
:
fi
# Make a temp file name in the proper directory.
dsttmp=$dstdir/#inst.$$#
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp &&
trap "rm -f ${dsttmp}" 0 &&
# and set any options; do chmod last to preserve setuid bits
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else :;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else :;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else :;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else :;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

29
lib/Makefile Normal file
View File

@ -0,0 +1,29 @@
#
# Extra functions which may not be available everywhere
#
SRCROOT = ..
-include ../MCONFIG
include ../MRULES
ifeq ($(LIBOBJS),)
all:
else
all: libxtra.a
endif
install:
clean:
-rm -f *.a *.o *.obj *.exe
distclean: clean
-rm -f *~
libxtra.a: $(LIBOBJS)
-rm -f libxtra.a
$(AR) libxtra.a $(LIBOBJS)
$(RANLIB) libxtra.a

27
lib/bsdsignal.c Normal file
View File

@ -0,0 +1,27 @@
/*
* bsdsignal.c
*
* Use sigaction() to simulate BSD signal()
*/
#include "config.h"
void (*bsd_signal(int signum, void (*handler) (int))) (int) {
struct sigaction action, oldaction;
memset(&action, 0, sizeof action);
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, signum);
action.sa_flags = SA_RESTART;
if (sigaction(signum, &action, &oldaction) == -1) {
#ifdef SIG_ERR
return SIG_ERR;
#else
return NULL;
#endif
}
return oldaction.sa_handler;
}

36
lib/daemon.c Normal file
View File

@ -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
}

23
lib/dup2.c Normal file
View File

@ -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;
}

121
lib/getaddrinfo.c Normal file
View File

@ -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;
}

23
lib/getopt.h Normal file
View File

@ -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 */

150
lib/getopt_long.c Normal file
View File

@ -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 '?';
}
}

52
lib/inet_ntop.c Normal file
View File

@ -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;
}

20
lib/xmalloc.c Normal file
View File

@ -0,0 +1,20 @@
/*
* xmalloc.c
*
* Simple error-checking version of malloc()
*
*/
#include "config.h"
void *xmalloc(size_t size)
{
void *p = malloc(size);
if (!p) {
fprintf(stderr, "Out of memory!\n");
exit(128);
}
return p;
}

20
lib/xstrdup.c Normal file
View File

@ -0,0 +1,20 @@
/*
* xstrdup.c
*
* Simple error-checking version of strdup()
*
*/
#include "config.h"
char *xstrdup(const char *s)
{
char *p = strdup(s);
if (!p) {
fprintf(stderr, "Out of memory!\n");
exit(128);
}
return p;
}

18
tftp-xinetd Normal file
View File

@ -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
}

228
tftp.spec Normal file
View File

@ -0,0 +1,228 @@
Summary: The client for the Trivial File Transfer Protocol (TFTP).
Name: tftp
Version: 5.2
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 <hpa@zytor.com>
- removed completely broken "Malta" patch.
- integrated into build machinery so rpm -ta works.
* Fri Feb 13 2004 Elliot Lee <sopwith@redhat.com>
- rebuilt
* Wed Jun 04 2003 Elliot Lee <sopwith@redhat.com>
- rebuilt
* Fri Apr 11 2003 Elliot Lee <sopwith@redhat.com>
- 0.33
- Add /tftpboot directory (#88204)
* Mon Feb 24 2003 Elliot Lee <sopwith@redhat.com>
- rebuilt
* Sun Feb 23 2003 Tim Powers <timp@redhat.com>
- add BuildPreReq on tcp_wrappers
* Wed Jan 22 2003 Tim Powers <timp@redhat.com>
- rebuilt
* Mon Nov 11 2002 Elliot Lee <sopwith@redhat.com> 0.32-1
- Update to 0.32
* Wed Oct 23 2002 Elliot Lee <sopwith@redhat.com> 0.30-1
- Fix #55789
- Update to 0.30
* Thu Jun 27 2002 Elliot Lee <sopwith@redhat.com>
- Try applying HJ's patch from #65476
* Fri Jun 21 2002 Tim Powers <timp@redhat.com>
- automated rebuild
* Mon Jun 17 2002 Elliot Lee <sopwith@redhat.com>
- Update to 0.29
* Thu May 23 2002 Tim Powers <timp@redhat.com>
- automated rebuild
* Wed Jan 09 2002 Tim Powers <timp@redhat.com>
- automated rebuild
* Tue Dec 18 2001 Elliot Lee <sopwith@redhat.com> 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 <sopwith@redhat.com>
- Bump release + rebuild.
* Tue Jun 12 2001 Helge Deller <hdeller@redhat.de> (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 <hdeller@redhat.de>
- rebuilt in 7.1.x
* Wed Apr 18 2001 Helge Deller <hdeller@redhat.de>
- fix tftp client's put problems (#29529)
- update to tftp-hpa-0.16
* Wed Apr 4 2001 Jakub Jelinek <jakub@redhat.com>
- don't let configure to guess compiler, it can pick up egcs
* Thu Feb 08 2001 Helge Deller <hdeller@redhat.de>
- 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 <hdeller@redhat.de>
- updated tftp client to 0.17 stable (#19640),
- drop dependency on xinetd for tftp client (#25051),
* Wed Jan 17 2001 Jeff Johnson <jbj@redhat.com>
- xinetd shouldn't wait on tftp (which forks) (#23923).
* Sat Jan 6 2001 Jeff Johnson <jbj@redhat.com>
- 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 <nalin@redhat.com>
- default to being disabled
* Thu Aug 17 2000 Jeff Johnson <jbj@redhat.com>
- correct group.
* Tue Jul 25 2000 Nalin Dahyabhai <nalin@redhat.com>
- change user from root to nobody
* Sat Jul 22 2000 Jeff Johnson <jbj@redhat.com>
- update to tftp-hpa-0.14 (#14003).
- add server_args (#14003).
- remove -D_BSD_SOURCE (#14003).
* Fri Jul 21 2000 Nalin Dahyabhai <nalin@redhat.com>
- cook up an xinetd config file for tftpd
* Wed Jul 12 2000 Prospector <bugzilla@redhat.com>
- automatic rebuild
* Sun Jun 18 2000 Jeff Johnson <jbj@redhat.com>
- FHS packaging.
- update to 0.17.
* Fri May 5 2000 Matt Wilson <msw@redhat.com>
- use _BSD_SOURCE for hpa's tftpd so we get BSD signal semantics.
* Fri Feb 11 2000 Bill Nottingham <notting@redhat.com>
- fix description
* Wed Feb 9 2000 Jeff Johnson <jbj@redhat.com>
- compress man pages (again).
* Wed Feb 02 2000 Cristian Gafton <gafton@redhat.com>
- man pages are compressed
- fix description and summary
* Tue Jan 4 2000 Bill Nottingham <notting@redhat.com>
- split client and server
* Tue Dec 21 1999 Jeff Johnson <jbj@redhat.com>
- update to 0.16.
* Sat Aug 28 1999 Jeff Johnson <jbj@redhat.com>
- update to 0.15.
* Wed Apr 7 1999 Jeff Johnson <jbj@redhat.com>
- tftpd should truncate file when overwriting (#412)
* Sun Mar 21 1999 Cristian Gafton <gafton@redhat.com>
- auto rebuild in the new build environment (release 22)
* Mon Mar 15 1999 Jeff Johnson <jbj@redhat.com>
- compile for 6.0.
* Fri Aug 7 1998 Jeff Johnson <jbj@redhat.com>
- build root
* Mon Apr 27 1998 Prospector System <bugs@redhat.com>
- translations modified for de, fr, tr
* Mon Sep 22 1997 Erik Troan <ewt@redhat.com>
- added check for getpwnam() failure
* Tue Jul 15 1997 Erik Troan <ewt@redhat.com>
- initial build

228
tftp.spec.in Normal file
View File

@ -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 <hpa@zytor.com>
- removed completely broken "Malta" patch.
- integrated into build machinery so rpm -ta works.
* Fri Feb 13 2004 Elliot Lee <sopwith@redhat.com>
- rebuilt
* Wed Jun 04 2003 Elliot Lee <sopwith@redhat.com>
- rebuilt
* Fri Apr 11 2003 Elliot Lee <sopwith@redhat.com>
- 0.33
- Add /tftpboot directory (#88204)
* Mon Feb 24 2003 Elliot Lee <sopwith@redhat.com>
- rebuilt
* Sun Feb 23 2003 Tim Powers <timp@redhat.com>
- add BuildPreReq on tcp_wrappers
* Wed Jan 22 2003 Tim Powers <timp@redhat.com>
- rebuilt
* Mon Nov 11 2002 Elliot Lee <sopwith@redhat.com> 0.32-1
- Update to 0.32
* Wed Oct 23 2002 Elliot Lee <sopwith@redhat.com> 0.30-1
- Fix #55789
- Update to 0.30
* Thu Jun 27 2002 Elliot Lee <sopwith@redhat.com>
- Try applying HJ's patch from #65476
* Fri Jun 21 2002 Tim Powers <timp@redhat.com>
- automated rebuild
* Mon Jun 17 2002 Elliot Lee <sopwith@redhat.com>
- Update to 0.29
* Thu May 23 2002 Tim Powers <timp@redhat.com>
- automated rebuild
* Wed Jan 09 2002 Tim Powers <timp@redhat.com>
- automated rebuild
* Tue Dec 18 2001 Elliot Lee <sopwith@redhat.com> 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 <sopwith@redhat.com>
- Bump release + rebuild.
* Tue Jun 12 2001 Helge Deller <hdeller@redhat.de> (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 <hdeller@redhat.de>
- rebuilt in 7.1.x
* Wed Apr 18 2001 Helge Deller <hdeller@redhat.de>
- fix tftp client's put problems (#29529)
- update to tftp-hpa-0.16
* Wed Apr 4 2001 Jakub Jelinek <jakub@redhat.com>
- don't let configure to guess compiler, it can pick up egcs
* Thu Feb 08 2001 Helge Deller <hdeller@redhat.de>
- 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 <hdeller@redhat.de>
- updated tftp client to 0.17 stable (#19640),
- drop dependency on xinetd for tftp client (#25051),
* Wed Jan 17 2001 Jeff Johnson <jbj@redhat.com>
- xinetd shouldn't wait on tftp (which forks) (#23923).
* Sat Jan 6 2001 Jeff Johnson <jbj@redhat.com>
- 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 <nalin@redhat.com>
- default to being disabled
* Thu Aug 17 2000 Jeff Johnson <jbj@redhat.com>
- correct group.
* Tue Jul 25 2000 Nalin Dahyabhai <nalin@redhat.com>
- change user from root to nobody
* Sat Jul 22 2000 Jeff Johnson <jbj@redhat.com>
- update to tftp-hpa-0.14 (#14003).
- add server_args (#14003).
- remove -D_BSD_SOURCE (#14003).
* Fri Jul 21 2000 Nalin Dahyabhai <nalin@redhat.com>
- cook up an xinetd config file for tftpd
* Wed Jul 12 2000 Prospector <bugzilla@redhat.com>
- automatic rebuild
* Sun Jun 18 2000 Jeff Johnson <jbj@redhat.com>
- FHS packaging.
- update to 0.17.
* Fri May 5 2000 Matt Wilson <msw@redhat.com>
- use _BSD_SOURCE for hpa's tftpd so we get BSD signal semantics.
* Fri Feb 11 2000 Bill Nottingham <notting@redhat.com>
- fix description
* Wed Feb 9 2000 Jeff Johnson <jbj@redhat.com>
- compress man pages (again).
* Wed Feb 02 2000 Cristian Gafton <gafton@redhat.com>
- man pages are compressed
- fix description and summary
* Tue Jan 4 2000 Bill Nottingham <notting@redhat.com>
- split client and server
* Tue Dec 21 1999 Jeff Johnson <jbj@redhat.com>
- update to 0.16.
* Sat Aug 28 1999 Jeff Johnson <jbj@redhat.com>
- update to 0.15.
* Wed Apr 7 1999 Jeff Johnson <jbj@redhat.com>
- tftpd should truncate file when overwriting (#412)
* Sun Mar 21 1999 Cristian Gafton <gafton@redhat.com>
- auto rebuild in the new build environment (release 22)
* Mon Mar 15 1999 Jeff Johnson <jbj@redhat.com>
- compile for 6.0.
* Fri Aug 7 1998 Jeff Johnson <jbj@redhat.com>
- build root
* Mon Apr 27 1998 Prospector System <bugs@redhat.com>
- translations modified for de, fr, tr
* Mon Sep 22 1997 Erik Troan <ewt@redhat.com>
- added check for getpwnam() failure
* Tue Jul 15 1997 Erik Troan <ewt@redhat.com>
- initial build

28
tftp/Makefile Normal file
View File

@ -0,0 +1,28 @@
SRCROOT = ..
VERSION = $(shell cat ../version)
-include ../MCONFIG
include ../MRULES
OBJS = tftp.$(O) main.$(O)
all: tftp$(X) tftp.1
tftp$(X): $(OBJS)
$(CC) $(LDFLAGS) $^ $(TFTP_LIBS) -o $@
$(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) tftp$(X) $(INSTALLROOT)$(BINDIR)
$(INSTALL_DATA) tftp.1 $(INSTALLROOT)$(MANDIR)/man1
clean:
rm -f *.o *.obj *.exe tftp tftp.1
distclean: clean
rm -f *~

40
tftp/extern.h Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 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.
*/
#ifndef RECVFILE_H
#define RECVFILE_H
void tftp_recvfile(int, const char *, const char *);
void tftp_sendfile(int, const char *, const char *);
#endif

945
tftp/main.c Normal file
View File

@ -0,0 +1,945 @@
/*
* 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 "common/tftpsubs.h"
/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
/*
* TFTP User Program -- Command Interface.
*/
#include <sys/file.h>
#include <ctype.h>
#ifdef WITH_READLINE
#include <readline/readline.h>
#ifdef HAVE_READLINE_HISTORY_H
#include <readline/history.h>
#endif
#endif
#include "extern.h"
#define TIMEOUT 5 /* secs between rexmt's */
#define LBUFLEN 200 /* size of input buffer */
struct modes {
const char *m_name;
const char *m_mode;
int m_openflags;
};
static const struct modes modes[] = {
{"netascii", "netascii", O_TEXT},
{"ascii", "netascii", O_TEXT},
{"octet", "octet", O_BINARY},
{"binary", "octet", O_BINARY},
{"image", "octet", O_BINARY},
{0, 0, 0}
};
#define MODE_OCTET (&modes[2])
#define MODE_NETASCII (&modes[0])
#define MODE_DEFAULT MODE_NETASCII
#ifdef HAVE_IPV6
int ai_fam = AF_UNSPEC;
int ai_fam_sock = AF_UNSPEC;
#else
int ai_fam = AF_INET;
int ai_fam_sock = AF_INET;
#endif
union sock_addr peeraddr;
int f = -1;
u_short port;
int trace;
int verbose;
int literal;
int connected;
const struct modes *mode;
#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;
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 setliteral(int, char **);
static void command(void);
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 **);
};
struct cmd cmdtab[] = {
{"connect",
"connect to remote tftp",
setpeer},
{"mode",
"set file transfer mode",
modecmd},
{"put",
"send file",
put},
{"get",
"receive file",
get},
{"quit",
"exit tftp",
quit},
{"verbose",
"toggle verbose mode",
setverbose},
{"trace",
"toggle packet tracing",
settrace},
{"literal",
"toggle literal mode, ignore ':' in file name",
setliteral},
{"status",
"show current status",
status},
{"binary",
"set mode to octet",
setbinary},
{"ascii",
"set mode to netascii",
setascii},
{"rexmt",
"set per-packet transmission timeout",
setrexmt},
{"timeout",
"set total retransmission timeout",
settimeout},
{"?",
"print help information",
help},
{"help",
"print help information",
help},
{0, 0, 0}
};
struct cmd *getcmd(char *);
char *tail(char *);
char *xstrdup(const char *);
const char *program;
static inline void usage(int errcode)
{
fprintf(stderr,
#ifdef HAVE_IPV6
"Usage: %s [-4][-6][-v][-l][-m mode] [host [port]] [-c command]\n",
#else
"Usage: %s [-v][-l][-m mode] [host [port]] [-c command]\n",
#endif
program);
exit(errcode);
}
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";
}
bsd_signal(SIGINT, intr);
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)
{
#ifdef WITH_READLINE
char *eline;
int len, elen;
len = strlen(partial);
eline = readline(mprompt);
if (!eline)
exit(0); /* EOF */
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 */
#endif
}
void setpeer(int argc, char *argv[])
{
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;
}
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;
}
void modecmd(int argc, char *argv[])
{
const struct modes *p;
const char *sep;
if (argc < 2) {
printf("Using %s mode to transfer files.\n", mode->m_mode);
return;
}
if (argc == 2) {
for (p = modes; p->m_name; p++)
if (strcmp(argv[1], p->m_name) == 0)
break;
if (p->m_name) {
settftpmode(p);
return;
}
printf("%s: unknown mode\n", argv[1]);
/* drop through and print usage message */
}
printf("usage: %s [", argv[0]);
sep = " ";
for (p = modes; p->m_name; p++) {
printf("%s%s", sep, p->m_name);
if (*sep == ' ')
sep = " | ";
}
printf(" ]\n");
return;
}
void setbinary(int argc, char *argv[])
{
(void)argc;
(void)argv; /* Quiet unused warning */
settftpmode(MODE_OCTET);
}
void setascii(int argc, char *argv[])
{
(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[])
{
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 (!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)
{
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[])
{
int fd;
int n;
char *cp;
char *src;
if (argc < 2) {
getmoreargs("get ", "(files) ");
makeargv();
argc = margc;
argv = margv;
}
if (argc < 2) {
getusage(argv[0]);
return;
}
if (!connected) {
for (n = 1; n < argc; n++)
if (literal || strchr(argv[n], ':') == 0) {
getusage(argv[0]);
return;
}
}
for (n = 1; n < argc; n++) {
src = strchr(argv[n], ':');
if (literal || src == NULL)
src = argv[n];
else {
int err;
*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)
{
printf("usage: %s host:file host:file ... file, or\n", s);
printf(" %s file file ... file if connected\n", s);
}
int rexmtval = TIMEOUT;
void setrexmt(int argc, char *argv[])
{
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;
}
int maxtimeout = 5 * TIMEOUT;
void settimeout(int argc, char *argv[])
{
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;
}
void setliteral(int argc, char *argv[])
{
(void)argc;
(void)argv; /* Quiet unused warning */
literal = !literal;
printf("Literal mode %s.\n", literal ? "on" : "off");
}
void status(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 Literal: %s\n", mode->m_mode,
verbose ? "on" : "off", trace ? "on" : "off",
literal ? "on" : "off");
printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
rexmtval, maxtimeout);
}
void intr(int sig)
{
(void)sig; /* Quiet unused warning */
bsd_signal(SIGALRM, SIG_IGN);
alarm(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)
{
struct cmd *c;
for (;;) {
#ifdef WITH_READLINE
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;
}
}
#endif
if ((line[0] == 0) || (line[0] == '\n'))
continue;
#ifdef WITH_READLINE
#ifdef HAVE_READLINE_HISTORY_H
add_history(line);
#endif
#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);
}
}
struct cmd *getcmd(char *name)
{
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);
}
/*
* Slice a string up into argc/argv.
*/
static void makeargv(void)
{
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;
}
void quit(int argc, char *argv[])
{
(void)argc;
(void)argv; /* Quiet unused warning */
exit(0);
}
/*
* Help command.
*/
void help(int argc, char *argv[])
{
struct cmd *c;
printf("%s\n", VERSION);
if (argc == 1) {
printf("Commands may be abbreviated. Commands are:\n\n");
for (c = cmdtab; c->name; c++)
printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
return;
}
while (--argc > 0) {
char *arg;
arg = *++argv;
c = getcmd(arg);
if (c == (struct cmd *)-1)
printf("?Ambiguous help command %s\n", arg);
else if (c == (struct cmd *)0)
printf("?Invalid help command %s\n", arg);
else
printf("%s\n", c->help);
}
}
void settrace(int argc, char *argv[])
{
(void)argc;
(void)argv; /* Quiet unused warning */
trace = !trace;
printf("Packet tracing %s.\n", trace ? "on" : "off");
}
void setverbose(int argc, char *argv[])
{
(void)argc;
(void)argv; /* Quiet unused warning */
verbose = !verbose;
printf("Verbose mode %s.\n", verbose ? "on" : "off");
}

207
tftp/tftp.1.in Normal file
View File

@ -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 <hpa@zytor.com>. 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).

425
tftp/tftp.c Normal file
View File

@ -0,0 +1,425 @@
/*
* 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 "common/tftpsubs.h"
/*
* TFTP User Program -- Protocol Machines
*/
#include "extern.h"
extern union sock_addr peeraddr; /* filled in by main */
extern int f; /* the opened socket */
extern int trace;
extern int verbose;
extern int rexmtval;
extern int maxtimeout;
#define PKTSIZE SEGSIZE+4
char ackbuf[PKTSIZE];
int timeout;
sigjmp_buf toplevel;
sigjmp_buf timeoutbuf;
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);
static void stopclock(void);
static void timer(int);
static void tpacket(const char *, struct tftphdr *, int);
/*
* Send the requested file.
*/
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;
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;
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, NULL);
break;
}
dp->th_opcode = htons((u_short) DATA);
dp->th_block = htons((u_short) block);
}
timeout = 0;
(void)sigsetjmp(timeoutbuf, 1);
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, 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;
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;
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, &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_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)
{
char *cp;
tp->th_opcode = htons((u_short) request);
cp = (char *)&(tp->th_stuff);
strcpy(cp, name);
cp += strlen(name);
*cp++ = '\0';
strcpy(cp, mode);
cp += strlen(mode);
*cp++ = '\0';
return (cp - (char *)tp);
}
static const char *const errmsgs[] = {
"Undefined error code", /* 0 - EUNDEF */
"File not found", /* 1 - ENOTFOUND */
"Access denied", /* 2 - EACCESS */
"Disk full or allocation exceeded", /* 3 - ENOSPACE */
"Illegal TFTP operation", /* 4 - EBADOP */
"Unknown transfer ID", /* 5 - EBADID */
"File already exists", /* 6 - EEXISTS */
"No such user", /* 7 - ENOUSER */
"Failure to negotiate RFC2347 options" /* 8 - EOPTNEG */
};
#define ERR_CNT (sizeof(errmsgs)/sizeof(const char *))
/*
* Send a nak packet (error message).
* Error code passed in is one of the
* standard TFTP codes, or a UNIX errno
* offset by 100.
*/
static void nak(int error, const char *msg)
{
struct tftphdr *tp;
int length;
tp = (struct tftphdr *)ackbuf;
tp->th_opcode = htons((u_short) ERROR);
tp->th_code = htons((u_short) error);
if (error >= 100) {
/* This is a Unix errno+100 */
if (!msg)
msg = strerror(error - 100);
error = EUNDEF;
} else {
if ((unsigned)error >= ERR_CNT)
error = EUNDEF;
if (!msg)
msg = errmsgs[error];
}
tp->th_code = htons((u_short) error);
length = strlen(msg) + 1;
memcpy(tp->th_msg, msg, length);
length += 4; /* Add space for header */
if (trace)
tpacket("sent", tp, length);
if (sendto(f, ackbuf, length, 0, &peeraddr.sa,
SOCKLEN(&peeraddr)) != length)
perror("nak");
}
static void tpacket(const char *s, struct tftphdr *tp, int n)
{
static const char *opcodes[] =
{ "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
char *cp, *file;
u_short op = ntohs((u_short) tp->th_opcode);
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("<file=%s, mode=%s>\n", file, cp + 1);
break;
case DATA:
printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
break;
case ACK:
printf("<block=%d>\n", ntohs(tp->th_block));
break;
case ERROR:
printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
break;
}
}
struct timeval tstart;
struct timeval tstop;
static void startclock(void)
{
(void)gettimeofday(&tstart, NULL);
}
static void stopclock(void)
{
(void)gettimeofday(&tstop, NULL);
}
static void printstats(const char *direction, unsigned long amount)
{
double delta;
delta = (tstop.tv_sec + (tstop.tv_usec / 100000.0)) -
(tstart.tv_sec + (tstart.tv_usec / 100000.0));
if (verbose) {
printf("%s %lu bytes in %.1f seconds", direction, amount, delta);
printf(" [%.0f bit/s]", (amount * 8.) / delta);
putchar('\n');
}
}
static void timer(int sig)
{
int save_errno = errno;
(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);
}

29
tftpd/Makefile Normal file
View File

@ -0,0 +1,29 @@
SRCROOT = ..
VERSION = $(shell cat ../version)
-include ../MCONFIG
include ../MRULES
OBJS = tftpd.$(O) recvfrom.$(O) misc.$(O) $(TFTPDOBJS)
all: tftpd$(X) tftpd.8
tftpd$(X): $(OBJS)
$(CC) $(LDFLAGS) $^ $(TFTPD_LIBS) -o $@
$(OBJS): ../common/tftpsubs.h
tftpd.8: tftpd.8.in ../version
sed -e 's/@@VERSION@@/$(VERSION)/g' < $< > $@
install: all
mkdir -p $(INSTALLROOT)$(SBINDIR) $(INSTALLROOT)$(MANDIR)/man8
$(INSTALL_PROGRAM) tftpd$(X) $(INSTALLROOT)$(SBINDIR)/in.tftpd
$(INSTALL_DATA) tftpd.8 $(INSTALLROOT)$(MANDIR)/man8/in.tftpd.8
cd $(INSTALLROOT)$(MANDIR)/man8 && $(LN_S) -f in.tftpd.8 tftpd.8
clean:
rm -f *.o *.obj *.exe tftpd tftpsubs.c tftpsubs.h tftpd.8
distclean: clean
rm -f *~

68
tftpd/misc.c Normal file
View File

@ -0,0 +1,68 @@
/* ----------------------------------------------------------------------- *
*
* 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/.
*
* ----------------------------------------------------------------------- */
/*
* misc.c
*
* Minor help routines.
*/
#include "config.h" /* Must be included first! */
#include <syslog.h>
#include "tftpd.h"
/*
* Set the signal handler and flags. Basically a user-friendly
* wrapper around sigaction().
*/
void set_signal(int signum, void (*handler) (int), 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);
}
}
/*
* malloc() that syslogs an error message and bails if it fails.
*/
void *tfmalloc(size_t size)
{
void *p = malloc(size);
if (!p) {
syslog(LOG_ERR, "malloc: %m");
exit(EX_OSERR);
}
return p;
}
/*
* strdup() that does the equivalent
*/
char *tfstrdup(const char *str)
{
char *p = strdup(str);
if (!p) {
syslog(LOG_ERR, "strdup: %m");
exit(EX_OSERR);
}
return p;
}

267
tftpd/recvfrom.c Normal file
View File

@ -0,0 +1,267 @@
/* ----------------------------------------------------------------------- *
*
* 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
* http://www.openbsd.org/.
*
* ----------------------------------------------------------------------- */
/*
* recvfrom.c
*
* Emulate recvfrom() using recvmsg(), but try to capture the local address
* since some TFTP clients consider it an error to get the reply from another
* IP address than the request was sent to.
*
*/
#include "config.h" /* Must be included first! */
#include "common/tftpsubs.h"
#include "recvfrom.h"
#ifdef HAVE_MACHINE_PARAM_H
#include <machine/param.h> /* Needed on some versions of FreeBSD */
#endif
#if defined(HAVE_RECVMSG) && defined(HAVE_MSGHDR_MSG_CONTROL)
#include <sys/uio.h>
#ifdef IP_PKTINFO
# ifndef HAVE_STRUCT_IN_PKTINFO
# ifdef __linux__
/* Assume this version of glibc simply lacks the definition */
struct in_pktinfo {
int ipi_ifindex;
struct in_addr ipi_spec_dst;
struct in_addr ipi_addr;
};
# else
# undef IP_PKTINFO /* No definition, no way to get it */
# endif
# endif
#endif
#ifndef CMSG_LEN
# define CMSG_LEN(size) (sizeof(struct cmsghdr) + (size))
#endif
#ifndef CMSG_SPACE
# define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size))
#endif
/*
* Check to see if this is a valid local address, meaning that we can
* legally bind to it.
*/
static int address_is_local(const union sock_addr *addr)
{
union sock_addr sa1, sa2;
int sockfd = -1;
int e;
int rv = 0;
socklen_t addrlen;
memcpy(&sa1, addr, sizeof sa1);
/* Multicast or universal broadcast address? */
if (sa1.sa.sa_family == AF_INET) {
if (ntohl(sa1.si.sin_addr.s_addr) >= (224UL << 24))
return 0;
sa1.si.sin_port = 0; /* Any port */
}
#ifdef HAVE_IPV6
else if (sa1.sa.sa_family == AF_INET6) {
if (IN6_IS_ADDR_MULTICAST(&sa1.s6.sin6_addr))
return 0;
sa1.s6.sin6_port = 0; /* Any port */
}
#endif
else
return 0;
sockfd = socket(sa1.sa.sa_family, SOCK_DGRAM, 0);
if (sockfd < 0)
goto err;
if (bind(sockfd, &sa1.sa, SOCKLEN(&sa1)))
goto err;
addrlen = SOCKLEN(addr);
if (getsockname(sockfd, (struct sockaddr *)&sa2, &addrlen))
goto err;
if (sa1.sa.sa_family != sa2.sa.sa_family)
goto err;
if (sa2.sa.sa_family == AF_INET)
rv = sa1.si.sin_addr.s_addr == sa2.si.sin_addr.s_addr;
#ifdef HAVE_IPV6
else if (sa2.sa.sa_family == AF_INET6)
rv = IN6_ARE_ADDR_EQUAL(&sa1.s6.sin6_addr, &sa2.s6.sin6_addr);
#endif
else
rv = 0;
err:
e = errno;
if (sockfd >= 0)
close(sockfd);
errno = e;
return rv;
}
int
myrecvfrom(int s, void *buf, int len, unsigned int flags,
struct sockaddr *from, socklen_t * fromlen,
union sock_addr *myaddr)
{
struct msghdr msg;
struct iovec iov;
int n;
struct cmsghdr *cmptr;
union {
struct cmsghdr cm;
#ifdef IP_PKTINFO
char control[CMSG_SPACE(sizeof(struct in_addr)) +
CMSG_SPACE(sizeof(struct in_pktinfo))];
#else
char control[CMSG_SPACE(sizeof(struct in_addr))];
#endif
#ifdef HAVE_IPV6
#ifdef HAVE_STRUCT_IN6_PKTINFO
char control6[CMSG_SPACE(sizeof(struct in6_addr)) +
CMSG_SPACE(sizeof(struct in6_pktinfo))];
#else
char control6[CMSG_SPACE(sizeof(struct in6_addr))];
#endif
#endif
} control_un;
int on = 1;
#ifdef IP_PKTINFO
struct in_pktinfo pktinfo;
#endif
#ifdef HAVE_STRUCT_IN6_PKTINFO
struct in6_pktinfo pktinfo6;
#endif
/* Try to enable getting the return address */
#ifdef IP_RECVDSTADDR
if (from->sa_family == AF_INET)
setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
#endif
#ifdef IP_PKTINFO
if (from->sa_family == AF_INET)
setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
#endif
#ifdef HAVE_IPV6
#ifdef IPV6_RECVPKTINFO
if (from->sa_family == AF_INET6)
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;
msg.msg_name = from;
msg.msg_namelen = *fromlen;
iov.iov_base = buf;
iov.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(*myaddr));
myaddr->sa.sa_family = from->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_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_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 &&
(cmptr->cmsg_type == IPV6_RECVPKTINFO ||
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
}
/* 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
}
}
return n;
}
#else /* pointless... */
int
myrecvfrom(int s, void *buf, int len, unsigned int flags,
struct sockaddr *from, socklen_t * fromlen,
union sock_addr *myaddr)
{
/* There is no way we can get the local address, fudge it */
bzero(myaddr, sizeof(*myaddr));
myaddr->sa.sa_family = from->sa_family;
sa_set_port(myaddr, htons(IPPORT_TFTP));
return recvfrom(s, buf, len, flags, from, fromlen);
}
#endif

23
tftpd/recvfrom.h Normal file
View File

@ -0,0 +1,23 @@
/* ----------------------------------------------------------------------- *
*
* 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
* http://www.openbsd.org/.
*
* ----------------------------------------------------------------------- */
/*
* recvfrom.h
*
* Header for recvfrom substitute
*
*/
#include "config.h"
int
myrecvfrom(int s, void *buf, int len, unsigned int flags,
struct sockaddr *from, socklen_t *fromlen,
union sock_addr *myaddr);

435
tftpd/remap.c Normal file
View File

@ -0,0 +1,435 @@
/* ----------------------------------------------------------------------- *
*
* 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/.
*
* ----------------------------------------------------------------------- */
/*
* remap.c
*
* Perform regular-expression based filename remapping.
*/
#include "config.h" /* Must be included first! */
#include <ctype.h>
#include <syslog.h>
#include <regex.h>
#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 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 */
struct rule {
struct rule *next;
int nrule;
int rule_flags;
char rule_mode;
regex_t rx;
const char *pattern;
};
static int xform_null(int c)
{
return c;
}
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. */
static int genmatchstring(char *string, const char *pattern,
const char *input, const regmatch_t * pmatch,
match_pattern_callback macrosub)
{
int (*xform) (int) = xform_null;
int len = 0;
int n, mlen, sublen;
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) {
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 + 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++;
}
}
/* Copy section after match */
if (string) {
memcpy(string, input + pmatch[0].rm_eo, endbytes);
string[endbytes] = '\0';
}
return len;
}
/*
* 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;
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++;
}
*buf = '\0';
*str = p;
return len;
}
/* Parse a line into a set of instructions */
static int parseline(char *line, struct rule *r, int lineno)
{
char buffer[MAXLINE];
char *p;
int rv;
int rxflags = REG_EXTENDED;
static int nrule;
memset(r, 0, sizeof *r);
r->nrule = nrule;
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 '~':
r->rule_flags |= RULE_INVERSE;
break;
case 'G':
case 'P':
r->rule_mode = *p;
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_INVERSE | RULE_REWRITE)) ==
(RULE_INVERSE | RULE_REWRITE)) {
syslog(LOG_ERR, "r rules cannot be inverted, 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 */
}
/* Read the rewrite pattern, if any */
if (readescstring(buffer, &line)) {
r->pattern = tfstrdup(buffer);
} else {
r->pattern = "";
}
nrule++;
return 1; /* Rule found */
}
/* Read a rule file */
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;
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 */
if (err) {
/* Bail on error, we have already logged an error message */
exit(EX_CONFIG);
}
return first_rule;
}
/* Destroy a rule file data structure */
void freerules(struct rule *r)
{
struct rule *next;
while (r) {
next = r->next;
regfree(&r->rx);
/* "" patterns aren't allocated by malloc() */
if (r->pattern && *r->pattern)
free((void *)r->pattern);
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,
char mode, 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;
/* Default error */
*errmsg = "Remap table failure";
if (verbosity >= 3) {
syslog(LOG_INFO, "remap: input: %s", current);
}
for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) {
if (ruleptr->rule_mode && ruleptr->rule_mode != mode)
continue; /* Rule not applicable, try next */
if (!deadman--) {
syslog(LOG_WARNING,
"remap: Breaking loop, input = %s, last = %s", input,
current);
free(current);
return NULL; /* Did not terminate! */
}
do {
if (regexec(&ruleptr->rx, current, 10, pmatch, 0) ==
(ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0)) {
/* Match on this rule */
was_match = 1;
if (ruleptr->rule_flags & RULE_INVERSE) {
/* No actual match, so clear out the pmatch array */
int i;
for (i = 0; i < 10; i++)
pmatch[i].rm_so = pmatch[i].rm_eo = -1;
}
if (ruleptr->rule_flags & RULE_ABORT) {
if (verbosity >= 3) {
syslog(LOG_INFO, "remap: rule %d: abort: %s",
ruleptr->nrule, current);
}
if (ruleptr->pattern[0]) {
/* Custom error message */
len =
genmatchstring(NULL, ruleptr->pattern, current,
pmatch, macrosub);
newstr = tfmalloc(len + 1);
genmatchstring(newstr, ruleptr->pattern, current,
pmatch, macrosub);
*errmsg = newstr;
} else {
*errmsg = NULL;
}
free(current);
return (NULL);
}
if (ruleptr->rule_flags & RULE_REWRITE) {
len = genmatchstring(NULL, ruleptr->pattern, current,
pmatch, macrosub);
newstr = tfmalloc(len + 1);
genmatchstring(newstr, ruleptr->pattern, current,
pmatch, macrosub);
free(current);
current = newstr;
if (verbosity >= 3) {
syslog(LOG_INFO, "remap: rule %d: rewrite: %s",
ruleptr->nrule, current);
}
}
} 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;
}

42
tftpd/remap.h Normal file
View File

@ -0,0 +1,42 @@
/* ----------------------------------------------------------------------- *
*
* 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/.
*
* ----------------------------------------------------------------------- */
/*
* remap.h
*
* Prototypes for regular-expression based filename remapping.
*/
#ifndef TFTPD_REMAP_H
#define TFTPD_REMAP_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 *);
/* Destroy a rule file data structure */
void freerules(struct rule *);
/* Execute a rule set on a string; returns a malloc'd new string. */
char *rewrite_string(const char *, const struct rule *, char,
match_pattern_callback, const char **);
#endif /* WITH_REGEX */
#endif /* TFTPD_REMAP_H */

33
tftpd/sample.rules Normal file
View File

@ -0,0 +1,33 @@
#
# Sample rule file for the -m (remapping option)
#
# This file has three fields: operation, regex, remapping
#
# The operation is a combination of the following letters:
#
# r - rewrite the matched string with the remapping pattern
# i - case-insensitive matching
# g - repeat until no match (used with "r")
# e - exit (with success) if we match this pattern, do not process
# subsequent rules
# s - start over from the first rule if we match this pattern
# a - abort (refuse the request) if we match this rule
# G - this rule applies to TFTP GET requests only
# P - this rule applies to TFTP PUT requests only
#
# The regex is a regular expression in the style of egrep(1).
#
# The remapping is a pattern, all characters are verbatim except \
# \0 copies the full string that matched the regex
# \1..\9 copies the 9 first (..) expressions in the regex
# \\ is an escaped \
#
# "#" begins a comment, unless \-escaped
#
ri ^[a-z]: # Remove "drive letters"
rg \\ / # Convert backslashes to slashes
rg \# @ # Convert hash marks to @ signs
rg /../ /..no../ # Convert /../ to /..no../
e ^ok/ # These are always ok
r ^[^/] /tftpboot/\0 # Convert non-absolute files
a \.pvt$ # Reject requests for private files

423
tftpd/tftpd.8.in Normal file
View File

@ -0,0 +1,423 @@
.\" -*- 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 "14 September 2009" "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\-\-mapfile\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\-\-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 \-\-mapfile
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 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 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 ~
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 the
.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
(e.g. 192.0.2.169).
.TP
\fB\\x\fP
The IP address of the requesting host, in hexadecimal notation
(e.g. C00002A9).
.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 whole names are prefixed by one of the given directories. If
possible, it is recommended that the
.B \-\-secure
flag is used to set up a chroot() environment for the server to run in
once a connection has been set up.
.PP
Finally, the filename remapping
.RB ( \-\-mapfile
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 <hpa@zytor.com>. 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).

1795
tftpd/tftpd.c Normal file

File diff suppressed because it is too large Load Diff

26
tftpd/tftpd.h Normal file
View File

@ -0,0 +1,26 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 2001 H. Peter Anvin - All Rights Reserved
*
* This program is free software available under the same license
* as the "OpenBSD" operating system, distributed at
* http://www.openbsd.org/.
*
* ----------------------------------------------------------------------- */
/*
* tftpd.h
*
* Prototypes for various functions that are part of the tftpd server.
*/
#ifndef TFTPD_TFTPD_H
#define TFTPD_TFTPD_H
void set_signal(int, void (*)(int), int);
void *tfmalloc(size_t);
char *tfstrdup(const char *);
extern int verbosity;
#endif

1
version Normal file
View File

@ -0,0 +1 @@
5.2