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