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