xref: /freebsd/usr.bin/tftp/tftp.c (revision 4dac6235cf2718f6c383b6bda1dc346bb357e886)
19b50d902SRodney W. Grimes /*
29b50d902SRodney W. Grimes  * Copyright (c) 1983, 1993
39b50d902SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
49b50d902SRodney W. Grimes  *
59b50d902SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
69b50d902SRodney W. Grimes  * modification, are permitted provided that the following conditions
79b50d902SRodney W. Grimes  * are met:
89b50d902SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
99b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
109b50d902SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
119b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
129b50d902SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
139b50d902SRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
149b50d902SRodney W. Grimes  *    must display the following acknowledgement:
159b50d902SRodney W. Grimes  *	This product includes software developed by the University of
169b50d902SRodney W. Grimes  *	California, Berkeley and its contributors.
179b50d902SRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
189b50d902SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
199b50d902SRodney W. Grimes  *    without specific prior written permission.
209b50d902SRodney W. Grimes  *
219b50d902SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
229b50d902SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
239b50d902SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
249b50d902SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
259b50d902SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
269b50d902SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
279b50d902SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
289b50d902SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
299b50d902SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
309b50d902SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
319b50d902SRodney W. Grimes  * SUCH DAMAGE.
329b50d902SRodney W. Grimes  */
339b50d902SRodney W. Grimes 
346dfc2060SDavid Malone #if 0
359b50d902SRodney W. Grimes #ifndef lint
366dfc2060SDavid Malone static char sccsid[] = "@(#)tftp.c	8.1 (Berkeley) 6/6/93";
376dfc2060SDavid Malone #endif /* not lint */
38fd129a02SPhilippe Charnier #endif
399b50d902SRodney W. Grimes 
406dfc2060SDavid Malone #include <sys/cdefs.h>
416dfc2060SDavid Malone __FBSDID("$FreeBSD$");
426dfc2060SDavid Malone 
439b50d902SRodney W. Grimes /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
449b50d902SRodney W. Grimes 
459b50d902SRodney W. Grimes /*
469b50d902SRodney W. Grimes  * TFTP User Program -- Protocol Machines
479b50d902SRodney W. Grimes  */
489b50d902SRodney W. Grimes #include <sys/types.h>
499b50d902SRodney W. Grimes #include <sys/socket.h>
509b50d902SRodney W. Grimes #include <sys/time.h>
519b50d902SRodney W. Grimes 
529b50d902SRodney W. Grimes #include <netinet/in.h>
539b50d902SRodney W. Grimes 
546dfc2060SDavid Malone #include <arpa/inet.h>
559b50d902SRodney W. Grimes #include <arpa/tftp.h>
569b50d902SRodney W. Grimes 
57fd129a02SPhilippe Charnier #include <err.h>
589b50d902SRodney W. Grimes #include <errno.h>
599b50d902SRodney W. Grimes #include <setjmp.h>
609b50d902SRodney W. Grimes #include <signal.h>
619b50d902SRodney W. Grimes #include <stdio.h>
625b116430SJohn Birrell #include <string.h>
639b50d902SRodney W. Grimes #include <unistd.h>
644dac6235SHajimu UMEMOTO #include <netdb.h>
659b50d902SRodney W. Grimes 
669b50d902SRodney W. Grimes #include "extern.h"
679b50d902SRodney W. Grimes #include "tftpsubs.h"
689b50d902SRodney W. Grimes 
694dac6235SHajimu UMEMOTO extern  struct sockaddr_storage peeraddr; /* filled in by main */
709b50d902SRodney W. Grimes extern  int     f;			/* the opened socket */
719b50d902SRodney W. Grimes extern  int     trace;
729b50d902SRodney W. Grimes extern  int     verbose;
739b50d902SRodney W. Grimes extern  int     rexmtval;
749b50d902SRodney W. Grimes extern  int     maxtimeout;
759b50d902SRodney W. Grimes 
769b50d902SRodney W. Grimes #define PKTSIZE    SEGSIZE+4
779b50d902SRodney W. Grimes char    ackbuf[PKTSIZE];
789b50d902SRodney W. Grimes int	timeout;
799b50d902SRodney W. Grimes jmp_buf	toplevel;
809b50d902SRodney W. Grimes jmp_buf	timeoutbuf;
819b50d902SRodney W. Grimes 
824dac6235SHajimu UMEMOTO static void nak(int, struct sockaddr *);
833f330d7dSWarner Losh static int makerequest(int, const char *, struct tftphdr *, const char *);
843f330d7dSWarner Losh static void printstats(const char *, unsigned long);
853f330d7dSWarner Losh static void startclock(void);
863f330d7dSWarner Losh static void stopclock(void);
873f330d7dSWarner Losh static void timer(int);
883f330d7dSWarner Losh static void tpacket(const char *, struct tftphdr *, int);
894dac6235SHajimu UMEMOTO static int cmpport(struct sockaddr *, struct sockaddr *);
909b50d902SRodney W. Grimes 
919b50d902SRodney W. Grimes /*
929b50d902SRodney W. Grimes  * Send the requested file.
939b50d902SRodney W. Grimes  */
949b50d902SRodney W. Grimes void
958692ad46SDavid Greenman xmitfile(fd, name, mode)
969b50d902SRodney W. Grimes 	int fd;
979b50d902SRodney W. Grimes 	char *name;
989b50d902SRodney W. Grimes 	char *mode;
999b50d902SRodney W. Grimes {
1008049f797SMark Murray 	struct tftphdr *ap;	   /* data and ack packets */
1018049f797SMark Murray 	struct tftphdr *dp;
1028049f797SMark Murray 	int n;
10367034ac6SJeroen Ruigrok van der Werven 	volatile unsigned short block;
10467034ac6SJeroen Ruigrok van der Werven 	volatile int size, convert;
1059b50d902SRodney W. Grimes 	volatile unsigned long amount;
1064dac6235SHajimu UMEMOTO 	struct sockaddr_storage from;
1079b50d902SRodney W. Grimes 	int fromlen;
1089b50d902SRodney W. Grimes 	FILE *file;
1094dac6235SHajimu UMEMOTO 	struct sockaddr_storage peer;
1104dac6235SHajimu UMEMOTO 	struct sockaddr_storage serv;	/* valid server port number */
1119b50d902SRodney W. Grimes 
1129b50d902SRodney W. Grimes 	startclock();		/* start stat's clock */
1139b50d902SRodney W. Grimes 	dp = r_init();		/* reset fillbuf/read-ahead code */
1149b50d902SRodney W. Grimes 	ap = (struct tftphdr *)ackbuf;
1159b50d902SRodney W. Grimes 	file = fdopen(fd, "r");
1169b50d902SRodney W. Grimes 	convert = !strcmp(mode, "netascii");
1179b50d902SRodney W. Grimes 	block = 0;
1189b50d902SRodney W. Grimes 	amount = 0;
1194dac6235SHajimu UMEMOTO 	memcpy(&peer, &peeraddr, peeraddr.ss_len);
1204dac6235SHajimu UMEMOTO 	memset(&serv, 0, sizeof(serv));
1219b50d902SRodney W. Grimes 
1229b50d902SRodney W. Grimes 	signal(SIGALRM, timer);
1239b50d902SRodney W. Grimes 	do {
1249b50d902SRodney W. Grimes 		if (block == 0)
1259b50d902SRodney W. Grimes 			size = makerequest(WRQ, name, dp, mode) - 4;
1269b50d902SRodney W. Grimes 		else {
1279b50d902SRodney W. Grimes 		/*	size = read(fd, dp->th_data, SEGSIZE);	 */
1289b50d902SRodney W. Grimes 			size = readit(file, &dp, convert);
1299b50d902SRodney W. Grimes 			if (size < 0) {
1304dac6235SHajimu UMEMOTO 				nak(errno + 100, (struct sockaddr *)&peer);
1319b50d902SRodney W. Grimes 				break;
1329b50d902SRodney W. Grimes 			}
1339b50d902SRodney W. Grimes 			dp->th_opcode = htons((u_short)DATA);
1349b50d902SRodney W. Grimes 			dp->th_block = htons((u_short)block);
1359b50d902SRodney W. Grimes 		}
1369b50d902SRodney W. Grimes 		timeout = 0;
1379b50d902SRodney W. Grimes 		(void) setjmp(timeoutbuf);
1389b50d902SRodney W. Grimes send_data:
1399b50d902SRodney W. Grimes 		if (trace)
1409b50d902SRodney W. Grimes 			tpacket("sent", dp, size + 4);
1419b50d902SRodney W. Grimes 		n = sendto(f, dp, size + 4, 0,
1424dac6235SHajimu UMEMOTO 		    (struct sockaddr *)&peer, peer.ss_len);
1439b50d902SRodney W. Grimes 		if (n != size + 4) {
144fd129a02SPhilippe Charnier 			warn("sendto");
1459b50d902SRodney W. Grimes 			goto abort;
1469b50d902SRodney W. Grimes 		}
1479b50d902SRodney W. Grimes 		read_ahead(file, convert);
1489b50d902SRodney W. Grimes 		for ( ; ; ) {
1499b50d902SRodney W. Grimes 			alarm(rexmtval);
1509b50d902SRodney W. Grimes 			do {
1519b50d902SRodney W. Grimes 				fromlen = sizeof(from);
1529b50d902SRodney W. Grimes 				n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
1539b50d902SRodney W. Grimes 				    (struct sockaddr *)&from, &fromlen);
1549b50d902SRodney W. Grimes 			} while (n <= 0);
1559b50d902SRodney W. Grimes 			alarm(0);
1569b50d902SRodney W. Grimes 			if (n < 0) {
157fd129a02SPhilippe Charnier 				warn("recvfrom");
1589b50d902SRodney W. Grimes 				goto abort;
1599b50d902SRodney W. Grimes 			}
1604dac6235SHajimu UMEMOTO 			if (!serv.ss_family)
1614dac6235SHajimu UMEMOTO 				serv = from;
1624dac6235SHajimu UMEMOTO 			else if (!cmpport((struct sockaddr *)&serv,
1634dac6235SHajimu UMEMOTO 			    (struct sockaddr *)&from)) {
1644dac6235SHajimu UMEMOTO 				warn("server port mismatch");
1654dac6235SHajimu UMEMOTO 				goto abort;
1664dac6235SHajimu UMEMOTO 			}
1674dac6235SHajimu UMEMOTO 			peer = from;
1689b50d902SRodney W. Grimes 			if (trace)
1699b50d902SRodney W. Grimes 				tpacket("received", ap, n);
1709b50d902SRodney W. Grimes 			/* should verify packet came from server */
1719b50d902SRodney W. Grimes 			ap->th_opcode = ntohs(ap->th_opcode);
1729b50d902SRodney W. Grimes 			ap->th_block = ntohs(ap->th_block);
1739b50d902SRodney W. Grimes 			if (ap->th_opcode == ERROR) {
1749b50d902SRodney W. Grimes 				printf("Error code %d: %s\n", ap->th_code,
1759b50d902SRodney W. Grimes 					ap->th_msg);
1769b50d902SRodney W. Grimes 				goto abort;
1779b50d902SRodney W. Grimes 			}
1789b50d902SRodney W. Grimes 			if (ap->th_opcode == ACK) {
1799b50d902SRodney W. Grimes 				int j;
1809b50d902SRodney W. Grimes 
1819b50d902SRodney W. Grimes 				if (ap->th_block == block) {
1829b50d902SRodney W. Grimes 					break;
1839b50d902SRodney W. Grimes 				}
1849b50d902SRodney W. Grimes 				/* On an error, try to synchronize
1859b50d902SRodney W. Grimes 				 * both sides.
1869b50d902SRodney W. Grimes 				 */
1879b50d902SRodney W. Grimes 				j = synchnet(f);
1889b50d902SRodney W. Grimes 				if (j && trace) {
1899b50d902SRodney W. Grimes 					printf("discarded %d packets\n",
1909b50d902SRodney W. Grimes 							j);
1919b50d902SRodney W. Grimes 				}
1929b50d902SRodney W. Grimes 				if (ap->th_block == (block-1)) {
1939b50d902SRodney W. Grimes 					goto send_data;
1949b50d902SRodney W. Grimes 				}
1959b50d902SRodney W. Grimes 			}
1969b50d902SRodney W. Grimes 		}
1979b50d902SRodney W. Grimes 		if (block > 0)
1989b50d902SRodney W. Grimes 			amount += size;
1999b50d902SRodney W. Grimes 		block++;
2009b50d902SRodney W. Grimes 	} while (size == SEGSIZE || block == 1);
2019b50d902SRodney W. Grimes abort:
2029b50d902SRodney W. Grimes 	fclose(file);
2039b50d902SRodney W. Grimes 	stopclock();
2049b50d902SRodney W. Grimes 	if (amount > 0)
2059b50d902SRodney W. Grimes 		printstats("Sent", amount);
2069b50d902SRodney W. Grimes }
2079b50d902SRodney W. Grimes 
2089b50d902SRodney W. Grimes /*
2099b50d902SRodney W. Grimes  * Receive a file.
2109b50d902SRodney W. Grimes  */
2119b50d902SRodney W. Grimes void
2129b50d902SRodney W. Grimes recvfile(fd, name, mode)
2139b50d902SRodney W. Grimes 	int fd;
2149b50d902SRodney W. Grimes 	char *name;
2159b50d902SRodney W. Grimes 	char *mode;
2169b50d902SRodney W. Grimes {
2178049f797SMark Murray 	struct tftphdr *ap;
2188049f797SMark Murray 	struct tftphdr *dp;
2198049f797SMark Murray 	int n;
22067034ac6SJeroen Ruigrok van der Werven 	volatile unsigned short block;
22167034ac6SJeroen Ruigrok van der Werven 	volatile int size, firsttrip;
2229b50d902SRodney W. Grimes 	volatile unsigned long amount;
2234dac6235SHajimu UMEMOTO 	struct sockaddr_storage from;
2249b50d902SRodney W. Grimes 	int fromlen;
2259b50d902SRodney W. Grimes 	FILE *file;
2269b50d902SRodney W. Grimes 	volatile int convert;		/* true if converting crlf -> lf */
2274dac6235SHajimu UMEMOTO 	struct sockaddr_storage peer;
2284dac6235SHajimu UMEMOTO 	struct sockaddr_storage serv;	/* valid server port number */
2299b50d902SRodney W. Grimes 
2309b50d902SRodney W. Grimes 	startclock();
2319b50d902SRodney W. Grimes 	dp = w_init();
2329b50d902SRodney W. Grimes 	ap = (struct tftphdr *)ackbuf;
2339b50d902SRodney W. Grimes 	file = fdopen(fd, "w");
2349b50d902SRodney W. Grimes 	convert = !strcmp(mode, "netascii");
2359b50d902SRodney W. Grimes 	block = 1;
2369b50d902SRodney W. Grimes 	firsttrip = 1;
2379b50d902SRodney W. Grimes 	amount = 0;
2384dac6235SHajimu UMEMOTO 	memcpy(&peer, &peeraddr, peeraddr.ss_len);
2394dac6235SHajimu UMEMOTO 	memset(&serv, 0, sizeof(serv));
2409b50d902SRodney W. Grimes 
2419b50d902SRodney W. Grimes 	signal(SIGALRM, timer);
2429b50d902SRodney W. Grimes 	do {
2439b50d902SRodney W. Grimes 		if (firsttrip) {
2449b50d902SRodney W. Grimes 			size = makerequest(RRQ, name, ap, mode);
2459b50d902SRodney W. Grimes 			firsttrip = 0;
2469b50d902SRodney W. Grimes 		} else {
2479b50d902SRodney W. Grimes 			ap->th_opcode = htons((u_short)ACK);
2489b50d902SRodney W. Grimes 			ap->th_block = htons((u_short)(block));
2499b50d902SRodney W. Grimes 			size = 4;
2509b50d902SRodney W. Grimes 			block++;
2519b50d902SRodney W. Grimes 		}
2529b50d902SRodney W. Grimes 		timeout = 0;
2539b50d902SRodney W. Grimes 		(void) setjmp(timeoutbuf);
2549b50d902SRodney W. Grimes send_ack:
2559b50d902SRodney W. Grimes 		if (trace)
2569b50d902SRodney W. Grimes 			tpacket("sent", ap, size);
2574dac6235SHajimu UMEMOTO 		if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peer,
2584dac6235SHajimu UMEMOTO 		    peer.ss_len) != size) {
2599b50d902SRodney W. Grimes 			alarm(0);
260fd129a02SPhilippe Charnier 			warn("sendto");
2619b50d902SRodney W. Grimes 			goto abort;
2629b50d902SRodney W. Grimes 		}
2639b50d902SRodney W. Grimes 		write_behind(file, convert);
2649b50d902SRodney W. Grimes 		for ( ; ; ) {
2659b50d902SRodney W. Grimes 			alarm(rexmtval);
2669b50d902SRodney W. Grimes 			do  {
2679b50d902SRodney W. Grimes 				fromlen = sizeof(from);
2689b50d902SRodney W. Grimes 				n = recvfrom(f, dp, PKTSIZE, 0,
2699b50d902SRodney W. Grimes 				    (struct sockaddr *)&from, &fromlen);
2709b50d902SRodney W. Grimes 			} while (n <= 0);
2719b50d902SRodney W. Grimes 			alarm(0);
2729b50d902SRodney W. Grimes 			if (n < 0) {
273fd129a02SPhilippe Charnier 				warn("recvfrom");
2749b50d902SRodney W. Grimes 				goto abort;
2759b50d902SRodney W. Grimes 			}
2764dac6235SHajimu UMEMOTO 			if (!serv.ss_family)
2774dac6235SHajimu UMEMOTO 				serv = from;
2784dac6235SHajimu UMEMOTO 			else if (!cmpport((struct sockaddr *)&serv,
2794dac6235SHajimu UMEMOTO 			    (struct sockaddr *)&from)) {
2804dac6235SHajimu UMEMOTO 				warn("server port mismatch");
2814dac6235SHajimu UMEMOTO 				goto abort;
2824dac6235SHajimu UMEMOTO 			}
2834dac6235SHajimu UMEMOTO 			peer = from;
2849b50d902SRodney W. Grimes 			if (trace)
2859b50d902SRodney W. Grimes 				tpacket("received", dp, n);
2869b50d902SRodney W. Grimes 			/* should verify client address */
2879b50d902SRodney W. Grimes 			dp->th_opcode = ntohs(dp->th_opcode);
2889b50d902SRodney W. Grimes 			dp->th_block = ntohs(dp->th_block);
2899b50d902SRodney W. Grimes 			if (dp->th_opcode == ERROR) {
2909b50d902SRodney W. Grimes 				printf("Error code %d: %s\n", dp->th_code,
2919b50d902SRodney W. Grimes 					dp->th_msg);
2929b50d902SRodney W. Grimes 				goto abort;
2939b50d902SRodney W. Grimes 			}
2949b50d902SRodney W. Grimes 			if (dp->th_opcode == DATA) {
2959b50d902SRodney W. Grimes 				int j;
2969b50d902SRodney W. Grimes 
2979b50d902SRodney W. Grimes 				if (dp->th_block == block) {
2989b50d902SRodney W. Grimes 					break;		/* have next packet */
2999b50d902SRodney W. Grimes 				}
3009b50d902SRodney W. Grimes 				/* On an error, try to synchronize
3019b50d902SRodney W. Grimes 				 * both sides.
3029b50d902SRodney W. Grimes 				 */
3039b50d902SRodney W. Grimes 				j = synchnet(f);
3049b50d902SRodney W. Grimes 				if (j && trace) {
3059b50d902SRodney W. Grimes 					printf("discarded %d packets\n", j);
3069b50d902SRodney W. Grimes 				}
3079b50d902SRodney W. Grimes 				if (dp->th_block == (block-1)) {
3089b50d902SRodney W. Grimes 					goto send_ack;	/* resend ack */
3099b50d902SRodney W. Grimes 				}
3109b50d902SRodney W. Grimes 			}
3119b50d902SRodney W. Grimes 		}
3129b50d902SRodney W. Grimes 	/*	size = write(fd, dp->th_data, n - 4); */
3139b50d902SRodney W. Grimes 		size = writeit(file, &dp, n - 4, convert);
3149b50d902SRodney W. Grimes 		if (size < 0) {
3154dac6235SHajimu UMEMOTO 			nak(errno + 100, (struct sockaddr *)&peer);
3169b50d902SRodney W. Grimes 			break;
3179b50d902SRodney W. Grimes 		}
3189b50d902SRodney W. Grimes 		amount += size;
3199b50d902SRodney W. Grimes 	} while (size == SEGSIZE);
3209b50d902SRodney W. Grimes abort:						/* ok to ack, since user */
3219b50d902SRodney W. Grimes 	ap->th_opcode = htons((u_short)ACK);	/* has seen err msg */
3229b50d902SRodney W. Grimes 	ap->th_block = htons((u_short)block);
3234dac6235SHajimu UMEMOTO 	(void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peer,
3244dac6235SHajimu UMEMOTO 	    peer.ss_len);
3259b50d902SRodney W. Grimes 	write_behind(file, convert);		/* flush last buffer */
3269b50d902SRodney W. Grimes 	fclose(file);
3279b50d902SRodney W. Grimes 	stopclock();
3289b50d902SRodney W. Grimes 	if (amount > 0)
3299b50d902SRodney W. Grimes 		printstats("Received", amount);
3309b50d902SRodney W. Grimes }
3319b50d902SRodney W. Grimes 
3329b50d902SRodney W. Grimes static int
3339b50d902SRodney W. Grimes makerequest(request, name, tp, mode)
3349b50d902SRodney W. Grimes 	int request;
3359b50d902SRodney W. Grimes 	const char *name;
3369b50d902SRodney W. Grimes 	struct tftphdr *tp;
3379b50d902SRodney W. Grimes 	const char *mode;
3389b50d902SRodney W. Grimes {
3398049f797SMark Murray 	char *cp;
3409b50d902SRodney W. Grimes 
3419b50d902SRodney W. Grimes 	tp->th_opcode = htons((u_short)request);
3429b50d902SRodney W. Grimes 	cp = tp->th_stuff;
3439b50d902SRodney W. Grimes 	strcpy(cp, name);
3449b50d902SRodney W. Grimes 	cp += strlen(name);
3459b50d902SRodney W. Grimes 	*cp++ = '\0';
3469b50d902SRodney W. Grimes 	strcpy(cp, mode);
3479b50d902SRodney W. Grimes 	cp += strlen(mode);
3489b50d902SRodney W. Grimes 	*cp++ = '\0';
3499b50d902SRodney W. Grimes 	return (cp - (char *)tp);
3509b50d902SRodney W. Grimes }
3519b50d902SRodney W. Grimes 
3529b50d902SRodney W. Grimes struct errmsg {
3539b50d902SRodney W. Grimes 	int	e_code;
3548049f797SMark Murray 	const char	*e_msg;
3559b50d902SRodney W. Grimes } errmsgs[] = {
3569b50d902SRodney W. Grimes 	{ EUNDEF,	"Undefined error code" },
3579b50d902SRodney W. Grimes 	{ ENOTFOUND,	"File not found" },
3589b50d902SRodney W. Grimes 	{ EACCESS,	"Access violation" },
3599b50d902SRodney W. Grimes 	{ ENOSPACE,	"Disk full or allocation exceeded" },
3609b50d902SRodney W. Grimes 	{ EBADOP,	"Illegal TFTP operation" },
3619b50d902SRodney W. Grimes 	{ EBADID,	"Unknown transfer ID" },
3629b50d902SRodney W. Grimes 	{ EEXISTS,	"File already exists" },
3639b50d902SRodney W. Grimes 	{ ENOUSER,	"No such user" },
3649b50d902SRodney W. Grimes 	{ -1,		0 }
3659b50d902SRodney W. Grimes };
3669b50d902SRodney W. Grimes 
3679b50d902SRodney W. Grimes /*
3689b50d902SRodney W. Grimes  * Send a nak packet (error message).
3699b50d902SRodney W. Grimes  * Error code passed in is one of the
3709b50d902SRodney W. Grimes  * standard TFTP codes, or a UNIX errno
3719b50d902SRodney W. Grimes  * offset by 100.
3729b50d902SRodney W. Grimes  */
3739b50d902SRodney W. Grimes static void
3744dac6235SHajimu UMEMOTO nak(error, peer)
3759b50d902SRodney W. Grimes 	int error;
3764dac6235SHajimu UMEMOTO 	struct sockaddr *peer;
3779b50d902SRodney W. Grimes {
3788049f797SMark Murray 	struct errmsg *pe;
3798049f797SMark Murray 	struct tftphdr *tp;
3809b50d902SRodney W. Grimes 	int length;
3819b50d902SRodney W. Grimes 
3829b50d902SRodney W. Grimes 	tp = (struct tftphdr *)ackbuf;
3839b50d902SRodney W. Grimes 	tp->th_opcode = htons((u_short)ERROR);
3849b50d902SRodney W. Grimes 	tp->th_code = htons((u_short)error);
3859b50d902SRodney W. Grimes 	for (pe = errmsgs; pe->e_code >= 0; pe++)
3869b50d902SRodney W. Grimes 		if (pe->e_code == error)
3879b50d902SRodney W. Grimes 			break;
3889b50d902SRodney W. Grimes 	if (pe->e_code < 0) {
3899b50d902SRodney W. Grimes 		pe->e_msg = strerror(error - 100);
3909b50d902SRodney W. Grimes 		tp->th_code = EUNDEF;
3919b50d902SRodney W. Grimes 	}
3929b50d902SRodney W. Grimes 	strcpy(tp->th_msg, pe->e_msg);
3939b50d902SRodney W. Grimes 	length = strlen(pe->e_msg) + 4;
3949b50d902SRodney W. Grimes 	if (trace)
3959b50d902SRodney W. Grimes 		tpacket("sent", tp, length);
3964dac6235SHajimu UMEMOTO 	if (sendto(f, ackbuf, length, 0, peer, peer->sa_len) != length)
397fd129a02SPhilippe Charnier 		warn("nak");
3989b50d902SRodney W. Grimes }
3999b50d902SRodney W. Grimes 
4009b50d902SRodney W. Grimes static void
4019b50d902SRodney W. Grimes tpacket(s, tp, n)
4029b50d902SRodney W. Grimes 	const char *s;
4039b50d902SRodney W. Grimes 	struct tftphdr *tp;
4049b50d902SRodney W. Grimes 	int n;
4059b50d902SRodney W. Grimes {
4068049f797SMark Murray 	static const char *opcodes[] =
4079b50d902SRodney W. Grimes 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
4088049f797SMark Murray 	char *cp, *file;
4099b50d902SRodney W. Grimes 	u_short op = ntohs(tp->th_opcode);
4109b50d902SRodney W. Grimes 
4119b50d902SRodney W. Grimes 	if (op < RRQ || op > ERROR)
4129b50d902SRodney W. Grimes 		printf("%s opcode=%x ", s, op);
4139b50d902SRodney W. Grimes 	else
4149b50d902SRodney W. Grimes 		printf("%s %s ", s, opcodes[op]);
4159b50d902SRodney W. Grimes 	switch (op) {
4169b50d902SRodney W. Grimes 
4179b50d902SRodney W. Grimes 	case RRQ:
4189b50d902SRodney W. Grimes 	case WRQ:
4199b50d902SRodney W. Grimes 		n -= 2;
4209b50d902SRodney W. Grimes 		file = cp = tp->th_stuff;
4219b50d902SRodney W. Grimes 		cp = index(cp, '\0');
4229b50d902SRodney W. Grimes 		printf("<file=%s, mode=%s>\n", file, cp + 1);
4239b50d902SRodney W. Grimes 		break;
4249b50d902SRodney W. Grimes 
4259b50d902SRodney W. Grimes 	case DATA:
4269b50d902SRodney W. Grimes 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
4279b50d902SRodney W. Grimes 		break;
4289b50d902SRodney W. Grimes 
4299b50d902SRodney W. Grimes 	case ACK:
4309b50d902SRodney W. Grimes 		printf("<block=%d>\n", ntohs(tp->th_block));
4319b50d902SRodney W. Grimes 		break;
4329b50d902SRodney W. Grimes 
4339b50d902SRodney W. Grimes 	case ERROR:
4349b50d902SRodney W. Grimes 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
4359b50d902SRodney W. Grimes 		break;
4369b50d902SRodney W. Grimes 	}
4379b50d902SRodney W. Grimes }
4389b50d902SRodney W. Grimes 
4399b50d902SRodney W. Grimes struct timeval tstart;
4409b50d902SRodney W. Grimes struct timeval tstop;
4419b50d902SRodney W. Grimes 
4429b50d902SRodney W. Grimes static void
4439b50d902SRodney W. Grimes startclock()
4449b50d902SRodney W. Grimes {
4459b50d902SRodney W. Grimes 
4469b50d902SRodney W. Grimes 	(void)gettimeofday(&tstart, NULL);
4479b50d902SRodney W. Grimes }
4489b50d902SRodney W. Grimes 
4499b50d902SRodney W. Grimes static void
4509b50d902SRodney W. Grimes stopclock()
4519b50d902SRodney W. Grimes {
4529b50d902SRodney W. Grimes 
4539b50d902SRodney W. Grimes 	(void)gettimeofday(&tstop, NULL);
4549b50d902SRodney W. Grimes }
4559b50d902SRodney W. Grimes 
4569b50d902SRodney W. Grimes static void
4579b50d902SRodney W. Grimes printstats(direction, amount)
4589b50d902SRodney W. Grimes 	const char *direction;
4599b50d902SRodney W. Grimes 	unsigned long amount;
4609b50d902SRodney W. Grimes {
4619b50d902SRodney W. Grimes 	double delta;
4629b50d902SRodney W. Grimes 			/* compute delta in 1/10's second units */
4639b50d902SRodney W. Grimes 	delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
4649b50d902SRodney W. Grimes 		((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
4659b50d902SRodney W. Grimes 	delta = delta/10.;      /* back to seconds */
4668049f797SMark Murray 	printf("%s %ld bytes in %.1f seconds", direction, amount, delta);
4679b50d902SRodney W. Grimes 	if (verbose)
4689b50d902SRodney W. Grimes 		printf(" [%.0f bits/sec]", (amount*8.)/delta);
4699b50d902SRodney W. Grimes 	putchar('\n');
4709b50d902SRodney W. Grimes }
4719b50d902SRodney W. Grimes 
4729b50d902SRodney W. Grimes static void
4739b50d902SRodney W. Grimes timer(sig)
4748049f797SMark Murray 	int sig __unused;
4759b50d902SRodney W. Grimes {
4769b50d902SRodney W. Grimes 
4779b50d902SRodney W. Grimes 	timeout += rexmtval;
4789b50d902SRodney W. Grimes 	if (timeout >= maxtimeout) {
4799b50d902SRodney W. Grimes 		printf("Transfer timed out.\n");
4809b50d902SRodney W. Grimes 		longjmp(toplevel, -1);
4819b50d902SRodney W. Grimes 	}
4829b50d902SRodney W. Grimes 	longjmp(timeoutbuf, 1);
4839b50d902SRodney W. Grimes }
4844dac6235SHajimu UMEMOTO 
4854dac6235SHajimu UMEMOTO static int
4864dac6235SHajimu UMEMOTO cmpport(sa, sb)
4874dac6235SHajimu UMEMOTO 	struct sockaddr *sa;
4884dac6235SHajimu UMEMOTO 	struct sockaddr *sb;
4894dac6235SHajimu UMEMOTO {
4904dac6235SHajimu UMEMOTO 	char a[NI_MAXSERV], b[NI_MAXSERV];
4914dac6235SHajimu UMEMOTO 
4924dac6235SHajimu UMEMOTO 	if (getnameinfo(sa, sa->sa_len, NULL, 0, a, sizeof(a), NI_NUMERICSERV))
4934dac6235SHajimu UMEMOTO 		return 0;
4944dac6235SHajimu UMEMOTO 	if (getnameinfo(sb, sb->sa_len, NULL, 0, b, sizeof(b), NI_NUMERICSERV))
4954dac6235SHajimu UMEMOTO 		return 0;
4964dac6235SHajimu UMEMOTO 	if (strcmp(a, b) != 0)
4974dac6235SHajimu UMEMOTO 		return 0;
4984dac6235SHajimu UMEMOTO 
4994dac6235SHajimu UMEMOTO 	return 1;
5004dac6235SHajimu UMEMOTO }
501