Initial revision

This commit is contained in:
Michael Brown
2005-05-17 16:44:57 +00:00
parent 75a5374d79
commit 1097cf8685
164 changed files with 24592 additions and 0 deletions

56
contrib/tftp/Makefile Normal file
View File

@@ -0,0 +1,56 @@
#
# Copyright (c) 1987 Regents of the University of California.
# All rights reserved.
#
# Redistribution and use in source and binary forms are permitted
# provided that the above copyright notice and this paragraph are
# duplicated in all such forms and that any documentation,
# advertising materials, and other materials related to such
# distribution and use acknowledge that the software was developed
# by the University of California, Berkeley. The name of the
# University may not be used to endorse or promote products derived
# from this software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# @(#)Makefile 5.8 (Berkeley) 9/20/88
#
# We override /usr/include/arpa/tftp.h with our own because
# we want tu_block to be unsigned short, not short as on most platforms
#
CFLAGS= -I. -O2 -Dsin=sin_x
SRCS= main.c tftp.c tftpsubs.c tftpd.c
OBJS= main.o tftp.o tftpsubs.o
DOBJS= tftpd.o tftpsubs.o
CC= gcc
LIBS= # -linet
all: tftp tftpd
tftp: ${OBJS}
${CC} -o $@ ${CFLAGS} ${OBJS} # -linet
tftpd: ${DOBJS}
${CC} -o $@ ${CFLAGS} ${DOBJS} ${LIBS}
clean:
rm -f ${OBJS} ${DOBJS} core tftp tftpd
cleandir: clean
rm -f tags .depend
depend: ${SRCS}
mkdep ${CFLAGS} ${SRCS}
install:
install -s -o root -g root -m 755 tftp /usr/bin/tftp
install -c -o root -g root -m 444 tftp.1 /usr/man/man1
install -s -o root -g root -m 755 tftpd /usr/sbin/in.tftpd
install -c -o root -g root -m 444 tftpd.8 /usr/man/man8
lint: ${SRCS}
lint ${CFLAGS} ${SRCS}
tags: ${SRCS}
ctags ${SRCS}

28
contrib/tftp/README Normal file
View File

@@ -0,0 +1,28 @@
This is a copy of the TFTP client as available from
ftp://sunsite.unc.edu/pub/linux/system/Network/file-transfer; I
modified the code, so that it understands RFC1782 and RFC1783
extensions to the TFTP protocol. This allows for negotating an
extended transfer block size of up to 1432 bytes (as oppossed to the
standard 512 bytes). On busy networks, this will result in
considerably improved throughput and less load on the network.
For further information and for licensing conditions, please have a
look at the header of the source files.
Markus Gutschke <gutschk@math.uni-muenster.de>
This is a copy of the TFTP server as available from
ftp://sunsite.unc.edu/pub/linux/system/Network/file-transfer; I
modified the code, so that it understands RFC1782 and RFC1783
extensions to the TFTP protocol. This allows for negotating an
extended transfer block size of up to 1432 bytes (as oppossed to the
standard 512 bytes). On busy networks, this will result in
considerably improved throughput and less load on the network.
I also added two command line options for changing the root directory
and for enabling debugging output.
For further information and for licensing conditions, please have a
look at the header of the source files.
Markus Gutschke <gutschk@math.uni-muenster.de>

80
contrib/tftp/arpa/tftp.h Normal file
View File

@@ -0,0 +1,80 @@
/*
* 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.
*
* @(#)tftp.h 8.1 (Berkeley) 6/2/93
*/
#ifndef _ARPA_TFTP_H
#define _ARPA_TFTP_H 1
/*
* Trivial File Transfer Protocol (IEN-133)
*/
#define SEGSIZE 512 /* data segment size */
/*
* Packet types.
*/
#define RRQ 01 /* read request */
#define WRQ 02 /* write request */
#define DATA 03 /* data packet */
#define ACK 04 /* acknowledgement */
#define ERROR 05 /* error code */
struct tftphdr {
short th_opcode; /* packet type */
union {
unsigned short tu_block; /* block # */
short tu_code; /* error code */
char tu_stuff[1]; /* request packet stuff */
} th_u;
char th_data[1]; /* data or error string */
};
#define th_block th_u.tu_block
#define th_code th_u.tu_code
#define th_stuff th_u.tu_stuff
#define th_msg th_data
/*
* Error codes.
*/
#define EUNDEF 0 /* not defined */
#define ENOTFOUND 1 /* file not found */
#define EACCESS 2 /* access violation */
#define ENOSPACE 3 /* disk full or allocation exceeded */
#define EBADOP 4 /* illegal TFTP operation */
#define EBADID 5 /* unknown transfer ID */
#define EEXISTS 6 /* file already exists */
#define ENOUSER 7 /* no such user */
#endif /* arpa/tftp.h */

684
contrib/tftp/main.c Normal file
View File

