File:  [Power 6/32 Unix Tahoe 4.2BSD] / cci / usr / src / ucb / tftp / tftp.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Sun Jul 28 12:29:21 2019 UTC (7 years ago) by root
Branches: bsd, MAIN
CVS tags: v121, HEAD
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;
	}
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.