|
|
Power 6/32 Unix version 1.21
#ifndef lint
static char sccsid[] = "@(#)tftp.c 4.7 (Berkeley) 8/11/83";
#endif
/*
* TFTP User Program -- Protocol Machines
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/tftp.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <setjmp.h>
/*----- to report system call error -----*/
extern int errno;
extern struct sockaddr_in sin;
extern char mode[];
/*----- various options and their values -----*/
extern int f;
extern int trace;
extern int verbose;
extern int connected;
extern int rexmtval;
extern int maxtimeout;
/*----- control messages -------*/
static char buf[BUFSIZ];
/*----- communication connections ------*/
static struct sockaddr_in to;
/*----- handling asynchronous events ----- */
static int timeout;
static jmp_buf timeoutbuf; /* timer expires */
static jmp_buf intrbuf; /* user abort */
extern char *inet_ntoa(); /* unix library call */
/*---------------------------------------------------
<<<<<<<<<<<<<<<<<<<<<<<<
<<<<<<< NOTES <<<<<<<<<< It does not pay to diff this version with
<<<<<<<<<<<<<<<<<<<<<<<< the original ucb version as this version
<<<<<<<<<<<<<<<<<<<<<<<< is nearly rewritten.
Changes:
. always report result even though transmitting 0 bytes
in recvfile() and sendfile(). Add local variable haserr
for this purpose.
. add global variable "to".
at start of receiving or sending files, copy daemon address
"sin" to "to". send request to daemon throught "to".
when daemon child returns its address, set "to" = address
of daemon child. from then on, communicate with daemon child
(through "to").
. connect to child daemon for performance and to ensure that
strayed messages are discarded by the system.
. close the current file when timed out.
. correct the connect() system call (call syntax was wrong)
in nak().
. close the current file and socket when interrupted.
----------------------------------------------------*/
timer()
{
longjmp(timeoutbuf, 1);
}
xmitintr()
{
alarm(0); /* turn off alarm */
longjmp(intrbuf, 1);
}
/*-----------------------------------------
* Send the requested file.
-----------------------------------------*/
sendfile(currentfile, name)
FILE *currentfile;
char *name;
{
/*-------- data transfer parameters */
register struct tftphdr *tp = (struct tftphdr *)buf;
register int block = 0;
int xamount = 0; /* # bytes transmitted */
int xsize;
int famount = 0; /* # bytes read from file */
int fsize;
int n;
int haserr = 0;
/*-------- connection parameters */
struct sockaddr_in from;
int fromlen;
/*-------- statistics parameters */
time_t start = time(0);
time_t delta;
/*----- others */
int (*oldintr)(); /* interrupt handler */
(void)signal(SIGALRM, timer);
oldintr = signal(SIGINT, xmitintr);
if (setjmp(intrbuf))
goto abort;
do {
/*----- send request and ack -------*/
timeout = 0;
if (setjmp(timeoutbuf)) {
timeout += rexmtval;
if (timeout >= maxtimeout)
goto expire;
}
if (trace)
tpacket("sent", tp, xsize + 4);
if (block == 0) {
/* first trip */
xsize = makerequest(WRQ, name) - 4;
getsock();
to = sin;
n = sendto(f, buf, xsize + 4, 0, (caddr_t)&to,
sizeof (to));
}
else {
xsize = tftpread(currentfile, tp->th_data, SEGSIZE,
&fsize);
if (xsize < 0) {
perror("tftp:sendfile:read fails");
nak(errno + 100);
break;
}
tp->th_opcode = htons((u_short)DATA);
tp->th_block = htons((u_short)block);
n = send(f, buf, xsize+4, 0);
#ifdef DEBUG
fprintf(stderr,"send %d bytes, block %d\n",
xsize, block);
#endif
}
if (n != xsize + 4) {
perror("tftp: sendto or send fails");
haserr++;
goto abort;
}
/* get response and data */
do {
alarm(rexmtval);
do {
fromlen = sizeof (from);
n = recvfrom(f, buf, sizeof (buf), 0,
(caddr_t)&from, &fromlen);
} while (n <= 0);
alarm(0);
if (n < 0) {
perror("tftp: recvfrom fails");
haserr++;
goto abort;
}
#ifdef DEBUG
fprintf(stderr,"sendfile: recv from %s %d\n",
inet_ntoa(from.sin_addr), from.sin_port);
#endif
if (trace)
tpacket("received", tp, n, from.sin_port);
/* use the address returned by daemon child */
if (block == 0) {
/* first trip */
if (connect(f, (char *)&from, sizeof(from))<0){
perror("tftp: connect fails");
haserr++;
goto abort;
}
to.sin_addr = from.sin_addr;
to.sin_port = from.sin_port;
}
tp->th_opcode = ntohs(tp->th_opcode);
tp->th_block = ntohs(tp->th_block);
if (tp->th_opcode == ERROR) {
printf("Error code %d: %s\n", tp->th_code,
tp->th_msg);
haserr++;
goto abort;
}
} while (tp->th_opcode != ACK ||
block != tp->th_block);
if (block > 0) {
xamount += xsize;
famount += fsize;
}
block++;
} while (xsize == SEGSIZE ||
block == 1);
abort:
done:
fclose(currentfile);
(void) close(f), f = -1;
if (haserr == 0 ||
xamount >0) {
delta = time(0) - start;
printf("Read %d bytes and sent %d bytes in %d seconds.\n",
famount, xamount, delta);
}
signal(SIGINT, oldintr);
oldintr = 0;
return;
expire:
printf("Transfer timed out.\n");
goto abort;
}
/*---------------------------------------------
* Receive a file.
----------------------------------------*/
recvfile(currentfile, name)
FILE *currentfile;
char *name;
{
/*----- file transfer bufs -------------*/
register struct tftphdr *tp = (struct tftphdr *)buf;
register int block = 1;
int xamount = 0;
int xsize;
int famount = 0;
int fsize;
int firsttrip = 1;
int n,m;
int haserr = 0;
long segsize; /* for debugging purposes */
/*----- connection --------*/
struct sockaddr_in from;
int fromlen;
/*----- statistics --------*/
time_t start = time(0);
time_t delta;
/*----- others -------*/
int (*oldintr)();
signal(SIGALRM, timer);
oldintr = signal(SIGINT, xmitintr);
if (setjmp(intrbuf))
goto abort;
/* file transfer */
do {
/* send request/ acknowledgement */
timeout = 0;
if (setjmp(timeoutbuf)) {
timeout += rexmtval;
if (timeout >= maxtimeout)
goto expire;
}
if (trace)
tpacket("sent", tp, xsize);
if (firsttrip) {
xsize = makerequest(RRQ, name);
getsock();
to = sin;
n = sendto(f, buf, xsize, 0, (caddr_t)&to, sizeof(to));
} else {
tp->th_opcode = htons((u_short)ACK);
tp->th_block = htons((u_short)(block));
xsize = 4;
block++;
n = send(f, buf, xsize, 0);
}
if (n != xsize) {
alarm(0);
perror("tftp: sendto or send fails");
haserr++;
goto abort;
}
/* get response and data */
do {
alarm(rexmtval);
do {
fromlen = sizeof(from);
n = recvfrom(f, buf, sizeof (buf), 0,
(caddr_t)&from, &fromlen);
} while (n <= 0);
alarm(0);
if (n < 0) {
perror("tftp: recvfrom");
haserr++;
goto abort;
}
#ifdef DEBUG
fprintf(stderr,"recvfile: recv from %s %d, n:%d\n",
inet_ntoa(from.sin_addr), from.sin_port,n);
#endif
/* use the address returned by daemon child */
if (firsttrip) {
if (connect(f, (char *)&from, sizeof(from))<0){
perror("tftp: recvfile: connect fails");
haserr++;
goto abort;
}
to.sin_addr = from.sin_addr;
to.sin_port = from.sin_port;
firsttrip = 0;
}
if (trace)
tpacket("received", tp, n,from.sin_port);
tp->th_opcode = ntohs(tp->th_opcode);
tp->th_block = ntohs(tp->th_block);
if (tp->th_opcode == ERROR) {
fprintf(stderr,"Error code %d: %s\n",
tp->th_code, tp->th_msg);
haserr++;
goto abort;
}
} while (tp->th_opcode != DATA ||
block != tp->th_block);
/* write to local file */
xsize = n -4;
n = tftpwrite(currentfile, tp->th_data, xsize, &fsize);
if (n < 0) {
perror("tftp:recvfile():write fails");
nak(errno + 100);
break;
}
xamount += xsize;
famount += fsize;
} while (xsize == SEGSIZE);
#ifdef DEBUG
fprintf(stderr,"Transfer complete: segsize %d, compare %d\n",
xsize, xsize==SEGSIZE);
#endif
abort:
done:
tp->th_opcode = htons((u_short)ACK);
tp->th_block = htons((u_short)block);
(void) send(f, buf, 4, 0);
fclose(currentfile);
(void) close(f), f = -1;
if (haserr == 0||
xamount > 0) {
delta = time(0) - start;
printf("Received %d bytes, wrote %d bytes in %d seconds.\n",
xamount, famount, delta);
}
signal(SIGINT, oldintr);
return;
expire:
printf("Transfer timed out\n");
goto abort;
}
/*------------------------------------------------------------
makerequest
----------------------------------------------------------*/
makerequest(request, name)
int request;
char *name;
{
register struct tftphdr *tp;
int size;
register char *cp;
tp = (struct tftphdr *)buf;
tp->th_opcode = htons((u_short)request);
strcpy(tp->th_stuff, name);
size = strlen(name);
cp = tp->th_stuff + strlen(name);
*cp++ = '\0';
strcpy(cp, mode);
cp += sizeof ("netascii") - 1;
*cp++ = '\0';
return (cp - buf);
}
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" },
{ -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];
strcpy(tp->th_msg, pe->e_msg);
length = strlen(pe->e_msg) + 4;
if (trace)
tpacket("sent", tp, length);
if (send(f, buf, length, 0) != length) {
perror("tftp:nak: send fails");
}
}
/*------------------------------------------------------------
tpackets
----------------------------------------------------------*/
tpacket(s, tp, n, dport)
struct tftphdr *tp;
int n, dport;
{
static char *opcodes[] =
{ "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
register char *cp, *file;
u_short op = ntohs(tp->th_opcode);
char *index();
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 = index(cp, '\0');
printf("<file=%s, mode=%s>\n", file, cp + 1);
break;
case DATA:
printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
break;
case ACK:
printf("<block=%d, from port=%d>\n",ntohs(tp->th_block),dport);
break;
case ERROR:
printf("<code=%d, msglen=%ld, msg=%s>\n",
ntohs(tp->th_code),n, tp->th_msg);
break;
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.