@@ -0,0 +1,684 @@
/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)main.c 5.8 (Berkeley) 10/11/88";
#endif /* not lint */
/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
/*
* TFTP User Program -- Command Interface.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <setjmp.h>
#include <ctype.h>
#include <netdb.h>
#define TIMEOUT 5 /* secs between rexmt's */
struct sockaddr_in sin;
int f;
short port;
int trace;
int verbose;
int connected;
char mode[32];
char line[200];
int margc;
char *margv[20];
char *prompt = "tftp";
jmp_buf toplevel;
void intr(int);
struct servent *sp;
int segsize = 512;
int quit(), help(), setverbose(), settrace(), status();
int get(), put(), setpeer(), modecmd(), setrexmt(), settimeout();
int setbinary(), setascii(), setblocksize();
#define HELPINDENT (sizeof("connect"))
struct cmd {
char *name;
char *help;
int (*handler)();
};
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";
char bshelp[] = "set blocksize for next transfer";
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 },
{ "blocksize", bshelp, setblocksize },
{ "?", hhelp, help },
0
};
struct cmd *getcmd();
char *tail();
char *index();
char *rindex();
main(argc, argv)
char *argv[];
{
struct sockaddr_in sin;
int top;
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 *)&sin, sizeof (sin));
sin.sin_family = AF_INET;
if (bind(f, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
perror("tftp: bind");
exit(1);
}
strcpy(mode, "netascii");
signal(SIGINT, intr);
if (argc > 1) {
if (setjmp(toplevel) != 0)
exit(0);
setpeer(argc, argv);
}
top = setjmp(toplevel) == 0;
for (;;)
command(top);
}
char hostname[100];
setpeer(argc, argv)
int argc;
char *argv[];
{
struct hostent *host;
if (argc < 2) {
strcpy(line, "Connect ");
printf("(to) ");
fgets(&line[strlen(line)], sizeof(line) - strlen(line) - 1, stdin);
makeargv();
argc = margc;
argv = margv;
}
if (argc > 3) {
printf("usage: %s host-name [port]\n", argv[0]);
return;
}
host = gethostbyname(argv[1]);
if (host) {
sin.sin_family = host->h_addrtype;
bcopy(host->h_addr, &sin.sin_addr, host->h_length);
strcpy(hostname, host->h_name);
} else {
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(argv[1]);
if (sin.sin_addr.s_addr == -1) {
connected = 0;
printf("%s: unknown host\n", argv[1]);
return;
}
strcpy(hostname, argv[1]);
}
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 }
};
modecmd(argc, argv)
char *argv[];
{
register 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) {
setmode(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;
}
setbinary(argc, argv)
char *argv[];
{ setmode("octet");
}
setascii(argc, argv)
char *argv[];
{ setmode("netascii");
}
setmode(newmode)
char *newmode;
{
strcpy(mode, newmode);
if (verbose)
printf("mode set to %s\n", mode);
}
/*
* Send file(s).
*/
put(argc, argv)
char *argv[];
{
int fd;
register int n;
register char *cp, *targ;
if (argc < 2) {
strcpy(line, "send ");
printf("(file) ");
fgets(&line[strlen(line)], sizeof(line) - strlen(line) - 1, stdin);
makeargv();
argc = margc;
argv = margv;
}
if (argc < 2) {
putusage(argv[0]);
return;
}
targ = argv[argc - 1];
if (index(argv[argc - 1], ':')) {
char *cp;
struct hostent *hp;
for (n = 1; n < argc - 1; n++)
if (index(argv[n], ':')) {
putusage(argv[0]);
return;
}
cp = argv[argc - 1];
targ = index(cp, ':');
*targ++ = 0;
hp = gethostbyname(cp);
if (hp == NULL) {
fprintf(stderr, "tftp: %s: ", cp);
herror((char *)NULL);
return;
}
bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
sin.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);
sin.sin_port = port;
sendfile(fd, targ, mode);
return;
}
/* this assumes the target is a directory */
/* on a remote unix system. hmmmm. */
cp = index(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);
sin.sin_port = port;
sendfile(fd, targ, mode);
}
}
putusage(s)
char *s;
{
printf("usage: %s file ... host:target, or\n", s);
printf(" %s file ... target (when already connected)\n", s);
}
/*
* Receive file(s).
*/
get(argc, argv)
char *argv[];
{
int fd;
register int n;
register char *cp;
char *src;
if (argc < 2) {
strcpy(line, "get ");
printf("(files) ");
fgets(&line[strlen(line)], sizeof(line) - strlen(line) - 1, stdin);
makeargv();
argc = margc;
argv = margv;
}
if (argc < 2) {
getusage(argv[0]);
return;
}
if (!connected) {
for (n = 1; n < argc ; n++)
if (index(argv[n], ':') == 0) {
getusage(argv[0]);
return;
}
}
for (n = 1; n < argc ; n++) {
src = index(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)&sin.sin_addr, hp->h_length);
sin.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);
sin.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);
sin.sin_port = port;
recvfile(fd, src, mode);
}
}
getusage(s)
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;
setrexmt(argc, argv)
char *argv[];
{
int t;
if (argc < 2) {
strcpy(line, "Rexmt-timeout ");
printf("(value) ");
fgets(&line[strlen(line)], sizeof(line) - strlen(line) - 1, 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("%d: bad value\n", t);
else
rexmtval = t;
}
int maxtimeout = 5 * TIMEOUT;
settimeout(argc, argv)
char *argv[];
{
int t;
if (argc < 2) {
strcpy(line, "Maximum-timeout ");
printf("(value) ");
fgets(&line[strlen(line)], sizeof(line) - strlen(line) - 1, 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("%d: bad value\n", t);
else
maxtimeout = t;
}
status(argc, argv)
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(filename)
char *filename;
{
register char *s;
while (*filename) {
s = rindex(filename, '/');
if (s == NULL)
break;
if (s[1])
return (s + 1);
*s = '\0';
}
return (filename);
}
/*
* Command parser.
*/
command(top)
int top;
{
register struct cmd *c;
if (!top)
putchar('\n');
for (;;) {
printf("%s> ", prompt);
if (fgets(line, sizeof(line), stdin) == 0) {
if (feof(stdin)) {
quit();
} else {
continue;
}
}
if (line[0] == 0)
continue;
makeargv();
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(name)
register char *name;
{
register char *p, *q;
register struct cmd *c, *found;
register int nmatches, longest;
longest = 0;
nmatches = 0;
found = 0;
for (c = cmdtab; p = c->name; 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.
*/
makeargv()
{
register char *cp;
register 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;
}
/*VARARGS*/
quit()
{
exit(0);
}
/*
* Help command.
*/
help(argc, argv)
int argc;
char *argv[];
{
register 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", HELPINDENT, c->name, c->help);
return;
}
while (--argc > 0) {
register 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);
}
}
/*VARARGS*/
settrace()
{
trace = !trace;
printf("Packet tracing %s.\n", trace ? "on" : "off");
}
/*VARARGS*/
setverbose()
{
verbose = !verbose;
printf("Verbose mode %s.\n", verbose ? "on" : "off");
}
setblocksize(argc, argv)
char *argv[];
{
int t;
if (argc < 2) {
strcpy(line, "blocksize ");
printf("(value) ");
fgets(&line[strlen(line)], sizeof(line) - strlen(line) - 1, stdin);
makeargv();
argc = margc;
argv = margv;
}
if (argc != 2) {
printf("usage: %s value\n", argv[0]);
return;
}
t = atoi(argv[1]);
if (t < 8 || t > 1432)
printf("%d: bad value\n", t);
else
segsize = t;
}

159
contrib/tftp/tftp.1 Normal file
View File

@@ -0,0 +1,159 @@
.\" Copyright (c) 1986 The Regents of the University of California.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms are permitted
.\" provided that the above copyright notice and this paragraph are
.\" duplicated in all such forms and that any documentation,
.\" advertising materials, and other materials related to such
.\" distribution and use acknowledge that the software was developed
.\" by the University of California, Berkeley. The name of the
.\" University may not be used to endorse or promote products derived
.\" from this software without specific prior written permission.
.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
.\"
.\" @(#)tftp.1 5.3 (Berkeley) 9/20/88
.\"
.TH TFTP 1 "September 20, 1988"
.UC 6
.SH NAME
tftp \- trivial file transfer program
.SH SYNOPSIS
.B tftp
[
host
]
.SH DESCRIPTION
.I Tftp
is the user interface to the Internet TFTP
(Trivial File Transfer Protocol),
which allows users to transfer files to and from a remote machine.
The remote
.I host
may be specified on the command line, in which case
.I tftp
uses
.I host
as the default host for future transfers (see the
.B connect
command below).
.SH COMMANDS
Once
.I tftp
is running, it issues the prompt
.B tftp>
and recognizes the following commands:
.TP
\fBconnect\fP \fIhost-name\fP [ \fIport\fP ]
Set the
.I host
(and optionally
.IR port )
for transfers.
Note that the TFTP protocol, unlike the FTP protocol,
does not maintain connections betweeen transfers; thus, the
.I 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
.I connect
command; the remote host can be specified as part of the
.I get
or
.I put
commands.
.TP
\fBmode\fP \fItransfer-mode\fP
Set the mode for transfers;
.I transfer-mode
may be one of
.IR ascii
or
.IR binary .
The default is
.IR ascii .
.TP
\fBput\fP \fIfile\fP
.ns
.TP
\fBput\fP \fIlocalfile remotefile\fP
.ns
.TP
\fBput\fP \fIfile1 file2 ... fileN remote-directory\fP
Put a file or set of files to the specified
remote file or directory.
The destination
can be in one of two forms:
a filename on the remote host, if the host has already been specified,
or a string of the form
.I host:filename
to specify both a host and filename at the same time.
If the latter form is used,
the hostname specified becomes the default for future transfers.
If the remote-directory form is used, the remote host is
assumed to be a
.I UNIX
machine.
.TP
\fBget\fP \fIfilename\fP
.ns
.TP
\fBget\fP \fIremotename\fP \fIlocalname\fP
.ns
.TP
\fBget\fP \fIfile1\fP \fIfile2\fP ... \fIfileN\fP
Get a file or set of files from the specified
.IR sources .
.I 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
.I host:filename
to specify both a host and filename at the same time.
If the latter form is used,
the last hostname specified becomes the default for future transfers.
.TP
.B quit
Exit
.IR tftp .
An end of file also exits.
.TP
.B verbose
Toggle verbose mode.
.TP
.B trace
Toggle packet tracing.
.TP
.B status
Show current status.
.TP
\fBrexmt\fP \fIretransmission-timeout\fP
Set the per-packet retransmission timeout, in seconds.
.TP
\fBtimeout\fP \fItotal-transmission-timeout\fP
Set the total transmission timeout, in seconds.
.TP
.B ascii
Shorthand for "mode ascii"
.TP
.B binary
Shorthand for "mode binary"
.TP
\fBblocksize\fP \fItransfer-blocksize\fP
Set the blocksize that is used for transfers. This assumes that the
server knows about RFC1782 and RFC1783 extensions to the TFTP
protocol; automatic fallback is supported and will result in a default
blocksize of 512 octets.
.TP
\fB?\fP \ [ \fIcommand-name\fP ... ]
Print help information.
.SH BUGS
.PP
Because there is no user-login or validation within
the
.I 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.

536
contrib/tftp/tftp.c Normal file
View File

@@ -0,0 +1,536 @@
/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static char sccsid[] = "@(#)tftp.c 5.7 (Berkeley) 6/29/88";
#endif /* not lint */
/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
/*
* TFTP User Program -- Protocol Machines
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/tftp.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <setjmp.h>
extern int errno;
extern struct sockaddr_in sin; /* filled in by main */
extern int f; /* the opened socket */
extern int trace;
extern int verbose;
extern int rexmtval;
extern int maxtimeout;
extern int segsize;
#define PKTSIZE (1432+4) /* SEGSIZE+4 */
char ackbuf[PKTSIZE];
int timeout;
jmp_buf toplevel;
jmp_buf timeoutbuf;
#ifndef OACK
#define OACK 6
#endif
void timer(int sig)
{
signal(SIGALRM, timer);
timeout += rexmtval;
if (timeout >= maxtimeout) {
printf("Transfer timed out.\n");
longjmp(toplevel, -1);
}
longjmp(timeoutbuf, 1);
}
strnlen(s, n)
char *s;
int n;
{
int i = 0;
while (n-- > 0 && *s++) i++;
return(i);
}
/*
* Parse an OACK package and set blocksize accordingly
*/
parseoack(cp, sz)
char *cp;
int sz;
{
int n;
segsize = 512;
while (sz > 0 && *cp) {
n = strnlen(cp, sz);
if (n == 7 && !strncmp("blksize", cp, 7)) {
cp += 8;
sz -= 8;
if (sz <= 0)
break;
for (segsize = 0, n = strnlen(cp, sz); n > 0;
n--, cp++, sz--) {
if (*cp < '0' || *cp > '9')
break;
segsize = 10*segsize + *cp - '0'; }
}
cp += n + 1;
sz -= n + 1;
}
if (segsize < 8 || segsize > 1432) {
printf("Remote host negotiated illegal blocksize %d\n",
segsize);
segsize = 512;
longjmp(timeoutbuf, -1);
}
}
/*
* Send the requested file.
*/
sendfile(fd, name, mode)
int fd;
char *name;
char *mode;
{
register struct tftphdr *ap; /* data and ack packets */
struct tftphdr *r_init(), *dp;
register int size, n;
u_short block = 0;
register unsigned long amount = 0;
struct sockaddr_in from;
int fromlen;
int convert; /* true if doing nl->crlf conversion */
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");
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(block);
}
timeout = 0;
(void) setjmp(timeoutbuf);
send_data:
if (trace)
tpacket("sent", dp, size + 4);
n = sendto(f, dp, size + 4, 0, (struct sockaddr *)&sin,
sizeof (sin));
if (n != size + 4) {
perror("tftp: sendto");
goto abort;
}
if (block) /* do not start reading until the blocksize
has been negotiated */
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;
}
sin.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);
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;
ap->th_block = ntohs(ap->th_block);
if (block == 0) {
if (trace)
printf("server does not know "
"about RFC1782; reset"
"ting blocksize\n");
segsize = 512;
}
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;
}
}
else if (ap->th_opcode == OACK) {
if (block) {
printf("protocol violation\n");
longjmp(toplevel, -1);
}
parseoack(&ap->th_stuff, n - 2);
break;
}
}
if (block > 0)
amount += size;
else
read_ahead(file, convert);
block++;
} while (size == segsize || block == 1);
abort:
fclose(file);
stopclock();
if (amount > 0)
printstats("Sent", amount);
}
/*
* Receive a file.
*/
recvfile(fd, name, mode)
int fd;
char *name;
char *mode;
{
register struct tftphdr *ap;
struct tftphdr *dp, *w_init();
register int n, size;
u_short block = 1;
unsigned long amount = 0;
struct sockaddr_in from;
int fromlen, firsttrip = 1;
FILE *file;
int convert; /* true if converting crlf -> lf */
int waitforoack = 1;
startclock();
dp = w_init();
ap = (struct tftphdr *)ackbuf;
file = fdopen(fd, "w");
convert = !strcmp(mode, "netascii");
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(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 *)&sin,
sizeof (sin)) != size) {
alarm(0);
perror("tftp: sendto");
goto abort;
}
if (!waitforoack)
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;
}
sin.sin_port = from.sin_port; /* added */
if (trace)
tpacket("received", dp, n);
/* should verify client address */
dp->th_opcode = ntohs(dp->th_opcode);
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 (waitforoack) {
if (trace)
printf("server does not know "
"about RFC1782; reset"
"ting blocksize\n");
waitforoack = 0;
segsize = 512;
}
dp->th_block = ntohs(dp->th_block);
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 */
}
}
else if (dp->th_opcode == OACK) {
if (block != 1 || !waitforoack) {
printf("protocol violation\n");
longjmp(toplevel, -1);
}
waitforoack = 0;
parseoack(&dp->th_stuff, n - 2);
ap->th_opcode = htons((u_short)ACK);
ap->th_block = htons(0);
size = 4;
goto send_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(block);
(void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&sin, sizeof (sin));
write_behind(file, convert); /* flush last buffer */
fclose(file);
stopclock();
if (amount > 0)
printstats("Received", amount);
}
makerequest(request, name, tp, mode)
int request;
char *name, *mode;
struct tftphdr *tp;
{
register 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';
strcpy(cp, "blksize");
cp += 7;
*cp++ = '\0';
sprintf(cp, "%d", segsize);
cp += strlen(cp) + 1;
return (cp - (char *)tp);
}
struct errmsg {
int e_code;
const char *e_msg;
} errmsgs[] = {
{ EUNDEF, "Undefined error code" },
{ ENOTFOUND, "File not found" },
{ EACCESS, "Access violation" },
{ ENOSPACE, "Disk full or allocation exceeded" },
{ EBADOP, "Illegal TFTP operation" },
{ EBADID, "Unknown transfer ID" },
{ EEXISTS, "File already exists" },
{ ENOUSER, "No such user" },
{ -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.
*/
nak(error)
int error;
{
register struct tftphdr *tp;
int length;
register struct errmsg *pe;
/* extern char *sys_errlist[]; */
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 = sys_errlist[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 *)&sin, sizeof (sin))
!= length)
perror("nak");
}
topts(cp, sz)
char *cp;
int sz;
{
int n, i = 0;
while (sz > 0 && *cp) {
n = strnlen(cp, sz);
if (n > 0) {
printf("%s%s=", i++ ? ", " : "", cp);
cp += n + 1;
sz -= n + 1;
if (sz <= 0)
break;
n = strnlen(cp, sz);
if (n > 0)
printf("%s", cp);
}
cp += n + 1;
sz -= n + 1;
}
}
tpacket(s, tp, n)
char *s;
struct tftphdr *tp;
int n;
{
static char *opcodes[] =
{ "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
register char *cp, *file;
u_short op = ntohs(tp->th_opcode);
char *index();
if (op < RRQ || op > OACK)
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 = index(cp, '\0');
printf("<file=%s, mode=%s, opts: ", file, cp + 1);
topts(index(cp + 1, '\000') + 1, n - strlen(file)
- strlen(cp + 1) - 2);
printf(">\n");
break;
case DATA:
printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
break;
case ACK:
printf("<block=%d>\n", ntohs(tp->th_block));
break;
case ERROR:
printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
break;
case OACK:
printf("<");
topts(tp->th_stuff, n - 2);
printf(">\n");
break;
}
}
struct timeval tstart;
struct timeval tstop;
struct timezone zone;
startclock() {
gettimeofday(&tstart, &zone);
}
stopclock() {
gettimeofday(&tstop, &zone);
}
printstats(direction, amount)
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 %ld bytes in %.1f seconds", direction, amount, delta);
if ((verbose) && (delta >= 0.1))
printf(" [%.0f bits/sec]", (amount*8.)/delta);
putchar('\n');
}

