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