xref: /original-bsd/usr.bin/tftp/tftp.c (revision 83e03edb)
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 the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)tftp.c	5.7 (Berkeley) 06/29/88";
20 #endif /* not lint */
21 
22 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
23 
24 /*
25  * TFTP User Program -- Protocol Machines
26  */
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/time.h>
30 
31 #include <netinet/in.h>
32 
33 #include <arpa/tftp.h>
34 
35 #include <signal.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <setjmp.h>
39 
40 extern	int errno;
41 
42 extern  struct sockaddr_in sin;         /* filled in by main */
43 extern  int     f;                      /* the opened socket */
44 extern  int     trace;
45 extern  int     verbose;
46 extern  int     rexmtval;
47 extern  int     maxtimeout;
48 
49 #define PKTSIZE    SEGSIZE+4
50 char    ackbuf[PKTSIZE];
51 int	timeout;
52 jmp_buf	toplevel;
53 jmp_buf	timeoutbuf;
54 
55 timer()
56 {
57 
58 	timeout += rexmtval;
59 	if (timeout >= maxtimeout) {
60 		printf("Transfer timed out.\n");
61 		longjmp(toplevel, -1);
62 	}
63 	longjmp(timeoutbuf, 1);
64 }
65 
66 /*
67  * Send the requested file.
68  */
69 sendfile(fd, name, mode)
70 	int fd;
71 	char *name;
72 	char *mode;
73 {
74 	register struct tftphdr *ap;       /* data and ack packets */
75 	struct tftphdr *r_init(), *dp;
76 	register int block = 0, size, n;
77 	register unsigned long amount = 0;
78 	struct sockaddr_in from;
79 	int fromlen;
80 	int convert;            /* true if doing nl->crlf conversion */
81 	FILE *file;
82 
83 	startclock();           /* start stat's clock */
84 	dp = r_init();          /* reset fillbuf/read-ahead code */
85 	ap = (struct tftphdr *)ackbuf;
86 	file = fdopen(fd, "r");
87 	convert = !strcmp(mode, "netascii");
88 
89 	signal(SIGALRM, timer);
90 	do {
91 		if (block == 0)
92 			size = makerequest(WRQ, name, dp, mode) - 4;
93 		else {
94 		/*      size = read(fd, dp->th_data, SEGSIZE);   */
95 			size = readit(file, &dp, convert);
96 			if (size < 0) {
97 				nak(errno + 100);
98 				break;
99 			}
100 			dp->th_opcode = htons((u_short)DATA);
101 			dp->th_block = htons((u_short)block);
102 		}
103 		timeout = 0;
104 		(void) setjmp(timeoutbuf);
105 send_data:
106 		if (trace)
107 			tpacket("sent", dp, size + 4);
108 		n = sendto(f, dp, size + 4, 0, (caddr_t)&sin, sizeof (sin));
109 		if (n != size + 4) {
110 			perror("tftp: sendto");
111 			goto abort;
112 		}
113 		read_ahead(file, convert);
114 		for ( ; ; ) {
115 			alarm(rexmtval);
116 			do {
117 				fromlen = sizeof (from);
118 				n = recvfrom(f, ackbuf, sizeof (ackbuf), 0,
119 				    (caddr_t)&from, &fromlen);
120 			} while (n <= 0);
121 			alarm(0);
122 			if (n < 0) {
123 				perror("tftp: recvfrom");
124 				goto abort;
125 			}
126 			sin.sin_port = from.sin_port;   /* added */
127 			if (trace)
128 				tpacket("received", ap, n);
129 			/* should verify packet came from server */
130 			ap->th_opcode = ntohs(ap->th_opcode);
131 			ap->th_block = ntohs(ap->th_block);
132 			if (ap->th_opcode == ERROR) {
133 				printf("Error code %d: %s\n", ap->th_code,
134 					ap->th_msg);
135 				goto abort;
136 			}
137 			if (ap->th_opcode == ACK) {
138 				int j;
139 
140 				if (ap->th_block == block) {
141 					break;
142 				}
143 				/* On an error, try to synchronize
144 				 * both sides.
145 				 */
146 				j = synchnet(f);
147 				if (j && trace) {
148 					printf("discarded %d packets\n",
149 							j);
150 				}
151 				if (ap->th_block == (block-1)) {
152 					goto send_data;
153 				}
154 			}
155 		}
156 		if (block > 0)
157 			amount += size;
158 		block++;
159 	} while (size == SEGSIZE || block == 1);
160 abort:
161 	fclose(file);
162 	stopclock();
163 	if (amount > 0)
164 		printstats("Sent", amount);
165 }
166 
167 /*
168  * Receive a file.
169  */
170 recvfile(fd, name, mode)
171 	int fd;
172 	char *name;
173 	char *mode;
174 {
175 	register struct tftphdr *ap;
176 	struct tftphdr *dp, *w_init();
177 	register int block = 1, n, size;
178 	unsigned long amount = 0;
179 	struct sockaddr_in from;
180 	int fromlen, firsttrip = 1;
181 	FILE *file;
182 	int convert;                    /* true if converting crlf -> lf */
183 
184 	startclock();
185 	dp = w_init();
186 	ap = (struct tftphdr *)ackbuf;
187 	file = fdopen(fd, "w");
188 	convert = !strcmp(mode, "netascii");
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, (caddr_t)&sin,
207 		    sizeof (sin)) != 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 				    (caddr_t)&from, &fromlen);
219 			} while (n <= 0);
220 			alarm(0);
221 			if (n < 0) {
222 				perror("tftp: recvfrom");
223 				goto abort;
224 			}
225 			sin.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, &sin, sizeof (sin));
266 	write_behind(file, convert);            /* flush last buffer */
267 	fclose(file);
268 	stopclock();
269 	if (amount > 0)
270 		printstats("Received", amount);
271 }
272 
273 makerequest(request, name, tp, mode)
274 	int request;
275 	char *name, *mode;
276 	struct tftphdr *tp;
277 {
278 	register char *cp;
279 
280 	tp->th_opcode = htons((u_short)request);
281 	cp = tp->th_stuff;
282 	strcpy(cp, name);
283 	cp += strlen(name);
284 	*cp++ = '\0';
285 	strcpy(cp, mode);
286 	cp += strlen(mode);
287 	*cp++ = '\0';
288 	return (cp - (char *)tp);
289 }
290 
291 struct errmsg {
292 	int	e_code;
293 	char	*e_msg;
294 } errmsgs[] = {
295 	{ EUNDEF,	"Undefined error code" },
296 	{ ENOTFOUND,	"File not found" },
297 	{ EACCESS,	"Access violation" },
298 	{ ENOSPACE,	"Disk full or allocation exceeded" },
299 	{ EBADOP,	"Illegal TFTP operation" },
300 	{ EBADID,	"Unknown transfer ID" },
301 	{ EEXISTS,	"File already exists" },
302 	{ ENOUSER,	"No such user" },
303 	{ -1,		0 }
304 };
305 
306 /*
307  * Send a nak packet (error message).
308  * Error code passed in is one of the
309  * standard TFTP codes, or a UNIX errno
310  * offset by 100.
311  */
312 nak(error)
313 	int error;
314 {
315 	register struct tftphdr *tp;
316 	int length;
317 	register struct errmsg *pe;
318 	extern char *sys_errlist[];
319 
320 	tp = (struct tftphdr *)ackbuf;
321 	tp->th_opcode = htons((u_short)ERROR);
322 	tp->th_code = htons((u_short)error);
323 	for (pe = errmsgs; pe->e_code >= 0; pe++)
324 		if (pe->e_code == error)
325 			break;
326 	if (pe->e_code < 0) {
327 		pe->e_msg = sys_errlist[error - 100];
328 		tp->th_code = EUNDEF;
329 	}
330 	strcpy(tp->th_msg, pe->e_msg);
331 	length = strlen(pe->e_msg) + 4;
332 	if (trace)
333 		tpacket("sent", tp, length);
334 	if (sendto(f, ackbuf, length, 0, &sin, sizeof (sin)) != length)
335 		perror("nak");
336 }
337 
338 tpacket(s, tp, n)
339 	char *s;
340 	struct tftphdr *tp;
341 	int n;
342 {
343 	static char *opcodes[] =
344 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
345 	register char *cp, *file;
346 	u_short op = ntohs(tp->th_opcode);
347 	char *index();
348 
349 	if (op < RRQ || op > ERROR)
350 		printf("%s opcode=%x ", s, op);
351 	else
352 		printf("%s %s ", s, opcodes[op]);
353 	switch (op) {
354 
355 	case RRQ:
356 	case WRQ:
357 		n -= 2;
358 		file = cp = tp->th_stuff;
359 		cp = index(cp, '\0');
360 		printf("<file=%s, mode=%s>\n", file, cp + 1);
361 		break;
362 
363 	case DATA:
364 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
365 		break;
366 
367 	case ACK:
368 		printf("<block=%d>\n", ntohs(tp->th_block));
369 		break;
370 
371 	case ERROR:
372 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
373 		break;
374 	}
375 }
376 
377 struct timeval tstart;
378 struct timeval tstop;
379 struct timezone zone;
380 
381 startclock() {
382 	gettimeofday(&tstart, &zone);
383 }
384 
385 stopclock() {
386 	gettimeofday(&tstop, &zone);
387 }
388 
389 printstats(direction, amount)
390 char *direction;
391 unsigned long amount;
392 {
393 	double delta;
394 			/* compute delta in 1/10's second units */
395 	delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
396 		((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
397 	delta = delta/10.;      /* back to seconds */
398 	printf("%s %d bytes in %.1f seconds", direction, amount, delta);
399 	if (verbose)
400 		printf(" [%.0f bits/sec]", (amount*8.)/delta);
401 	putchar('\n');
402 }
403 
404