75
contrib/tftp/tftpd.8 Normal file
View File

@@ -0,0 +1,75 @@
.\" Copyright (c) 1983 The Regents of the University of California.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms are permitted
.\" provided that the above copyright notice and this paragraph are
.\" duplicated in all such forms and that any documentation,
.\" advertising materials, and other materials related to such
.\" distribution and use acknowledge that the software was developed
.\" by the University of California, Berkeley. The name of the
.\" University may not be used to endorse or promote products derived
.\" from this software without specific prior written permission.
.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
.\"
.\" @(#)tftpd.8 6.3 (Berkeley) 9/20/88
.\"
.TH TFTPD 8 "September 20, 1988"
.UC 5
.SH NAME
tftpd \- DARPA Trivial File Transfer Protocol server
.SH SYNOPSIS
.B /etc/tftpd
[
.SM \-c
<rootdir> ] [
.SM \-d
] [
.SM \-r
<filter> ]
.SH DESCRIPTION
.I Tftpd
is a server which supports the DARPA Trivial File Transfer
Protocol.
The TFTP server operates
at the port indicated in the ``tftp'' service description;
see
.IR services (5).
The server is normally started by
.IR inetd (8).
.PP
The use of
.I tftp
does not require an account or password on the remote system.
Due to the lack of authentication information,
.I tftpd
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 ``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.
.SH OPTIONS
.TP
.B \-c
Pathname of a directory that is considered the rootdirectory for all
transfers. N.B.
.I tftpd
does not actually perform a
.IR chroot (2)
call; you should be aware of the security implications and you
probably should run the server from an unpriviledged account.
.TP
.B \-d
Increased debugging level.
.TP
.B \-r
Pathname of a file that is considered to be a filter program. Whenever
a client tries to download this file, the filter will be started and
its output is send to the client. An arbitrary amount of these
filters can be specified.
.SH "SEE ALSO"
tftp(1), inetd(8)

