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