From 09f975fa4c82f89cdae255845599399e61c0c4ad Mon Sep 17 00:00:00 2001 From: hpa Date: Sun, 26 Sep 1999 06:32:41 +0000 Subject: [PATCH] Initial revision --- MCONFIG.in | 31 + MRULES | 22 + Makefile | 25 + README | 24 + aclocal.m4 | 67 ++ configure | 1547 +++++++++++++++++++++++++++++++++++++++++++++++ configure.in | 28 + install-sh | 238 ++++++++ tftp/Makefile | 21 + tftp/extern.h | 42 ++ tftp/main.c | 704 +++++++++++++++++++++ tftp/tftp.1 | 177 ++++++ tftp/tftp.c | 453 ++++++++++++++ tftp/tftpsubs.c | 271 +++++++++ tftp/tftpsubs.h | 57 ++ tftpd/Makefile | 27 + tftpd/tftpd.8 | 141 +++++ tftpd/tftpd.c | 821 +++++++++++++++++++++++++ 18 files changed, 4696 insertions(+) create mode 100644 MCONFIG.in create mode 100644 MRULES create mode 100644 Makefile create mode 100644 README create mode 100644 aclocal.m4 create mode 100755 configure create mode 100644 configure.in create mode 100755 install-sh create mode 100644 tftp/Makefile create mode 100644 tftp/extern.h create mode 100644 tftp/main.c create mode 100644 tftp/tftp.1 create mode 100644 tftp/tftp.c create mode 100644 tftp/tftpsubs.c create mode 100644 tftp/tftpsubs.h create mode 100644 tftpd/Makefile create mode 100644 tftpd/tftpd.8 create mode 100644 tftpd/tftpd.c diff --git a/MCONFIG.in b/MCONFIG.in new file mode 100644 index 0000000..f2dac70 --- /dev/null +++ b/MCONFIG.in @@ -0,0 +1,31 @@ +# Prefixes +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +# Directory for user binaries +BINDIR = @bindir@ + +# Man page tree +MANDIR = @mandir@ + +# System binaries +SBINDIR = @sbindir@ + +# Install into chroot area +INSTALLROOT = + +# Install program +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +# Compiler and compiler flags +CC = @CC@ +CFLAGS = @CFLAGS@ + +# Link flags +LDFLAGS = @LDFLAGS@ + +# Libraries +LIBS = @LIBS@ + diff --git a/MRULES b/MRULES new file mode 100644 index 0000000..decd202 --- /dev/null +++ b/MRULES @@ -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 $@ $< + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..004e9fd --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +# 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 = tftp tftpd + +%.build: + $(MAKE) -C $(patsubst %.build, %, $@) + +%.install: + $(MAKE) -C $(patsubst %.install, %, $@) install + +%.clean: + $(MAKE) -C $(patsubst %.clean, %, $@) clean + +%.spotless: + $(MAKE) -C $(patsubst %.spotless, %, $@) spotless + +all: $(patsubst %, %.build, $(SUB)) + +install: $(patsubst %, %.install, $(SUB)) + +clean: $(patsubst %, %.clean, $(SUB)) + +spotless: $(patsubst %, %.spotless, $(SUB)) + rm -f MCONFIG config.* *~ diff --git a/README b/README new file mode 100644 index 0000000..2428de9 --- /dev/null +++ b/README @@ -0,0 +1,24 @@ +This is tftp-hpa-0.12; this version was put out by H. Peter Anvin +. + +This is a conglomerate of a number of versions of the BSD TFTP code, +ported to Linux, although it should work on mostly any POSIX-compliant +OS with sockets. + +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, and converting to using autoconf for setup was my own code, as +are any bugs introduced in this merge. diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..1a3cfef --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,67 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_CFLAGS() +dnl +dnl Attempt to add the given option to CFLAGS, if it doesn't break compilation +dnl -------------------------------------------------------------------------- +AC_DEFUN(PA_ADD_CFLAGS, +[AC_MSG_CHECKING([if $CC accepts $1]) + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS $1" + AC_TRY_COMPILE([#include ], + [printf("Hello, World!\n");], + AC_MSG_RESULT([yes]), + AC_MSG_RESULT([no]) + CFLAGS="$pa_add_cflags__old_cflags")]) + +dnl -------------------------------------------------------------------------- +dnl PA_BSD_SIGNAL() +dnl +dnl Test for BSD signal semantics. Set shell variable BSD_SIGNAL=1 if OK. +dnl May modify CFLAGS and/or LIBS. +dnl -------------------------------------------------------------------------- +AC_DEFUN(PA_CHECK_BSD_SIGNAL, +[AC_TRY_RUN([ +#include +#include +int count=0; +handle() { count++; } +int main() { + int pid=getpid(); + signal(SIGINT, handle); + kill(pid,SIGINT); + kill(pid,SIGINT); + kill(pid,SIGINT); + if (count!=3) return 1; + return 0; +} +], BSD_SIGNAL=1)]) + +AC_DEFUN(PA_BSD_SIGNAL, +[AC_MSG_CHECKING([for BSD signal semantics]) +PA_CHECK_BSD_SIGNAL() +if test -z "$BSD_SIGNAL"; then + AC_MSG_RESULT([no]) + AC_MSG_CHECKING([if -D__USE_BSD_SIGNAL helps]) + pa_bsd_signal__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -D__USE_BSD_SIGNAL" + PA_CHECK_BSD_SIGNAL() + if test -z "$BSD_SIGNAL"; then + AC_MSG_RESULT([no]) + CFLAGS="$pa_bsd_signal__old_cflags" + AC_MSG_CHECKING([if -lbsd helps]) + pa_bsd_signal__old_libs="$LIBS" + LIBS="$LIBS -lbsd" + PA_CHECK_BSD_SIGNAL() + if test -z "$BSD_SIGNAL"; then + AC_MSG_RESULT([no]) + LIBS="$pa_bsd_signal__old_libs" + else + AC_MSG_RESULT([yes]) + fi + else + AC_MSG_RESULT([yes]) + fi +else + AC_MSG_RESULT([yes]) +fi +]) diff --git a/configure b/configure new file mode 100755 index 0000000..2091ed2 --- /dev/null +++ b/configure @@ -0,0 +1,1547 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.12 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.12" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=MCONFIG.in + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:526: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:555: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + ac_prog_rejected=no + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:603: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:637: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:642: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes + ac_test_CFLAGS="${CFLAGS+set}" + ac_save_CFLAGS="$CFLAGS" + CFLAGS= + echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:666: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 + if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" + elif test $ac_cv_prog_cc_g = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-O2" + fi +else + GCC= + test "${CFLAGS+set}" = set || CFLAGS="-g" +fi + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:694: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:748: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking for inline""... $ac_c" 1>&6 +echo "configure:769: checking for inline" >&5 +if eval "test \"`echo '$''{'ac_cv_c_inline'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_inline=$ac_kw; break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +done + +fi + +echo "$ac_t""$ac_cv_c_inline" 1>&6 +case "$ac_cv_c_inline" in + inline | yes) ;; + no) cat >> confdefs.h <<\EOF +#define inline +EOF + ;; + *) cat >> confdefs.h <&6 +echo "configure:810: checking if $CC accepts -Wall" >&5 + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Wall" + cat > conftest.$ac_ext < +int main() { +printf("Hello, World!\n"); +; return 0; } +EOF +if { (eval echo configure:821: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 + CFLAGS="$pa_add_cflags__old_cflags" +fi +rm -f conftest* +echo $ac_n "checking if $CC accepts -W""... $ac_c" 1>&6 +echo "configure:833: checking if $CC accepts -W" >&5 + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -W" + cat > conftest.$ac_ext < +int main() { +printf("Hello, World!\n"); +; return 0; } +EOF +if { (eval echo configure:844: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 + CFLAGS="$pa_add_cflags__old_cflags" +fi +rm -f conftest* +echo $ac_n "checking if $CC accepts -Wpointer-arith""... $ac_c" 1>&6 +echo "configure:856: checking if $CC accepts -Wpointer-arith" >&5 + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Wpointer-arith" + cat > conftest.$ac_ext < +int main() { +printf("Hello, World!\n"); +; return 0; } +EOF +if { (eval echo configure:867: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 + CFLAGS="$pa_add_cflags__old_cflags" +fi +rm -f conftest* +echo $ac_n "checking if $CC accepts -Wbad-function-cast""... $ac_c" 1>&6 +echo "configure:879: checking if $CC accepts -Wbad-function-cast" >&5 + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Wbad-function-cast" + cat > conftest.$ac_ext < +int main() { +printf("Hello, World!\n"); +; return 0; } +EOF +if { (eval echo configure:890: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 + CFLAGS="$pa_add_cflags__old_cflags" +fi +rm -f conftest* +echo $ac_n "checking if $CC accepts -Wcast-equal""... $ac_c" 1>&6 +echo "configure:902: checking if $CC accepts -Wcast-equal" >&5 + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Wcast-equal" + cat > conftest.$ac_ext < +int main() { +printf("Hello, World!\n"); +; return 0; } +EOF +if { (eval echo configure:913: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 + CFLAGS="$pa_add_cflags__old_cflags" +fi +rm -f conftest* +echo $ac_n "checking if $CC accepts -Wstrict-prototypes""... $ac_c" 1>&6 +echo "configure:925: checking if $CC accepts -Wstrict-prototypes" >&5 + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Wstrict-prototypes" + cat > conftest.$ac_ext < +int main() { +printf("Hello, World!\n"); +; return 0; } +EOF +if { (eval echo configure:936: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 + CFLAGS="$pa_add_cflags__old_cflags" +fi +rm -f conftest* +echo $ac_n "checking if $CC accepts -Wmissing-prototypes""... $ac_c" 1>&6 +echo "configure:948: checking if $CC accepts -Wmissing-prototypes" >&5 + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Wmissing-prototypes" + cat > conftest.$ac_ext < +int main() { +printf("Hello, World!\n"); +; return 0; } +EOF +if { (eval echo configure:959: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 + CFLAGS="$pa_add_cflags__old_cflags" +fi +rm -f conftest* +echo $ac_n "checking if $CC accepts -Wmissing-declarations""... $ac_c" 1>&6 +echo "configure:971: checking if $CC accepts -Wmissing-declarations" >&5 + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Wmissing-declarations" + cat > conftest.$ac_ext < +int main() { +printf("Hello, World!\n"); +; return 0; } +EOF +if { (eval echo configure:982: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 + CFLAGS="$pa_add_cflags__old_cflags" +fi +rm -f conftest* +echo $ac_n "checking if $CC accepts -Wnested-externs""... $ac_c" 1>&6 +echo "configure:994: checking if $CC accepts -Wnested-externs" >&5 + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Wnested-externs" + cat > conftest.$ac_ext < +int main() { +printf("Hello, World!\n"); +; return 0; } +EOF +if { (eval echo configure:1005: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 + CFLAGS="$pa_add_cflags__old_cflags" +fi +rm -f conftest* +echo $ac_n "checking if $CC accepts -Winline""... $ac_c" 1>&6 +echo "configure:1017: checking if $CC accepts -Winline" >&5 + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Winline" + cat > conftest.$ac_ext < +int main() { +printf("Hello, World!\n"); +; return 0; } +EOF +if { (eval echo configure:1028: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 + CFLAGS="$pa_add_cflags__old_cflags" +fi +rm -f conftest* +echo $ac_n "checking if $CC accepts -Wcast-align""... $ac_c" 1>&6 +echo "configure:1040: checking if $CC accepts -Wcast-align" >&5 + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Wcast-align" + cat > conftest.$ac_ext < +int main() { +printf("Hello, World!\n"); +; return 0; } +EOF +if { (eval echo configure:1051: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 + CFLAGS="$pa_add_cflags__old_cflags" +fi +rm -f conftest* +echo $ac_n "checking if $CC accepts -pipe""... $ac_c" 1>&6 +echo "configure:1063: checking if $CC accepts -pipe" >&5 + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -pipe" + cat > conftest.$ac_ext < +int main() { +printf("Hello, World!\n"); +; return 0; } +EOF +if { (eval echo configure:1074: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 + CFLAGS="$pa_add_cflags__old_cflags" +fi +rm -f conftest* + +echo $ac_n "checking for BSD signal semantics""... $ac_c" 1>&6 +echo "configure:1087: checking for BSD signal semantics" >&5 +if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext < +#include +int count=0; +handle() { count++; } +int main() { + int pid=getpid(); + signal(SIGINT, handle); + kill(pid,SIGINT); + kill(pid,SIGINT); + kill(pid,SIGINT); + if (count!=3) return 1; + return 0; +} + +EOF +if { (eval echo configure:1110: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + BSD_SIGNAL=1 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -fr conftest* +fi + +if test -z "$BSD_SIGNAL"; then + echo "$ac_t""no" 1>&6 + echo $ac_n "checking if -D__USE_BSD_SIGNAL helps""... $ac_c" 1>&6 +echo "configure:1123: checking if -D__USE_BSD_SIGNAL helps" >&5 + pa_bsd_signal__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS -D__USE_BSD_SIGNAL" + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext < +#include +int count=0; +handle() { count++; } +int main() { + int pid=getpid(); + signal(SIGINT, handle); + kill(pid,SIGINT); + kill(pid,SIGINT); + kill(pid,SIGINT); + if (count!=3) return 1; + return 0; +} + +EOF +if { (eval echo configure:1148: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + BSD_SIGNAL=1 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -fr conftest* +fi + + if test -z "$BSD_SIGNAL"; then + echo "$ac_t""no" 1>&6 + CFLAGS="$pa_bsd_signal__old_cflags" + echo $ac_n "checking if -lbsd helps""... $ac_c" 1>&6 +echo "configure:1162: checking if -lbsd helps" >&5 + pa_bsd_signal__old_libs="$LIBS" + LIBS="$LIBS -lbsd" + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext < +#include +int count=0; +handle() { count++; } +int main() { + int pid=getpid(); + signal(SIGINT, handle); + kill(pid,SIGINT); + kill(pid,SIGINT); + kill(pid,SIGINT); + if (count!=3) return 1; + return 0; +} + +EOF +if { (eval echo configure:1187: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + BSD_SIGNAL=1 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -fr conftest* +fi + + if test -z "$BSD_SIGNAL"; then + echo "$ac_t""no" 1>&6 + LIBS="$pa_bsd_signal__old_libs" + else + echo "$ac_t""yes" 1>&6 + fi + else + echo "$ac_t""yes" 1>&6 + fi +else + echo "$ac_t""yes" 1>&6 +fi + + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:1241: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + for ac_prog in ginstall installbsd scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + # OSF/1 installbsd also uses dspmsg, but is usable. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +cat > conftest.defs <<\EOF +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g +s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g +s%\[%\\&%g +s%\]%\\&%g +s%\$%$$%g +EOF +DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '` +rm -f conftest.defs + + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.12" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "MCONFIG" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@CC@%$CC%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..e9e3cf3 --- /dev/null +++ b/configure.in @@ -0,0 +1,28 @@ +dnl +dnl autoconf input file to generate MCONFIG +dnl + +AC_INIT(MCONFIG.in) + +AC_PROG_CC +AC_C_CONST +AC_C_INLINE + +PA_ADD_CFLAGS(-Wall) +PA_ADD_CFLAGS(-W) +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(-Wcast-align) +PA_ADD_CFLAGS(-pipe) + +PA_BSD_SIGNAL() + +AC_PROG_INSTALL + +AC_OUTPUT(MCONFIG) diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..89fc9b0 --- /dev/null +++ b/install-sh @@ -0,0 +1,238 @@ +#! /bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5. +# +# 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. +# + + +# 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}" + +tranformbasename="" +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 + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + 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 + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + 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 + true + 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 + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; 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 + true + 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 true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/tftp/Makefile b/tftp/Makefile new file mode 100644 index 0000000..8623796 --- /dev/null +++ b/tftp/Makefile @@ -0,0 +1,21 @@ +all: tftp + +include ../MCONFIG +include ../MRULES + +OBJS = tftp.o main.o tftpsubs.o + +tftp: $(OBJS) + $(CC) $(LDFLAGS) $^ $(LIBS) -o $@ + +$(OBJS): tftpsubs.h + +install: tftp + $(INSTALL_PROGRAM) -s tftp $(INSTALLROOT)$(BINDIR) + $(INSTALL_DATA) tftp.1 $(INSTALLROOT)$(MANDIR)/man1 + +clean: + rm -f *.o tftp + +spotless: clean + rm -f *~ diff --git a/tftp/extern.h b/tftp/extern.h new file mode 100644 index 0000000..0d8c5e8 --- /dev/null +++ b/tftp/extern.h @@ -0,0 +1,42 @@ +/* $Id$ */ + +/* $OpenBSD: extern.h,v 1.2 1996/06/26 05:40:33 deraadt Exp $ */ +/* $NetBSD: extern.h,v 1.2 1994/12/08 09:51:24 jtc Exp $ */ + +/* + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * 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. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + */ + +void recvfile (int, char *, char *); +void sendfile (int, char *, char *); diff --git a/tftp/main.c b/tftp/main.c new file mode 100644 index 0000000..42139a7 --- /dev/null +++ b/tftp/main.c @@ -0,0 +1,704 @@ +/* $OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $ */ +/* $NetBSD: main.c,v 1.6 1995/05/21 16:54:10 mycroft Exp $ */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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 lint +static const char *copyright = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +/* static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; */ +/* static char rcsid[] = "$OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $"; */ +static const char *rcsid = "tftp-hpa $Id$"; +#endif /* not lint */ + +/* Many bug fixes are from Jim Guyton */ + +/* + * TFTP User Program -- Command Interface. + */ +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +#define TIMEOUT 5 /* secs between rexmt's */ +#define LBUFLEN 200 /* size of input buffer */ + +struct sockaddr_in peeraddr; +int f; +short port; +int trace; +int verbose; +int connected; +char mode[32]; +char line[LBUFLEN]; +int margc; +char *margv[20]; +char *prompt = "tftp"; +jmp_buf toplevel; +void intr(int); +struct servent *sp; + +void get (int, char **); +void help (int, char **); +void modecmd (int, char **); +void put (int, char **); +void quit (int, char **); +void setascii (int, char **); +void setbinary (int, char **); +void setpeer (int, char **); +void setrexmt (int, char **); +void settimeout (int, char **); +void settrace (int, char **); +void setverbose (int, char **); +void status (int, char **); + +static void command (void); + +static void getusage (char *); +static void makeargv (void); +static void putusage (char *); +static void settftpmode (char *); + +#define HELPINDENT (sizeof("connect")) + +struct cmd { + char *name; + char *help; + void (*handler) (int, char **); +}; + +char vhelp[] = "toggle verbose mode"; +char thelp[] = "toggle packet tracing"; +char chelp[] = "connect to remote tftp"; +char qhelp[] = "exit tftp"; +char hhelp[] = "print help information"; +char shelp[] = "send file"; +char rhelp[] = "receive file"; +char mhelp[] = "set file transfer mode"; +char sthelp[] = "show current status"; +char xhelp[] = "set per-packet retransmission timeout"; +char ihelp[] = "set total retransmission timeout"; +char ashelp[] = "set mode to netascii"; +char bnhelp[] = "set mode to octet"; + +struct cmd cmdtab[] = { + { "connect", chelp, setpeer }, + { "mode", mhelp, modecmd }, + { "put", shelp, put }, + { "get", rhelp, get }, + { "quit", qhelp, quit }, + { "verbose", vhelp, setverbose }, + { "trace", thelp, settrace }, + { "status", sthelp, status }, + { "binary", bnhelp, setbinary }, + { "ascii", ashelp, setascii }, + { "rexmt", xhelp, setrexmt }, + { "timeout", ihelp, settimeout }, + { "?", hhelp, help }, + { 0 } +}; + +struct cmd *getcmd(char *); +char *tail(char *); + +int +main(int argc, char *argv[]) +{ + struct sockaddr_in s_in; + + sp = getservbyname("tftp", "udp"); + if (sp == 0) { + fprintf(stderr, "tftp: udp/tftp: unknown service\n"); + exit(1); + } + f = socket(AF_INET, SOCK_DGRAM, 0); + if (f < 0) { + perror("tftp: socket"); + exit(3); + } + bzero((char *)&s_in, sizeof (s_in)); + s_in.sin_family = AF_INET; + if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { + perror("tftp: bind"); + exit(1); + } + strcpy(mode, "netascii"); + signal(SIGINT, intr); + if (argc > 1) { + if (setjmp(toplevel) != 0) + exit(0); + setpeer(argc, argv); + } + if (setjmp(toplevel) != 0) + (void)putchar('\n'); + + command(); + + return 0; /* Never reached */ +} + +char hostname[MAXHOSTNAMELEN]; + +void +setpeer(int argc, char *argv[]) +{ + struct hostent *host; + + if (argc < 2) { + strcpy(line, "Connect "); + printf("(to) "); + fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); + makeargv(); + argc = margc; + argv = margv; + } + if ((argc < 2) || (argc > 3)) { + printf("usage: %s host-name [port]\n", argv[0]); + return; + } + if (inet_aton(argv[1], &peeraddr.sin_addr) != 0) { + peeraddr.sin_family = AF_INET; + (void) strncpy(hostname, argv[1], sizeof hostname); + hostname[sizeof(hostname)-1] = '\0'; + } else { + host = gethostbyname(argv[1]); + if (host == 0) { + connected = 0; + printf("%s: unknown host\n", argv[1]); + return; + } + peeraddr.sin_family = host->h_addrtype; + bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length); + (void) strcpy(hostname, host->h_name); + } + port = sp->s_port; + if (argc == 3) { + port = atoi(argv[2]); + if (port < 0) { + printf("%s: bad port number\n", argv[2]); + connected = 0; + return; + } + port = htons(port); + } + connected = 1; +} + +struct modes { + char *m_name; + char *m_mode; +} modes[] = { + { "ascii", "netascii" }, + { "netascii", "netascii" }, + { "binary", "octet" }, + { "image", "octet" }, + { "octet", "octet" }, +/* { "mail", "mail" }, */ + { 0, 0 } +}; + +void +modecmd(int argc, char *argv[]) +{ + struct modes *p; + char *sep; + + if (argc < 2) { + printf("Using %s mode to transfer files.\n", mode); + return; + } + if (argc == 2) { + for (p = modes; p->m_name; p++) + if (strcmp(argv[1], p->m_name) == 0) + break; + if (p->m_name) { + settftpmode(p->m_mode); + return; + } + printf("%s: unknown mode\n", argv[1]); + /* drop through and print usage message */ + } + + 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[]) +{ + + settftpmode("octet"); +} + +void +setascii(int argc, char *argv[]) +{ + + settftpmode("netascii"); +} + +static void +settftpmode(char *newmode) +{ + strcpy(mode, newmode); + if (verbose) + printf("mode set to %s\n", mode); +} + + +/* + * Send file(s). + */ +void +put(int argc, char *argv[]) +{ + int fd; + int n; + char *cp, *targ; + + if (argc < 2) { + strcpy(line, "send "); + printf("(file) "); + fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); + makeargv(); + argc = margc; + argv = margv; + } + if (argc < 2) { + putusage(argv[0]); + return; + } + targ = argv[argc - 1]; + if (strchr(argv[argc - 1], ':')) { + char *cp; + struct hostent *hp; + + for (n = 1; n < argc - 1; n++) + if (strchr(argv[n], ':')) { + putusage(argv[0]); + return; + } + cp = argv[argc - 1]; + targ = strchr(cp, ':'); + *targ++ = 0; + hp = gethostbyname(cp); + if (hp == NULL) { + fprintf(stderr, "tftp: %s: ", cp); + herror((char *)NULL); + return; + } + bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length); + peeraddr.sin_family = hp->h_addrtype; + connected = 1; + strcpy(hostname, hp->h_name); + } + if (!connected) { + printf("No target machine specified.\n"); + return; + } + if (argc < 4) { + cp = argc == 2 ? tail(targ) : argv[1]; + fd = open(cp, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "tftp: "); perror(cp); + return; + } + if (verbose) + printf("putting %s to %s:%s [%s]\n", + cp, hostname, targ, mode); + peeraddr.sin_port = port; + sendfile(fd, targ, mode); + return; + } + /* this assumes the target is a directory */ + /* on a remote unix system. hmmmm. */ + cp = strchr(targ, '\0'); + *cp++ = '/'; + for (n = 1; n < argc - 1; n++) { + strcpy(cp, tail(argv[n])); + fd = open(argv[n], O_RDONLY); + if (fd < 0) { + fprintf(stderr, "tftp: "); perror(argv[n]); + continue; + } + if (verbose) + printf("putting %s to %s:%s [%s]\n", + argv[n], hostname, targ, mode); + peeraddr.sin_port = port; + sendfile(fd, targ, 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) { + strcpy(line, "get "); + printf("(files) "); + fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); + makeargv(); + argc = margc; + argv = margv; + } + if (argc < 2) { + getusage(argv[0]); + return; + } + if (!connected) { + for (n = 1; n < argc ; n++) + if (strchr(argv[n], ':') == 0) { + getusage(argv[0]); + return; + } + } + for (n = 1; n < argc ; n++) { + src = strchr(argv[n], ':'); + if (src == NULL) + src = argv[n]; + else { + struct hostent *hp; + + *src++ = 0; + hp = gethostbyname(argv[n]); + if (hp == NULL) { + fprintf(stderr, "tftp: %s: ", argv[n]); + herror((char *)NULL); + continue; + } + bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, + hp->h_length); + peeraddr.sin_family = hp->h_addrtype; + connected = 1; + strcpy(hostname, hp->h_name); + } + if (argc < 4) { + cp = argc == 3 ? argv[2] : tail(src); + fd = creat(cp, 0644); + if (fd < 0) { + fprintf(stderr, "tftp: "); perror(cp); + return; + } + if (verbose) + printf("getting from %s:%s to %s [%s]\n", + hostname, src, cp, mode); + peeraddr.sin_port = port; + recvfile(fd, src, mode); + break; + } + cp = tail(src); /* new .. jdg */ + fd = creat(cp, 0644); + if (fd < 0) { + fprintf(stderr, "tftp: "); perror(cp); + continue; + } + if (verbose) + printf("getting from %s:%s to %s [%s]\n", + hostname, src, cp, mode); + peeraddr.sin_port = port; + recvfile(fd, src, 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) { + strcpy(line, "Rexmt-timeout "); + printf("(value) "); + fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); + 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) { + strcpy(line, "Maximum-timeout "); + printf("(value) "); + fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); + makeargv(); + argc = margc; + argv = margv; + } + if (argc != 2) { + printf("usage: %s value\n", argv[0]); + return; + } + t = atoi(argv[1]); + if (t < 0) + printf("%s: bad value\n", argv[1]); + else + maxtimeout = t; +} + +void +status(int argc, char *argv[]) +{ + if (connected) + printf("Connected to %s.\n", hostname); + else + printf("Not connected.\n"); + printf("Mode: %s Verbose: %s Tracing: %s\n", mode, + verbose ? "on" : "off", trace ? "on" : "off"); + printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", + rexmtval, maxtimeout); +} + +void +intr(int sig) +{ + + signal(SIGALRM, SIG_IGN); + alarm(0); + longjmp(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 (;;) { + printf("%s> ", prompt); + if (fgets(line, LBUFLEN, stdin) == 0) { + if (feof(stdin)) { + exit(0); + } else { + continue; + } + } + if ((line[0] == 0) || (line[0] == '\n')) + continue; + makeargv(); + if (margc == 0) + continue; + c = getcmd(margv[0]); + if (c == (struct cmd *)-1) { + printf("?Ambiguous command\n"); + continue; + } + if (c == 0) { + printf("?Invalid command\n"); + continue; + } + (*c->handler)(margc, margv); + } +} + +struct cmd * +getcmd(char *name) +{ + char *p, *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[]) +{ + + exit(0); +} + +/* + * Help command. + */ +void +help(int argc, char *argv[]) +{ + struct cmd *c; + + if (argc == 1) { + printf("Commands may be abbreviated. Commands are:\n\n"); + for (c = cmdtab; c->name; c++) + printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); + return; + } + while (--argc > 0) { + char *arg; + arg = *++argv; + c = getcmd(arg); + if (c == (struct cmd *)-1) + printf("?Ambiguous help command %s\n", arg); + else if (c == (struct cmd *)0) + printf("?Invalid help command %s\n", arg); + else + printf("%s\n", c->help); + } +} + +void +settrace(int argc, char *argv[]) +{ + trace = !trace; + printf("Packet tracing %s.\n", trace ? "on" : "off"); +} + +void +setverbose(int argc, char *argv[]) +{ + verbose = !verbose; + printf("Verbose mode %s.\n", verbose ? "on" : "off"); +} diff --git a/tftp/tftp.1 b/tftp/tftp.1 new file mode 100644 index 0000000..41685c8 --- /dev/null +++ b/tftp/tftp.1 @@ -0,0 +1,177 @@ +.\" $OpenBSD: tftp.1,v 1.4 1999/06/05 01:21:43 aaron Exp $ +.\" $NetBSD: tftp.1,v 1.5 1995/08/18 14:45:44 pk Exp $ +.\" +.\" Copyright (c) 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tftp.1 8.2 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt TFTP 1 +.Os +.Sh NAME +.Nm tftp +.Nd trivial file transfer program +.Sh SYNOPSIS +.Nm tftp +.Op Ar host +.Sh DESCRIPTION +.Nm tftp +is the user interface to the Internet +.Tn TFTP +(Trivial File Transfer Protocol), +which allows users to transfer files to and from a remote machine. +The remote +.Ar host +may be specified on the command line, in which case +.Nm tftp +uses +.Ar host +as the default host for future transfers (see the +.Ic connect +command below). +.Sh COMMANDS +Once +.Nm tftp +is running, it issues the prompt +.Ql tftp> +and recognizes the following commands: +.Pp +.Bl -tag -width verbose -compact +.It Ic \&? Ar command-name Op Ar ... +Print help information. +.Pp +.It Ic ascii +Shorthand for +.Ic mode ascii . +.Pp +.It Ic binary +Shorthand for +.Ic mode binary . +.Pp +.It Ic connect Ar host Op Ar port +Set the +.Ar host +(and optionally +.Ar port ) +for transfers. +Note that the +.Tn TFTP +protocol, unlike the +.Tn FTP +protocol, +does not maintain connections between transfers; thus, the +.Ic connect +command does not actually create a connection, +but merely remembers what host is to be used for transfers. +You do not have to use the +.Ic connect +command; the remote host can be specified as part of the +.Ic get +or +.Ic put +commands. +.Pp +.It Ic get Ar filename +.It Ic get Ar remotename localname +.It Ic get Ar file Op Ar ... +Get a file or set of files from the specified +.Ar sources . +.Ar source +can be in one of two forms: +a filename on the remote host, if the host has already been specified, +or a string of the form +.Ar hosts:filename +to specify both a host and filename at the same time. +If the latter form is used, +the last hostname specified becomes the default for future transfers. +.Pp +.It Ic mode Ar transfer-mode +Set the mode for transfers; +.Ar transfer-mode +may be one of +.Ic ascii +or +.Ic binary . +The default is +.Ic ascii . +.Pp +.It Ic put Ar file +.It Ic put Ar localfile remotefile +.It Ic put Ar file1 file2 ... fileN remote-directory +Put a file or set of files to the specified +remote file or directory. +The destination +can be in one of two forms: +a filename on the remote host, if the host has already been specified, +or a string of the form +.Ar hosts:filename +to specify both a host and filename at the same time. +If the latter form is used, +the hostname specified becomes the default for future transfers. +If the remote-directory form is used, the remote host is +assumed to be a +.Tn UNIX +machine. +.Pp +.It Ic quit +Exit +.Nm tftp . +An end-of-file also exits. +.Pp +.It Ic rexmt Ar retransmission-timeout +Set the per-packet retransmission timeout, in seconds. +.Pp +.It Ic status +Show current status. +.Pp +.It Ic timeout Ar total-transmission-timeout +Set the total transmission timeout, in seconds. +.Pp +.It Ic trace +Toggle packet tracing. +.Pp +.It Ic verbose +Toggle verbose mode. +.El +.Sh BUGS +Because there is no user login or validation within +the +.Tn TFTP +protocol, the remote site will probably have some +sort of file access restrictions in place. The +exact methods are specific to each site and therefore +difficult to document here. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/tftp/tftp.c b/tftp/tftp.c new file mode 100644 index 0000000..f68984a --- /dev/null +++ b/tftp/tftp.c @@ -0,0 +1,453 @@ +/* $Id$ */ + +/* $OpenBSD: tftp.c,v 1.4 1997/08/06 06:43:45 deraadt Exp $ */ +/* $NetBSD: tftp.c,v 1.5 1995/04/29 05:55:25 cgd Exp $ */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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 lint +/* static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; */ +/* static char rcsid[] = "$OpenBSD: tftp.c,v 1.4 1997/08/06 06:43:45 deraadt Exp $"; */ +static const char *rcsid = "tftp-hpa $Id$"; +#endif /* not lint */ + +/* Many bug fixes are from Jim Guyton */ + +/* + * TFTP User Program -- Protocol Machines + */ +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "extern.h" +#include "tftpsubs.h" + +extern int errno; + +extern struct sockaddr_in peeraddr; /* filled in by main */ +extern int f; /* the opened socket */ +extern int trace; +extern int verbose; +extern int rexmtval; +extern int maxtimeout; + +#ifndef EOPTNEG +#define EOPTNEG 8 +#endif +#ifndef OACK +#define OACK 6 +#endif + +#define PKTSIZE SEGSIZE+4 +char ackbuf[PKTSIZE]; +int timeout; +jmp_buf toplevel; +jmp_buf timeoutbuf; + +static void nak(int); +static int makerequest(int, const char *, struct tftphdr *, const char *); +static void printstats(const char *, unsigned long); +static void startclock(void); +static void stopclock(void); +static void timer(int); +static void tpacket(const char *, struct tftphdr *, int); + +/* + * Send the requested file. + */ +void +sendfile(int fd, char *name, char *mode) +{ + struct tftphdr *ap; /* data and ack packets */ + struct tftphdr *dp; + int n; + volatile int block, size, convert; + volatile unsigned long amount; + struct sockaddr_in from; + int fromlen; + FILE *file; + + startclock(); /* start stat's clock */ + dp = r_init(); /* reset fillbuf/read-ahead code */ + ap = (struct tftphdr *)ackbuf; + file = fdopen(fd, "r"); + convert = !strcmp(mode, "netascii"); + block = 0; + amount = 0; + + signal(SIGALRM, timer); + do { + if (block == 0) + size = makerequest(WRQ, name, dp, mode) - 4; + else { + /* size = read(fd, dp->th_data, SEGSIZE); */ + size = readit(file, &dp, convert); + if (size < 0) { + nak(errno + 100); + break; + } + dp->th_opcode = htons((u_short)DATA); + dp->th_block = htons((u_short)block); + } + timeout = 0; + (void) setjmp(timeoutbuf); +send_data: + if (trace) + tpacket("sent", dp, size + 4); + n = sendto(f, dp, size + 4, 0, + (struct sockaddr *)&peeraddr, sizeof(peeraddr)); + if (n != size + 4) { + perror("tftp: sendto"); + goto abort; + } + read_ahead(file, convert); + for ( ; ; ) { + alarm(rexmtval); + do { + fromlen = sizeof(from); + n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, + (struct sockaddr *)&from, &fromlen); + } while (n <= 0); + alarm(0); + if (n < 0) { + perror("tftp: recvfrom"); + goto abort; + } + peeraddr.sin_port = from.sin_port; /* added */ + if (trace) + tpacket("received", ap, n); + /* should verify packet came from server */ + ap->th_opcode = ntohs(ap->th_opcode); + ap->th_block = ntohs(ap->th_block); + if (ap->th_opcode == ERROR) { + printf("Error code %d: %s\n", ap->th_code, + ap->th_msg); + goto abort; + } + if (ap->th_opcode == ACK) { + int j; + + if (ap->th_block == block) { + break; + } + /* On an error, try to synchronize + * both sides. + */ + j = synchnet(f); + if (j && trace) { + printf("discarded %d packets\n", + j); + } + if (ap->th_block == (block-1)) { + goto send_data; + } + } + } + if (block > 0) + amount += size; + block++; + } while (size == SEGSIZE || block == 1); +abort: + fclose(file); + stopclock(); + if (amount > 0) + printstats("Sent", amount); +} + +/* + * Receive a file. + */ +void +recvfile(int fd, char *name, char *mode) +{ + struct tftphdr *ap; + struct tftphdr *dp; + int n; + volatile int block, size, firsttrip; + volatile unsigned long amount; + struct sockaddr_in from; + int fromlen; + FILE *file; + volatile int convert; /* true if converting crlf -> lf */ + + startclock(); + dp = w_init(); + ap = (struct tftphdr *)ackbuf; + file = fdopen(fd, "w"); + convert = !strcmp(mode, "netascii"); + block = 1; + firsttrip = 1; + amount = 0; + + 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) setjmp(timeoutbuf); +send_ack: + if (trace) + tpacket("sent", ap, size); + if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, + sizeof(peeraddr)) != size) { + alarm(0); + perror("tftp: sendto"); + goto abort; + } + write_behind(file, convert); + for ( ; ; ) { + alarm(rexmtval); + do { + fromlen = sizeof(from); + n = recvfrom(f, dp, PKTSIZE, 0, + (struct sockaddr *)&from, &fromlen); + } while (n <= 0); + alarm(0); + if (n < 0) { + perror("tftp: recvfrom"); + goto abort; + } + peeraddr.sin_port = from.sin_port; /* added */ + if (trace) + tpacket("received", dp, n); + /* should verify client address */ + dp->th_opcode = ntohs(dp->th_opcode); + dp->th_block = ntohs(dp->th_block); + if (dp->th_opcode == ERROR) { + printf("Error code %d: %s\n", dp->th_code, + dp->th_msg); + goto abort; + } + if (dp->th_opcode == DATA) { + int j; + + if (dp->th_block == block) { + break; /* have next packet */ + } + /* On an error, try to synchronize + * both sides. + */ + j = synchnet(f); + if (j && trace) { + printf("discarded %d packets\n", j); + } + if (dp->th_block == (block-1)) { + goto send_ack; /* resend ack */ + } + } + } + /* size = write(fd, dp->th_data, n - 4); */ + size = writeit(file, &dp, n - 4, convert); + if (size < 0) { + nak(errno + 100); + break; + } + amount += size; + } while (size == SEGSIZE); +abort: /* ok to ack, since user */ + ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ + ap->th_block = htons((u_short)block); + (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, + sizeof(peeraddr)); + write_behind(file, convert); /* flush last buffer */ + fclose(file); + stopclock(); + if (amount > 0) + printstats("Received", amount); +} + +static int +makerequest(int request, const char *name, + struct tftphdr *tp, const char *mode) +{ + char *cp; + + tp->th_opcode = htons((u_short)request); + cp = tp->th_stuff; + strcpy(cp, name); + cp += strlen(name); + *cp++ = '\0'; + strcpy(cp, mode); + cp += strlen(mode); + *cp++ = '\0'; + return (cp - (char *)tp); +} + +struct errmsg { + int e_code; + char *e_msg; +} errmsgs[] = { + { EUNDEF, "Undefined error code" }, + { ENOTFOUND, "File not found" }, + { EACCESS, "Access violation" }, + { ENOSPACE, "Disk full or allocation exceeded" }, + { EBADOP, "Illegal TFTP operation" }, + { EBADID, "Unknown transfer ID" }, + { EEXISTS, "File already exists" }, + { ENOUSER, "No such user" }, + { EOPTNEG, "Failure to negotiate RFC2347 options" }, + { -1, 0 } +}; + +/* + * 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) +{ + struct errmsg *pe; + struct tftphdr *tp; + int length; + + tp = (struct tftphdr *)ackbuf; + tp->th_opcode = htons((u_short)ERROR); + tp->th_code = htons((u_short)error); + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + break; + if (pe->e_code < 0) { + pe->e_msg = strerror(error - 100); + tp->th_code = EUNDEF; + } + strcpy(tp->th_msg, pe->e_msg); + length = strlen(pe->e_msg) + 4; + if (trace) + tpacket("sent", tp, length); + if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, + sizeof(peeraddr)) != length) + perror("nak"); +} + +static void +tpacket(const char *s, struct tftphdr *tp, int n) +{ + static char *opcodes[] = + { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" }; + char *cp, *file; + u_short op = ntohs(tp->th_opcode); + + if (op < RRQ || op > ERROR) + printf("%s opcode=%x ", s, op); + else + printf("%s %s ", s, opcodes[op]); + switch (op) { + + case RRQ: + case WRQ: + n -= 2; + file = cp = tp->th_stuff; + cp = strchr(cp, '\0'); + printf("\n", file, cp + 1); + break; + + case DATA: + printf("\n", ntohs(tp->th_block), n - 4); + break; + + case ACK: + printf("\n", ntohs(tp->th_block)); + break; + + case ERROR: + printf("\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; + /* compute delta in 1/10's second units */ + delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - + ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); + delta = delta/10.; /* back to seconds */ + printf("%s %lu bytes in %.1f seconds", direction, amount, delta); + if (verbose) + printf(" [%.0f bits/sec]", (amount*8.)/delta); + putchar('\n'); +} + +static void +timer(int sig) +{ + int save_errno = errno; + + timeout += rexmtval; + if (timeout >= maxtimeout) { + printf("Transfer timed out.\n"); + errno = save_errno; + longjmp(toplevel, -1); + } + errno = save_errno; + longjmp(timeoutbuf, 1); +} diff --git a/tftp/tftpsubs.c b/tftp/tftpsubs.c new file mode 100644 index 0000000..b0ecc91 --- /dev/null +++ b/tftp/tftpsubs.c @@ -0,0 +1,271 @@ +/* $OpenBSD: tftpsubs.c,v 1.2 1996/06/26 05:40:36 deraadt Exp $ */ +/* $NetBSD: tftpsubs.c,v 1.3 1994/12/08 09:51:31 jtc Exp $ */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)tftpsubs.c 8.1 (Berkeley) 6/6/93"; +#endif +static char rcsid[] = "$OpenBSD: tftpsubs.c,v 1.2 1996/06/26 05:40:36 deraadt Exp $"; +#endif /* not lint */ + +/* Simple minded read-ahead/write-behind subroutines for tftp user and + server. Written originally with multiple buffers in mind, but current + implementation has two buffer logic wired in. + + Todo: add some sort of final error check so when the write-buffer + is finally flushed, the caller can detect if the disk filled up + (or had an i/o error) and return a nak to the other side. + + Jim Guyton 10/85 + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "tftpsubs.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 i, j = 0; + char rbuf[PKTSIZE]; + struct sockaddr_in from; + int fromlen; + + while (1) { + (void) ioctl(f, FIONREAD, &i); + if (i) { + j++; + fromlen = sizeof from; + (void) recvfrom(f, rbuf, sizeof (rbuf), 0, + (struct sockaddr *)&from, &fromlen); + } else { + return(j); + } + } +} diff --git a/tftp/tftpsubs.h b/tftp/tftpsubs.h new file mode 100644 index 0000000..a2a3a6c --- /dev/null +++ b/tftp/tftpsubs.h @@ -0,0 +1,57 @@ +/* $Id$ */ + +/* $OpenBSD: tftpsubs.h,v 1.2 1996/06/26 05:40:37 deraadt Exp $ */ +/* $NetBSD: tftpsubs.h,v 1.2 1994/12/08 09:51:32 jtc Exp $ */ + +/* + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * 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. + * + * @(#)tftpsubs.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * Prototypes for read-ahead/write-behind subroutines for tftp user and + * server. + */ +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 + diff --git a/tftpd/Makefile b/tftpd/Makefile new file mode 100644 index 0000000..73b51ea --- /dev/null +++ b/tftpd/Makefile @@ -0,0 +1,27 @@ +all: tftpd + +include ../MCONFIG +include ../MRULES + +OBJS = tftpd.o tftpsubs.o + +tftpd: $(OBJS) + $(CC) $(LDFLAGS) $^ $(LIBS) -o $@ + +tftpsubs.c: + ln -sf ../tftp/tftpsubs.c . +tftpsubs.h: + ln -sf ../tftp/tftpsubs.h . + +$(OBJS): tftpsubs.h + +install: tftpd + $(INSTALL_PROGRAM) tftpd $(INSTALLROOT)$(SBINDIR)/in.tftpd + $(INSTALL_DATA) tftpd.8 $(INSTALLROOT)$(MANDIR)/man8/in.tftpd.8 + ln -sf in.tftpd.8 $(INSTALLROOT)$(MANDIR)/man8/tftpd.8 + +clean: + rm -f *.o tftpd tftpsubs.c tftpsubs.h + +spotless: clean + rm -f *~ diff --git a/tftpd/tftpd.8 b/tftpd/tftpd.8 new file mode 100644 index 0000000..0df83bf --- /dev/null +++ b/tftpd/tftpd.8 @@ -0,0 +1,141 @@ +.\" $OpenBSD: tftpd.8,v 1.7 1999/07/09 13:35:51 aaron Exp $ +.\" +.\" Copyright (c) 1983, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)tftpd.8 6.7 (Berkeley) 5/13/91 +.\" $OpenBSD: tftpd.8,v 1.7 1999/07/09 13:35:51 aaron Exp $ +.\" +.Dd June 11, 1997 +.Dt TFTPD 8 +.Os +.Sh NAME +.Nm tftpd +.Nd +.Tn DARPA +Trivial File Transfer Protocol server +.Sh SYNOPSIS +.Nm tftpd +.Op Fl cs +.Op Fl r Ar option... +.Op Ar directory +.Sh DESCRIPTION +.Nm +is a server which supports the +.Tn DARPA +Trivial File Transfer +Protocol. +The +.Tn TFTP +server operates +at the port indicated in the +.Ql tftp +service description; +see +.Xr services 5 . +The server is normally started by +.Xr inetd 8 . +.Pp +The use of +.Xr tftp 1 +does not require an account or password on the remote system. +Due to the lack of authentication information, +.Nm +will allow only publicly readable files to be +accessed. +Files may be written only if they already exist and are publicly writable. +Note that this extends the concept of +.Dq public +to include +all users on all hosts that can be reached through the network; +this may not be appropriate on all systems, and its implications +should be considered before enabling tftp service. +The server should have the user ID with the lowest possible privilege. +.Pp +Access to files may be restricted by invoking +.Nm +with a list of directories by including pathnames +as server program arguments in +.Pa /etc/inetd.conf . +In this case access is restricted to files whose +names are prefixed by the one of the given directories. +.Pp +If the +.Fl c +flag is used, +.Nm +will allow new files to be created; otherwise uploaded files must already +exist. Files are created with default permissions allowing anyone to read +or write to them. +.Pp +When using the +.Fl s +flag with a directory name, +.Nm +will +.Xr chroot 2 +on startup; therefore the remote host is not expected to pass the directory +as part of the file name to transfer. This option is intended primarily for +compatibility with SunOS boot ROMs which do not include a directory name. +.Pp +This version of +.Nm +supports RFC 2347 option negotiation; the current version supports the +.Pa blksize +(RFC 2348), +.Pa blksize , +(RFC 2349), and +.Pa timeout +(RFC 2349) options. The +.Fl r +flag can be used to disable options individually; this may allow +working around client bugs. +.Sh SEE ALSO +.Xr tftp 1 , +.Xr inetd 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +.Pp +The +.Fl s +flag appeared in NetBSD 0.9a. +.Pp +The +.Fl c +flag was added in OpenBSD 2.1 . +.Pp +The +.Fl r +flag and RFC 2347 options were added by H. Peter Anvin based on +patches by Markus Gutschke and Gero Kulhman. diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c new file mode 100644 index 0000000..6712f24 --- /dev/null +++ b/tftpd/tftpd.c @@ -0,0 +1,821 @@ +/* $OpenBSD: tftpd.c,v 1.13 1999/06/23 17:01:36 deraadt Exp $ */ + +/* + * Copyright (c) 1983 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 lint +static const char *copyright = +"@(#) Copyright (c) 1983 Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)tftpd.c 5.13 (Berkeley) 2/26/91";*/ +/*static char rcsid[] = "$OpenBSD: tftpd.c,v 1.13 1999/06/23 17:01:36 deraadt Exp $: tftpd.c,v 1.6 1997/02/16 23:49:21 deraadt Exp $";*/ +static const char *cvsid = "tftp-hpa $Id$"; +#endif /* not lint */ + +/* + * Trivial file transfer protocol server. + * + * This version includes many modifications by Jim Guyton + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tftpsubs.h" + +#define TIMEOUT 5 /* Default timeout (seconds) */ +#define TRIES 4 /* Number of attempts to send each packet */ +#define TIMEOUT_LIMIT (TRIES*(TRIES+1)/2) + +#ifndef OACK +#define OACK 6 +#endif +#ifndef EOPTNEG +#define EOPTNEG 8 +#endif + +extern int errno; +extern char *__progname; +struct sockaddr_in s_in = { AF_INET }; +int peer; +int timeout = TIMEOUT; +int rexmtval = TIMEOUT; +int maxtimeout = TIMEOUT_LIMIT*TIMEOUT; + +#define PKTSIZE MAX_SEGSIZE+4 +char buf[PKTSIZE]; +char ackbuf[PKTSIZE]; +struct sockaddr_in from; +int fromlen; +off_t tsize; +int tsize_ok; + +int ndirs; +char **dirs; + +int secure = 0; +int cancreate = 0; + +struct formats; + +int tftp(struct tftphdr *, int); +void nak(int); +void timer(int); +void justquit(int); +void do_opt(char *, char *, char **); + +int set_blksize(char *, char **); +int set_tsize(char *, char **); +int set_timeout(char *, char **); + +struct options { + char *o_opt; + int (*o_fnc)(char *, char **); +} options[] = { + { "blksize", set_blksize }, + { "tsize", set_tsize }, + { "timeout", set_timeout }, + { NULL, NULL } +}; + + + +static void +usage(void) +{ + syslog(LOG_ERR, "Usage: %s [-cs] [-r option...] [directory ...]", + __progname); + exit(1); +} + +int +main(int argc, char **argv) +{ + struct tftphdr *tp; + struct passwd *pw; + struct options *opt; + int n = 0; + int on = 1; + int fd = 0; + int pid; + int i, j; + int c; + + openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON); + + while ((c = getopt(argc, argv, "csr:")) != -1) + switch (c) { + case 'c': + cancreate = 1; + break; + case 's': + secure = 1; + break; + case 'r': + for ( opt = options ; opt->o_opt ; opt++ ) { + if ( !strcasecmp(optarg, opt->o_opt) ) { + opt->o_opt = ""; /* Don't support this option */ + break; + } + } + if ( !opt->o_opt ) { + syslog(LOG_ERR, "Unknown option: %s", optarg); + exit(1); + } + break; + + default: + usage(); + break; + } + + for (; optind != argc; optind++) { + if (dirs) + dirs = realloc(dirs, (ndirs+2) * sizeof (char *)); + else + dirs = calloc(ndirs+2, sizeof(char *)); + if (dirs == NULL) { + syslog(LOG_ERR, "malloc: %m"); + exit(1); + } + dirs[n++] = argv[optind]; + dirs[n] = NULL; + ndirs++; + } + + if (secure) { + if (ndirs == 0) { + syslog(LOG_ERR, "no -s directory"); + exit(1); + } + if (ndirs > 1) { + syslog(LOG_ERR, "too many -s directories"); + exit(1); + } + if (chdir(dirs[0])) { + syslog(LOG_ERR, "%s: %m", dirs[0]); + exit(1); + } + } + + pw = getpwnam("nobody"); + if (!pw) { + syslog(LOG_ERR, "no nobody: %m"); + exit(1); + } + + if (secure && chroot(".")) { + syslog(LOG_ERR, "chroot: %m"); + exit(1); + } + + (void) setegid(pw->pw_gid); + (void) setgid(pw->pw_gid); + (void) seteuid(pw->pw_uid); + (void) setuid(pw->pw_uid); + + if (ioctl(fd, FIONBIO, &on) < 0) { + syslog(LOG_ERR, "ioctl(FIONBIO): %m"); + exit(1); + } + fromlen = sizeof (from); + n = recvfrom(fd, buf, sizeof (buf), 0, + (struct sockaddr *)&from, &fromlen); + if (n < 0) { + syslog(LOG_ERR, "recvfrom: %m"); + exit(1); + } + /* + * Now that we have read the message out of the UDP + * socket, we fork and exit. Thus, inetd will go back + * to listening to the tftp port, and the next request + * to come in will start up a new instance of tftpd. + * + * We do this so that inetd can run tftpd in "wait" mode. + * The problem with tftpd running in "nowait" mode is that + * inetd may get one or more successful "selects" on the + * tftp port before we do our receive, so more than one + * instance of tftpd may be started up. Worse, if tftpd + * break before doing the above "recvfrom", inetd would + * spawn endless instances, clogging the system. + */ + for (i = 1; i < 20; i++) { + pid = fork(); + if (pid < 0) { + sleep(i); + /* + * flush out to most recently sent request. + * + * This may drop some request, but those + * will be resent by the clients when + * they timeout. The positive effect of + * this flush is to (try to) prevent more + * than one tftpd being started up to service + * a single request from a single client. + */ + j = sizeof from; + i = recvfrom(fd, buf, sizeof (buf), 0, + (struct sockaddr *)&from, &j); + if (i > 0) { + n = i; + fromlen = j; + } + } else + break; + } + if (pid < 0) { + syslog(LOG_ERR, "fork: %m"); + exit(1); + } else if (pid != 0) + exit(0); + + from.sin_family = AF_INET; + alarm(0); + close(fd); + close(1); + peer = socket(AF_INET, SOCK_DGRAM, 0); + if (peer < 0) { + syslog(LOG_ERR, "socket: %m"); + exit(1); + } + if (bind(peer, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { + syslog(LOG_ERR, "bind: %m"); + exit(1); + } + if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { + syslog(LOG_ERR, "connect: %m"); + exit(1); + } + tp = (struct tftphdr *)buf; + tp->th_opcode = ntohs(tp->th_opcode); + if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) + tftp(tp, n); + exit(0); +} + +int validate_access(char *, int, struct formats *); +void sendfile(struct formats *, struct tftphdr *, int); +void recvfile(struct formats *, struct tftphdr *, int); + +struct formats { + char *f_mode; + int (*f_validate)(char *, int, struct formats *); + void (*f_send)(struct formats *, struct tftphdr *, int); + void (*f_recv)(struct formats *, struct tftphdr *, int); + int f_convert; +} formats[] = { + { "netascii", validate_access, sendfile, recvfile, 1 }, + { "octet", validate_access, sendfile, recvfile, 0 }, + { NULL } +}; + +/* + * Handle initial connection protocol. + */ +int +tftp(struct tftphdr *tp, int size) +{ + char *cp; + int argn, ecode; + struct formats *pf = NULL; + char *filename, *mode = NULL; + + char *val = NULL, *opt = NULL; + char *ap = ackbuf + 2; + + ((struct tftphdr *)ackbuf)->th_opcode = ntohs(OACK); + + filename = cp = tp->th_stuff; + argn = 0; + + while ( cp < buf + size && *cp ) { + do { + cp++; + } while (cp < buf + size && *cp); + + if ( *cp ) { + nak(EBADOP); /* Corrupt packet - no final NULL */ + exit(0); + } + + argn++; + if (argn == 1) { + mode = ++cp; + } else if (argn == 2) { + for (cp = mode; *cp; cp++) + *cp = tolower(*cp); + for (pf = formats; pf->f_mode; pf++) { + if (!strcmp(pf->f_mode, mode)) + break; + } + if (!pf->f_mode) { + nak(EBADOP); + exit(0); + } + ecode = (*pf->f_validate)(filename, tp->th_opcode, pf); + if (ecode) { + nak(ecode); + exit(0); + } + opt = ++cp; + } else if ( argn & 1 ) { + val = ++cp; + } else { + do_opt(opt, val, &ap); + opt = ++cp; + } + } + + if (!pf) { + nak(EBADOP); + exit(0); + } + + if ( ap != (ackbuf+2) ) { + if ( tp->th_opcode == WRQ ) + (*pf->f_recv)(pf, ackbuf, ap-ackbuf); + else + (*pf->f_send)(pf, ackbuf, ap-ackbuf); + } else { + if (tp->th_opcode == WRQ) + (*pf->f_recv)(pf, NULL, 0); + else + (*pf->f_send)(pf, NULL, 0); + } + exit(1); +} + +/* + * Set a non-standard block size (c.f. RFC2348) + */ +int +set_blksize(char *val, char **ret) +{ + static char b_ret[6]; + int sz = atoi(val); + + if (sz < 8) + return(0); + else if (sz > MAX_SEGSIZE) + sz = MAX_SEGSIZE; + + segsize = sz; + sprintf(*ret = b_ret, "%d", sz); + return(1); +} + + +/* + * Return a file size (c.f. RFC2349) + * For netascii mode, we don't know the size ahead of time; + * so reject the option. + */ +int +set_tsize(char *val, char **ret) +{ + static char b_ret[sizeof(off_t)*CHAR_BIT/3+2]; + off_t sz = atol(val); + + if ( !tsize_ok ) + return 0; + + if (sz == 0) + sz = tsize; + sprintf(*ret = b_ret, "%lu", sz); + return(1); +} + +/* + * Set the timeout (c.f. RFC2349) + */ +int +set_timeout(char *val, char **ret) +{ + static char b_ret[4]; + unsigned long to = atol(val); + + if ( to < 1 || to > 255 ) + return 0; + + timeout = to; + rexmtval = to; + maxtimeout = TIMEOUT_LIMIT*to; + + sprintf(*ret = b_ret, "%lu", to); + return(1); +} + +/* + * Parse RFC2347 style options + */ +void +do_opt(char *opt, char *val, char **ap) +{ + struct options *po; + char *ret; + + if ( !*opt ) + return; + + for (po = options; po->o_opt; po++) + if (!strcasecmp(po->o_opt, opt)) { + if (po->o_fnc(val, &ret)) { + if (*ap + strlen(opt) + strlen(ret) + 2 >= + ackbuf + sizeof(ackbuf)) { + nak(ENOSPACE); /* EOPTNEG? */ + exit(0); + } + *ap = strrchr(strcpy(strrchr(strcpy(*ap, opt),'\0') + 1, + ret),'\0') + 1; + } else { + nak(EOPTNEG); + exit(0); + } + break; + } + return; +} + + +FILE *file; + +/* + * Validate file access. Since we + * have no uid or gid, for now require + * file to exist and be publicly + * readable/writable. + * If we were invoked with arguments + * from inetd then the file must also be + * in one of the given directory prefixes. + * Note also, full path name must be + * given as we have no login directory. + */ +int +validate_access(char *filename, int mode, struct formats *pf) +{ + struct stat stbuf; + int fd, wmode; + char *cp, **dirp; + + tsize_ok = 0; + + if (!secure) { + if (*filename != '/') + return (EACCESS); + /* + * prevent tricksters from getting around the directory + * restrictions + */ + for (cp = filename + 1; *cp; cp++) + if(*cp == '.' && strncmp(cp-1, "/../", 4) == 0) + return(EACCESS); + for (dirp = dirs; *dirp; dirp++) + if (strncmp(filename, *dirp, strlen(*dirp)) == 0) + break; + if (*dirp==0 && dirp!=dirs) + return (EACCESS); + } + + /* + * We use different a different permissions scheme if `cancreate' is + * set. + */ + wmode = O_TRUNC; + if (stat(filename, &stbuf) < 0) { + if (!cancreate) + return (errno == ENOENT ? ENOTFOUND : EACCESS); + else { + if ((errno == ENOENT) && (mode != RRQ)) + wmode |= O_CREAT; + else + return(EACCESS); + } + } else { + if (mode == RRQ) { + if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) + return (EACCESS); + tsize = stbuf.st_size; + tsize_ok = !pf->f_convert; + } else { + if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) + return (EACCESS); + tsize = 0; + tsize_ok = 1; + } + } + fd = open(filename, mode == RRQ ? O_RDONLY : (O_WRONLY|wmode), 0666); + if (fd < 0) + return (errno + 100); + /* + * If the file was created, set default permissions. + */ + if ((wmode & O_CREAT) && fchmod(fd, 0666) < 0) { + int serrno = errno; + + close(fd); + unlink(filename); + + return (serrno + 100); + } + file = fdopen(fd, (mode == RRQ)? "r":"w"); + if (file == NULL) + return (errno + 100); + return (0); +} + +int timeout; +jmp_buf timeoutbuf; + +void +timer(int sig) +{ + + timeout += rexmtval; + if (timeout >= maxtimeout) + exit(0); + longjmp(timeoutbuf, 1); +} + +/* + * Send the requested file. + */ +void +sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) +{ + struct tftphdr *dp; + struct tftphdr *ap; /* ack packet */ + int block = 1, size, n; + + signal(SIGALRM, timer); + ap = (struct tftphdr *)ackbuf; + + if (oap) { + timeout = 0; + (void)setjmp(timeoutbuf); +oack: + if (send(peer, oap, oacklen, 0) != oacklen) { + syslog(LOG_ERR, "tftpd: oack: %m\n"); + goto abort; + } + for ( ; ; ) { + alarm(rexmtval); + n = recv(peer, ackbuf, sizeof(ackbuf), 0); + alarm(0); + if (n < 0) { + syslog(LOG_ERR, "tftpd: read: %m\n"); + goto abort; + } + ap->th_opcode = ntohs((u_short)ap->th_opcode); + ap->th_block = ntohs((u_short)ap->th_block); + + if (ap->th_opcode == ERROR) { + syslog(LOG_ERR, "tftp: client does not accept " + "options\n"); + goto abort; + } + if (ap->th_opcode == ACK) { + if (ap->th_block == 0) + break; + /* Resynchronize with the other side */ + (void)synchnet(peer); + goto oack; + } + } + } + + dp = r_init(); + do { + size = readit(file, &dp, pf->f_convert); + if (size < 0) { + nak(errno + 100); + goto abort; + } + dp->th_opcode = htons((u_short)DATA); + dp->th_block = htons((u_short)block); + timeout = 0; + (void) setjmp(timeoutbuf); + +send_data: + if (send(peer, dp, size + 4, 0) != size + 4) { + syslog(LOG_ERR, "tftpd: write: %m"); + goto abort; + } + read_ahead(file, pf->f_convert); + for ( ; ; ) { + alarm(rexmtval); /* read the ack */ + n = recv(peer, ackbuf, sizeof (ackbuf), 0); + alarm(0); + if (n < 0) { + syslog(LOG_ERR, "tftpd: read(ack): %m"); + goto abort; + } + ap->th_opcode = ntohs((u_short)ap->th_opcode); + ap->th_block = ntohs((u_short)ap->th_block); + + if (ap->th_opcode == ERROR) + goto abort; + + if (ap->th_opcode == ACK) { + if (ap->th_block == block) { + break; + } + /* Re-synchronize with the other side */ + (void) synchnet(peer); + if (ap->th_block == (block -1)) { + goto send_data; + } + } + + } + block++; + } while (size == segsize); +abort: + (void) fclose(file); +} + +void +justquit(int sig) +{ + exit(0); +} + + +/* + * Receive a file. + */ +void +recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) +{ + struct tftphdr *dp; + struct tftphdr *ap; /* ack buffer */ + int block = 0, acksize, n, size; + + signal(SIGALRM, timer); + dp = w_init(); + do { + timeout = 0; + + if (!block++ && oap) { + ap = (struct tftphdr *)ackbuf; + acksize = oacklen; + } else { + ap = (struct tftphdr *)ackbuf; + ap->th_opcode = htons((u_short)ACK); + ap->th_block = htons((u_short)block); + acksize = 4; + } + (void) setjmp(timeoutbuf); +send_ack: + if (send(peer, ackbuf, acksize, 0) != acksize) { + syslog(LOG_ERR, "tftpd: write(ack): %m"); + goto abort; + } + write_behind(file, pf->f_convert); + for ( ; ; ) { + alarm(rexmtval); + n = recv(peer, dp, PKTSIZE, 0); + alarm(0); + if (n < 0) { /* really? */ + syslog(LOG_ERR, "tftpd: read: %m"); + goto abort; + } + dp->th_opcode = ntohs((u_short)dp->th_opcode); + dp->th_block = ntohs((u_short)dp->th_block); + if (dp->th_opcode == ERROR) + goto abort; + if (dp->th_opcode == DATA) { + if (dp->th_block == block) { + break; /* normal */ + } + /* Re-synchronize with the other side */ + (void) synchnet(peer); + if (dp->th_block == (block-1)) + goto send_ack; /* rexmit */ + } + } + /* size = write(file, dp->th_data, n - 4); */ + size = writeit(file, &dp, n - 4, pf->f_convert); + if (size != (n-4)) { /* ahem */ + if (size < 0) nak(errno + 100); + else nak(ENOSPACE); + goto abort; + } + } while (size == segsize); + write_behind(file, pf->f_convert); + (void) fclose(file); /* close data file */ + + ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ + ap->th_block = htons((u_short)(block)); + (void) send(peer, ackbuf, 4, 0); + + signal(SIGALRM, justquit); /* just quit on timeout */ + alarm(rexmtval); + n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ + alarm(0); + if (n >= 4 && /* if read some data */ + dp->th_opcode == DATA && /* and got a data block */ + block == dp->th_block) { /* then my last ack was lost */ + (void) send(peer, ackbuf, 4, 0); /* resend final ack */ + } +abort: + return; +} + +struct errmsg { + int e_code; + char *e_msg; +} errmsgs[] = { + { EUNDEF, "Undefined error code" }, + { ENOTFOUND, "File not found" }, + { EACCESS, "Access violation" }, + { ENOSPACE, "Disk full or allocation exceeded" }, + { EBADOP, "Illegal TFTP operation" }, + { EBADID, "Unknown transfer ID" }, + { EEXISTS, "File already exists" }, + { ENOUSER, "No such user" }, + { EOPTNEG, "Failure to negotiate RFC2347 options" }, + { -1, 0 } +}; + +/* + * Send a nak packet (error message). + * Error code passed in is one of the + * standard TFTP codes, or a UNIX errno + * offset by 100. + */ +void +nak(int error) +{ + struct tftphdr *tp; + int length; + struct errmsg *pe; + + tp = (struct tftphdr *)buf; + tp->th_opcode = htons((u_short)ERROR); + tp->th_code = htons((u_short)error); + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + break; + if (pe->e_code < 0) { + pe->e_msg = strerror(error - 100); + tp->th_code = EUNDEF; /* set 'undef' errorcode */ + } + strcpy(tp->th_msg, pe->e_msg); + length = strlen(pe->e_msg); + tp->th_msg[length] = '\0'; + length += 5; + if (send(peer, buf, length, 0) != length) + syslog(LOG_ERR, "nak: %m"); +}