742
contrib/tftp/tftpd.c Normal file
View File

@@ -0,0 +1,742 @@
/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)tftpd.c 5.8 (Berkeley) 6/18/88";
#endif /* not lint */
/*
* Trivial file transfer protocol server.
*
* This version includes many modifications by Jim Guyton <guyton@rand-unix>
*
* Further modifications by Markus Gutschke <gutschk@math.uni-muenster.de>
* - RFC1782 option parsing
* - RFC1783 extended blocksize
* - "-c" option for changing the root directory
* - "-d" option for debugging output
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/tftp.h>
#include <alloca.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <netdb.h>
#include <setjmp.h>
#include <syslog.h>
#define TIMEOUT 5
#ifndef OACK
#define OACK 06
#endif
#ifndef EOPTNEG
#define EOPTNEG 8
#endif
extern int errno;
struct sockaddr_in sin = { AF_INET };
int peer;
int rexmtval = TIMEOUT;
int maxtimeout = 5*TIMEOUT;
#define PKTSIZE (1432+4) /* SEGSIZE+4 */
int segsize = SEGSIZE;
char buf[PKTSIZE];
char ackbuf[PKTSIZE];
struct sockaddr_in from;
int fromlen;
char *rootdir = NULL;
int debug = 0;
struct filters {
struct filters *next;
char *fname;
} *filters = NULL;
int isfilter = 0;
main(argc, argv)
char *argv[];
{
register struct tftphdr *tp;
register int n;
int on = 1;
extern int optind;
extern char *optarg;
openlog(argv[0], LOG_PID, LOG_DAEMON);
while ((n = getopt(argc, argv, "c:dr:")) >= 0) {
switch (n) {
case 'c':
if (rootdir)
goto usage;
rootdir = optarg;
break;
case 'd':
debug++;
break;
case 'r': {
struct filters *fp = (void *)
malloc(sizeof(struct filters) +
strlen(optarg) + 1);
fp->next = filters;
fp->fname = (char *)(fp + 1);
strcpy(fp->fname, optarg);
filters = fp;
break; }
default:
usage:
syslog(LOG_ERR, "Usage: %s [-c chroot] "
"[-r readfilter] [-d]\n",
argv[0]);
exit(1);
}
}
if (argc-optind != 0)
goto usage;
ioctl(0, FIONBIO, &on);
/* if (ioctl(0, FIONBIO, &on) < 0) {
syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
exit(1);
}
*/
fromlen = sizeof (from);
n = recvfrom(0, buf, segsize+4, 0,
(struct sockaddr *)&from, &fromlen);
if (n < 0) {
syslog(LOG_ERR, "recvfrom: %m\n");
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.
*/
{
int pid;
int i, j;
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(0, buf, segsize+4, 0,
(struct sockaddr *)&from, &j);
if (i > 0) {
n = i;
fromlen = j;
}
} else {
break;
}
}
if (pid < 0) {
syslog(LOG_ERR, "fork: %m\n");
exit(1);
} else if (pid != 0) {
exit(0);
}
}
from.sin_family = AF_INET;
alarm(0);
close(0);
close(1);
peer = socket(AF_INET, SOCK_DGRAM, 0);
if (peer < 0) {
syslog(LOG_ERR, "socket: %m\n");
exit(1);
}
if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
syslog(LOG_ERR, "bind: %m\n");
exit(1);
}
if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
syslog(LOG_ERR, "connect: %m\n");
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(1);
}
int validate_access();
int sendfile(), recvfile();
struct formats {
char *f_mode;
int (*f_validate)();
int (*f_send)();
int (*f_recv)();
int f_convert;
} formats[] = {
{ "netascii", validate_access, sendfile, recvfile, 1 },
{ "octet", validate_access, sendfile, recvfile, 0 },
#ifdef notdef
{ "mail", validate_user, sendmail, recvmail, 1 },
#endif
{ 0 }
};
int set_blksize();
struct options {
char *o_opt;
int (*o_fnc)();
} options[] = {
{ "blksize", set_blksize },
{ 0 }
};
/*
* Set a non-standard block size (c.f. RFC1783)
*/
set_blksize(val, ret)
char *val;
char **ret;
{
static char b_ret[5];
int sz = atoi(val);
if (sz < 8) {
if (debug)
syslog(LOG_ERR, "Requested packetsize %d < 8\n", sz);
return(0);
} else if (sz > PKTSIZE-4) {
if (debug)
syslog(LOG_INFO, "Requested packetsize %d > %d\n",
sz, PKTSIZE-4);
sz = PKTSIZE-4;
} else if (debug)
syslog(LOG_INFO, "Adjusted packetsize to %d octets\n", sz);
segsize = sz;
sprintf(*ret = b_ret, "%d", sz);
return(1);
}
/*
* Parse RFC1782 style options
*/
do_opt(opt, val, ap)
char *opt;
char *val;
char **ap;
{
struct options *po;
char *ret;
for (po = options; po->o_opt; po++)
if (strcasecmp(po->o_opt, opt) == 0) {
if (po->o_fnc(val, &ret)) {
if (*ap + strlen(opt) + strlen(ret) + 2 >=
ackbuf + sizeof(ackbuf)) {
if (debug)
syslog(LOG_ERR,
"Ackbuf overflow\n");
nak(ENOSPACE);
exit(1);
}
*ap = strrchr(strcpy(strrchr(strcpy(*ap, opt),
'\000')+1, val),
'\000')+1;
} else {
nak(EOPTNEG);
exit(1);
}
break;
}
if (debug && !po->o_opt)
syslog(LOG_WARNING, "Unhandled option: %d = %d\n", opt, val);
return;
}
/*
* Handle initial connection protocol.
*/
tftp(tp, size)
struct tftphdr *tp;
int size;
{
register char *cp;
int argn = 0, ecode;
register struct formats *pf;
char *filename, *mode;
char *val, *opt;
char *ap = ackbuf+2;
int isopts;
((struct tftphdr *)ackbuf)->th_opcode = ntohs(OACK);
filename = cp = tp->th_stuff;
again:
while (cp < buf + size) {
if (*cp == '\0')
break;
cp++;
}
if (*cp != '\0') {
if (debug)
syslog(LOG_WARNING, "Received illegal request\n");
nak(EBADOP);
exit(1);
}
if (!argn++) {
mode = ++cp;
goto again;
} else {
if (debug && argn == 3)
syslog(LOG_INFO, "Found RFC1782 style options\n");
*(argn & 1 ? &val : &opt) = ++cp;
if (argn & 1)
do_opt(opt, val, &ap);
if (cp < buf + size && *cp != '\000')
goto again;
}
for (cp = mode; *cp; cp++)
if (isupper(*cp))
*cp = tolower(*cp);
for (pf = formats; pf->f_mode; pf++)
if (strcmp(pf->f_mode, mode) == 0)
break;
if (pf->f_mode == 0) {
if (debug)
syslog(LOG_WARNING, "Unknown data format: %s\n", mode);
nak(EBADOP);
exit(1);
}
if (rootdir) {
cp = alloca(strlen(rootdir) + strlen(filename) + 1);
if (cp == NULL) {
nak(100+ENOMEM);
exit(1);
}
if (*filename != '/') {
if (debug)
syslog(LOG_ERR,
"Filename has to be absolute: %s\n",
filename);
nak(EACCESS);
exit(1);
}
filename = strcat(strcpy(cp, rootdir), filename);
}
ecode = (*pf->f_validate)(filename, tp->th_opcode);
if (ecode) {
nak(ecode, ERROR);
exit(1);
}
isopts = ap != (ackbuf+2);
(tp->th_opcode == WRQ ? *pf->f_recv : *pf->f_send)
(pf, isopts ? ackbuf : NULL, isopts ? ap-ackbuf : 0);
exit(0);
}
FILE *file;
/*
* Validate file access. Since we
* have no uid or gid, for now require
* file to exist and be publicly
* readable/writable.
* Note also, full path name must be
* given as we have no login directory.
*/
validate_access(filename, mode)
char *filename;
int mode;
{
struct stat stbuf;
int fd;
char *cp;
isfilter = 0;
if (mode == RRQ) {
struct filters *fp = filters;
for (; fp; fp = fp->next) {
if (!strcmp(fp->fname,
filename +
(rootdir ? strlen(rootdir) : 0))) {
if (debug)
syslog(LOG_INFO, "Opening input "
"filter: %s\n", filename);
if ((file = popen(filename, "r")) == NULL) {
syslog(LOG_ERR, "Failed to open input "
"filter\n");
return (EACCESS); }
fd = fileno(file);
isfilter = 1;
return (0);
}
}
}
if (*filename != '/') {
if (debug)
syslog(LOG_ERR, "Filename has to be absolute: %s\n",
filename);
return (EACCESS);
}
for (cp = filename; *cp; cp++)
if (*cp == '~' || *cp == '$' ||
(*cp == '/' && cp[1] == '.' && cp[2] == '.')) {
if (debug)
syslog(LOG_ERR, "Illegal filename: %s\n",
filename);
return (EACCESS);
}
if (debug)
syslog(LOG_INFO, "Validating \"%s\" for %sing\n",
filename, mode == RRQ ? "read" : "writ");
if (stat(filename, &stbuf) < 0)
return (errno == ENOENT ? ENOTFOUND : EACCESS);
if (mode == RRQ) {
if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
return (EACCESS);
} else {
if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
return (EACCESS);
}
fd = open(filename, mode == RRQ ? 0 : 1);
if (fd < 0)
return (errno + 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) {
if (debug)
syslog(LOG_WARNING, "Timeout!\n");
exit(1);
}
longjmp(timeoutbuf, 1);
}
/*
* Send the requested file.
*/
sendfile(pf, oap, oacklen)
struct formats *pf;
struct tftphdr *oap;
int oacklen;
{
struct tftphdr *dp, *r_init();
register struct tftphdr *ap; /* ack packet */
register int size, n;
u_short block = 1;
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: write: %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(ap->th_block);
if (ap->th_opcode == ERROR) {
if (debug)
syslog(LOG_ERR, "Client does not "
"accept options\n");
goto abort; }
if (ap->th_opcode == ACK) {
if (ap->th_block == 0) {
if (debug)
syslog(LOG_DEBUG,
"RFC1782 option "
"negotiation "
"succeeded\n");
break;
}
/* Re-synchronize 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(block);
timeout = 0;
(void) setjmp(timeoutbuf);
send_data:
if (send(peer, dp, size + 4, 0) != size + 4) {
syslog(LOG_ERR, "tftpd: write: %m\n");
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: %m\n");
goto abort;
}
ap->th_opcode = ntohs((u_short)ap->th_opcode);
ap->th_block = ntohs(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:
if (isfilter)
pclose(file);
else
(void) fclose(file);
isfilter = 0;
}
void justquit(int sig)
{
exit(0);
}
/*
* Receive a file.
*/
recvfile(pf, oap, oacklen)
struct formats *pf;
struct tftphdr *oap;
int oacklen;
{
struct tftphdr *dp, *w_init();
register struct tftphdr *ap; /* ack buffer */
register int acksize, n, size;
u_short block = 0;
signal(SIGALRM, timer);
dp = w_init();
do {
timeout = 0;
if (!block++ && oap) {
ap = (struct tftphdr *)oap;
acksize = oacklen;
} else {
ap = (struct tftphdr *)ackbuf;
ap->th_opcode = htons((u_short)ACK);
ap->th_block = htons(block-1);
acksize = 4;
}
(void) setjmp(timeoutbuf);
send_ack:
if (send(peer, (char *)ap, acksize, 0) != acksize) {
syslog(LOG_ERR, "tftpd: write: %m\n");
goto abort;
}
write_behind(file, pf->f_convert);
for ( ; ; ) {
alarm(rexmtval);
n = recv(peer, dp, segsize+4, 0);
alarm(0);
if (n < 0) { /* really? */
syslog(LOG_ERR, "tftpd: read: %m\n");
goto abort;
}
dp->th_opcode = ntohs((u_short)dp->th_opcode);
dp->th_block = ntohs(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);
if (isfilter)
pclose(file);
else
(void) fclose(file); /* close data file */
isfilter = 0;
ap = (struct tftphdr *)ackbuf;
ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */
ap->th_block = htons(block);
(void) send(peer, ackbuf, 4, 0);
signal(SIGALRM, justquit); /* just quit on timeout */
alarm(rexmtval);
n = recv(peer, buf, segsize, 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;
const char *e_msg;
} errmsgs[] = {
{ EUNDEF, "Undefined error code" },
{ ENOTFOUND, "File not found" },
{ EACCESS, "Access violation" },
{ ENOSPACE, "Disk full or allocation exceeded" },
{ EBADOP, "Illegal TFTP operation" },
{ EBADID, "Unknown transfer ID" },
{ EEXISTS, "File already exists" },
{ ENOUSER, "No such user" },
{ EOPTNEG, "Failure to negotiate RFC1782 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.
*/
nak(error)
int error;
{
register struct tftphdr *tp;
int length;
register struct errmsg *pe;
/* extern char *sys_errlist[]; */
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 = sys_errlist[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 (debug)
syslog(LOG_ERR, "Negative acknowledge: %s\n", tp->th_msg);
if (send(peer, buf, length, 0) != length)
syslog(LOG_ERR, "nak: %m\n");
}

260
contrib/tftp/tftpsubs.c Normal file
View File

@@ -0,0 +1,260 @@
/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static char sccsid[] = "@(#)tftpsubs.c 5.4 (Berkeley) 6/29/88";
#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 <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/tftp.h>
#include <stdio.h>
#define PKTSIZE (1432+4) /* SEGSIZE+4 */ /* should be moved to tftp.h */
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 */
extern int segsize;
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) */
struct tftphdr *rw_init();
struct tftphdr *w_init() { return rw_init(0); } /* write-behind */
struct tftphdr *r_init() { return rw_init(1); } /* read-ahead */
struct tftphdr *
rw_init(x) /* init for either read-ahead or write-behind */
int x; /* zero for write-behind, one for read-head */
{
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.
*/
readit(file, dpp, convert)
FILE *file; /* file opened for read */
struct tftphdr **dpp;
int convert; /* if true, convert to ascii */
{
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
*/
read_ahead(file, convert)
FILE *file; /* file opened for read */
int convert; /* if true, convert to ascii */
{
register int i;
register char *p;
register 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) {
int i;
b->counter = 0;
do {
i = read(fileno(file), dp->th_data + b->counter,
segsize - b->counter);
if (i > 0)
b->counter += i;
} while (i != 0 && !(i < 0 && errno != EINTR) &&
b->counter < 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.
*/
writeit(file, dpp, ct, convert)
FILE *file;
struct tftphdr **dpp;
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 */
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.
*/
write_behind(file, convert)
FILE *file;
int convert;
{
char *buf;
int count;
register int ct;
register char *p;
register 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(f)
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);
}
}
}