xref: /original-bsd/usr.bin/tftp/tftp.c (revision c3e32dec)
1 /*
2  * Copyright (c) 1983, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)tftp.c	8.1 (Berkeley) 06/06/93";
10 #endif /* not lint */
11 
12 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
13 
14 /*
15  * TFTP User Program -- Protocol Machines
16  */
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <sys/time.h>
20 
21 #include <netinet/in.h>
22 
23 #include <arpa/tftp.h>
24 
25 #include <errno.h>
26 #include <setjmp.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 
31 #include "extern.h"
32 #include "tftpsubs.h"
33 
34 extern	int errno;
35 
36 extern  struct sockaddr_in peeraddr;	/* filled in by main */
37 extern  int     f;			/* the opened socket */
38 extern  int     trace;
39 extern  int     verbose;
40 extern  int     rexmtval;
41 extern  int     maxtimeout;
42 
43 #define PKTSIZE    SEGSIZE+4
44 char    ackbuf[PKTSIZE];
45 int	timeout;
46 jmp_buf	toplevel;
47 jmp_buf	timeoutbuf;
48 
49 static void nak __P((int));
50 static int makerequest __P((int, const char *, struct tftphdr *, const char *));
51 static void printstats __P((const char *, unsigned long));
52 static void startclock __P((void));
53 static void stopclock __P((void));
54 static void timer __P((int));
55 static void tpacket __P((const char *, struct tftphdr *, int));
56 
57 /*
58  * Send the requested file.
59  */
60 void
61 sendfile(fd, name, mode)
62 	int fd;
63 	char *name;
64 	char *mode;
65 {
66 	register struct tftphdr *ap;	   /* data and ack packets */
67 	struct tftphdr *r_init(), *dp;
68 	register int n;
69 	volatile int block, size, convert;
70 	volatile unsigned long amount;
71 	struct sockaddr_in from;
72 	int fromlen;
73 	FILE *file;
74 
75 	startclock();		/* start stat's clock */
76 	dp = r_init();		/* reset fillbuf/read-ahead code */
77 	ap = (struct tftphdr *)ackbuf;
78 	file = fdopen(fd, "r");
79 	convert = !strcmp(mode, "netascii");
80 	block = 0;
81 	amount = 0;
82 
83 	signal(SIGALRM, timer);
84 	do {
85 		if (block == 0)
86 			size = makerequest(WRQ, name, dp, mode) - 4;
87 		else {
88 		/*	size = read(fd, dp->th_data, SEGSIZE);	 */
89 			size = readit(file, &dp, convert);
90 			if (size < 0) {
91 				nak(errno + 100);
92 				break;
93 			}
94 			dp->th_opcode = htons((u_short)DATA);
95 			dp->th_block = htons((u_short)block);
96 		}
97 		timeout = 0;
98 		(void) setjmp(timeoutbuf);
99 send_data:
100 		if (trace)
101 			tpacket("sent", dp, size + 4);
102 		n = sendto(f, dp, size + 4, 0,
103 		    (struct sockaddr *)&peeraddr, sizeof(peeraddr));
104 		if (n != size + 4) {
105 			perror("tftp: sendto");
106 			goto abort;
107 		}
108 		read_ahead(file, convert);
109 		for ( ; ; ) {
110 			alarm(rexmtval);
111 			do {
112 				fromlen = sizeof(from);
113 				n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
114 				    (struct sockaddr *)&from, &fromlen);
115 			} while (n <= 0);
116 			alarm(0);
117 			if (n < 0) {
118 				perror("tftp: recvfrom");
119 				goto abort;
120 			}
121 			peeraddr.sin_port = from.sin_port;	/* added */
122 			if (trace)
123 				tpacket("received", ap, n);
124 			/* should verify packet came from server */
125 			ap->th_opcode = ntohs(ap->th_opcode);
126 			ap->th_block = ntohs(ap->th_block);
127 			if (ap->th_opcode == ERROR) {
128 				printf("Error code %d: %s\n", ap->th_code,
129 					ap->th_msg);
130 				goto abort;
131 			}
132 			if (ap->th_opcode == ACK) {
133 				int j;
134 
135 				if (ap->th_block == block) {
136 					break;
137 				}
138 				/* On an error, try to synchronize
139 				 * both sides.
140 				 */
141 				j = synchnet(f);
142 				if (j && trace) {
143 					printf("discarded %d packets\n",
144 							j);
145 				}
146 				if (ap->th_block == (block-1)) {
147 					goto send_data;
148 				}
149 			}
150 		}
151 		if (block > 0)
152 			amount += size;
153 		block++;
154 	} while (size == SEGSIZE || block == 1);
155 abort:
156 	fclose(file);
157 	stopclock();
158 	if (amount > 0)
159 		printstats("Sent", amount);
160 }
161 
162 /*
163  * Receive a file.
164  */
165 void
166 recvfile(fd, name, mode)
167 	int fd;
168 	char *name;
169 	char *mode;
170 {
171 	register struct tftphdr *ap;
172 	struct tftphdr *dp, *w_init();
173 	register int n;
174 	volatile int block, size, firsttrip;
175 	volatile unsigned long amount;
176 	struct sockaddr_in from;
177 	int fromlen;
178 	FILE *file;
179 	volatile int convert;		/* true if converting crlf -> lf */
180 
181 	startclock();
182 	dp = w_init();
183 	ap = (struct tftphdr *)ackbuf;
184 	file = fdopen(fd, "w");
185 	convert = !strcmp(mode, "netascii");
186 	block = 1;
187 	firsttrip = 1;
188 	amount = 0;
189 
190 	signal(SIGALRM, timer);
191 	do {
192 		if (firsttrip) {
193 			size = makerequest(RRQ, name, ap, mode);
194 			firsttrip = 0;
195 		} else {
196 			ap->th_opcode = htons((u_short)ACK);
197 			ap->th_block = htons((u_short)(block));
198 			size = 4;
199 			block++;
200 		}
201 		timeout = 0;
202 		(void) setjmp(timeoutbuf);
203 send_ack:
204 		if (trace)
205 			tpacket("sent", ap, size);
206 		if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
207 		    sizeof(peeraddr)) != size) {
208 			alarm(0);
209 			perror("tftp: sendto");
210 			goto abort;
211 		}
212 		write_behind(file, convert);
213 		for ( ; ; ) {
214 			alarm(rexmtval);
215 			do  {
216 				fromlen = sizeof(from);
217 				n = recvfrom(f, dp, PKTSIZE, 0,
218 				    (struct sockaddr *)&from, &fromlen);
219 			} while (n <= 0);
220 			alarm(0);
221 			if (n < 0) {
222 				perror("tftp: recvfrom");
223 				goto abort;
224 			}
225 			peeraddr.sin_port = from.sin_port;	/* added */
226 			if (trace)
227 				tpacket("received", dp, n);
228 			/* should verify client address */
229 			dp->th_opcode = ntohs(dp->th_opcode);
230 			dp->th_block = ntohs(dp->th_block);
231 			if (dp->th_opcode == ERROR) {
232 				printf("Error code %d: %s\n", dp->th_code,
233 					dp->th_msg);
234 				goto abort;
235 			}
236 			if (dp->th_opcode == DATA) {
237 				int j;
238 
239 				if (dp->th_block == block) {
240 					break;		/* have next packet */
241 				}
242 				/* On an error, try to synchronize
243 				 * both sides.
244 				 */
245 				j = synchnet(f);
246 				if (j && trace) {
247 					printf("discarded %d packets\n", j);
248 				}
249 				if (dp->th_block == (block-1)) {
250 					goto send_ack;	/* resend ack */
251 				}
252 			}
253 		}
254 	/*	size = write(fd, dp->th_data, n - 4); */
255 		size = writeit(file, &dp, n - 4, convert);
256 		if (size < 0) {
257 			nak(errno + 100);
258 			break;
259 		}
260 		amount += size;
261 	} while (size == SEGSIZE);
262 abort:						/* ok to ack, since user */
263 	ap->th_opcode = htons((u_short)ACK);	/* has seen err msg */
264 	ap->th_block = htons((u_short)block);
265 	(void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
266 	    sizeof(peeraddr));
267 	write_behind(file, convert);		/* flush last buffer */
268 	fclose(file);
269 	stopclock();
270 	if (amount > 0)
271 		printstats("Received", amount);
272 }
273 
274 static int
275 makerequest(request, name, tp, mode)
276 	int request;
277 	const char *name;
278 	struct tftphdr *tp;
279 	const char *mode;
280 {
281 	register char *cp;
282 
283 	tp->th_opcode = htons((u_short)request);
284 	cp = tp->th_stuff;
285 	strcpy(cp, name);
286 	cp += strlen(name);
287 	*cp++ = '\0';
288 	strcpy(cp, mode);
289 	cp += strlen(mode);
290 	*cp++ = '\0';
291 	return (cp - (char *)tp);
292 }
293 
294 struct errmsg {
295 	int	e_code;
296 	char	*e_msg;
297 } errmsgs[] = {
298 	{ EUNDEF,	"Undefined error code" },
299 	{ ENOTFOUND,	"File not found" },
300 	{ EACCESS,	"Access violation" },
301 	{ ENOSPACE,	"Disk full or allocation exceeded" },
302 	{ EBADOP,	"Illegal TFTP operation" },
303 	{ EBADID,	"Unknown transfer ID" },
304 	{ EEXISTS,	"File already exists" },
305 	{ ENOUSER,	"No such user" },
306 	{ -1,		0 }
307 };
308 
309 /*
310  * Send a nak packet (error message).
311  * Error code passed in is one of the
312  * standard TFTP codes, or a UNIX errno
313  * offset by 100.
314  */
315 static void
316 nak(error)
317 	int error;
318 {
319 	register struct errmsg *pe;
320 	register struct tftphdr *tp;
321 	int length;
322 	char *strerror();
323 
324 	tp = (struct tftphdr *)ackbuf;
325 	tp->th_opcode = htons((u_short)ERROR);
326 	tp->th_code = htons((u_short)error);
327 	for (pe = errmsgs; pe->e_code >= 0; pe++)
328 		if (pe->e_code == error)
329 			break;
330 	if (pe->e_code < 0) {
331 		pe->e_msg = strerror(error - 100);
332 		tp->th_code = EUNDEF;
333 	}
334 	strcpy(tp->th_msg, pe->e_msg);
335 	length = strlen(pe->e_msg) + 4;
336 	if (trace)
337 		tpacket("sent", tp, length);
338 	if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
339 	    sizeof(peeraddr)) != length)
340 		perror("nak");
341 }
342 
343 static void
344 tpacket(s, tp, n)
345 	const char *s;
346 	struct tftphdr *tp;
347 	int n;
348 {
349 	static char *opcodes[] =
350 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
351 	register char *cp, *file;
352 	u_short op = ntohs(tp->th_opcode);
353 	char *index();
354 
355 	if (op < RRQ || op > ERROR)
356 		printf("%s opcode=%x ", s, op);
357 	else
358 		printf("%s %s ", s, opcodes[op]);
359 	switch (op) {
360 
361 	case RRQ:
362 	case WRQ:
363 		n -= 2;
364 		file = cp = tp->th_stuff;
365 		cp = index(cp, '\0');
366 		printf("<file=%s, mode=%s>\n", file, cp + 1);
367 		break;
368 
369 	case DATA:
370 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
371 		break;
372 
373 	case ACK:
374 		printf("<block=%d>\n", ntohs(tp->th_block));
375 		break;
376 
377 	case ERROR:
378 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
379 		break;
380 	}
381 }
382 
383 struct timeval tstart;
384 struct timeval tstop;
385 
386 static void
387 startclock()
388 {
389 
390 	(void)gettimeofday(&tstart, NULL);
391 }
392 
393 static void
394 stopclock()
395 {
396 
397 	(void)gettimeofday(&tstop, NULL);
398 }
399 
400 static void
401 printstats(direction, amount)
402 	const char *direction;
403 	unsigned long amount;
404 {
405 	double delta;
406 			/* compute delta in 1/10's second units */
407 	delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
408 		((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
409 	delta = delta/10.;      /* back to seconds */
410 	printf("%s %d bytes in %.1f seconds", direction, amount, delta);
411 	if (verbose)
412 		printf(" [%.0f bits/sec]", (amount*8.)/delta);
413 	putchar('\n');
414 }
415 
416 static void
417 timer(sig)
418 	int sig;
419 {
420 
421 	timeout += rexmtval;
422 	if (timeout >= maxtimeout) {
423 		printf("Transfer timed out.\n");
424 		longjmp(toplevel, -1);
425 	}
426 	longjmp(timeoutbuf, 1);
427 }
428