xref: /csrg-svn/usr.bin/tftp/tftp.c (revision 22404)
1 /*
2  * Copyright (c) 1983 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.1 (Berkeley) 06/06/85";
9 #endif not lint
10 
11 /*
12  * TFTP User Program -- Protocol Machines
13  */
14 #include <sys/types.h>
15 #include <sys/socket.h>
16 
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19 #include <arpa/tftp.h>
20 
21 #include <signal.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <setjmp.h>
25 #include <netdb.h>
26 
27 extern	int errno;
28 extern	struct sockaddr_in sin;
29 extern	char mode[];
30 int	f;
31 int	trace;
32 int	connected;
33 char	sbuf[BUFSIZ];			/* send buffer */
34 char	rbuf[BUFSIZ];			/* receive buffer */
35 int	rexmtval;
36 int	maxtimeout;
37 int	timeout;
38 jmp_buf	toplevel;
39 jmp_buf	timeoutbuf;
40 
41 timer()
42 {
43 
44 	timeout += rexmtval;
45 	if (timeout >= maxtimeout) {
46 		printf("Transfer timed out.\n");
47 		longjmp(toplevel, -1);
48 	}
49 	longjmp(timeoutbuf, 1);
50 }
51 
52 /*
53  * Send the requested file.
54  */
55 sendfile(fd, name)
56 	int fd;
57 	char *name;
58 {
59 	register struct tftphdr *stp = (struct tftphdr *)sbuf;
60 	register struct tftphdr *rtp = (struct tftphdr *)rbuf;
61 	register int block = 0, size, n, amount = 0;
62 	struct sockaddr_in from, to;
63 	time_t start = time(0), delta;
64 	int fromlen, aborted = 0;
65 
66 	to = sin;
67 	signal(SIGALRM, timer);
68 	do {
69 		if (block == 0)
70 			size = makerequest(WRQ, name) - 4;
71 		else {
72 			size = read(fd, stp->th_data, SEGSIZE);
73 			if (size < 0) {
74 				nak(&to, errno + 100);
75 				break;
76 			}
77 			stp->th_opcode = htons((u_short)DATA);
78 			stp->th_block = htons((u_short)block);
79 		}
80 		timeout = 0;
81 		(void) setjmp(timeoutbuf);
82 		if (trace)
83 			tpacket("sent", &to, stp, size + 4);
84 		n = sendto(f, sbuf, size + 4, 0, (caddr_t)&to, sizeof (to));
85 		if (n != size + 4) {
86 			perror("tftp: sendto");
87 			aborted = 1;
88 			goto done;
89 		}
90 		do {
91 again:
92 			alarm(rexmtval);
93 			do {
94 				fromlen = sizeof (from);
95 				n = recvfrom(f, rbuf, sizeof (rbuf), 0,
96 				    (caddr_t)&from, &fromlen);
97 			} while (n <= 0);
98 			alarm(0);
99 			if (n < 0) {
100 				perror("tftp: recvfrom");
101 				aborted = 1;
102 				goto done;
103 			}
104 			if (to.sin_addr.s_addr != from.sin_addr.s_addr) {
105 				tpacket("discarded (wrong host)",
106 				    &from, rtp, n);
107 				goto again;
108 			}
109 			if (to.sin_port = sin.sin_port)
110 				to.sin_port = from.sin_port;
111 			if (to.sin_port != from.sin_port) {
112 				tpacket("discarded (wrong port)",
113 				    &from, rtp, n);
114 				goto again;
115 			}
116 			if (trace)
117 				tpacket("received", &from, rtp, n);
118 			/* should verify packet came from server */
119 			rtp->th_opcode = ntohs(rtp->th_opcode);
120 			rtp->th_block = ntohs(rtp->th_block);
121 			if (rtp->th_opcode == ERROR) {
122 				printf("Error code %d: %s\n", rtp->th_code,
123 					rtp->th_msg);
124 				aborted = 1;
125 				goto done;
126 			}
127 		} while (rtp->th_opcode != ACK && block != rtp->th_block);
128 		if (block > 0)
129 			amount += size;
130 		block++;
131 	} while (size == SEGSIZE || block == 1);
132 	if (!aborted && amount > 0) {
133 		delta = time(0) - start;
134 		printf("Sent %d bytes in %d seconds.\n", amount, delta);
135 	}
136 done:
137 	(void) close(fd);
138 	return (aborted);
139 }
140 
141 /*
142  * Receive a file.
143  */
144 recvfile(fd, name)
145 	int fd;
146 	char *name;
147 {
148 	register struct tftphdr *stp = (struct tftphdr *)sbuf;
149 	register struct tftphdr *rtp = (struct tftphdr *)rbuf;
150 	register int block = 1, n, size, amount = 0;
151 	struct sockaddr_in from, to;
152 	time_t start = time(0), delta;
153 	int fromlen, firsttrip = 1, aborted = 0;
154 
155 	to = sin;
156 	signal(SIGALRM, timer);
157 	do {
158 		if (firsttrip) {
159 			size = makerequest(RRQ, name);
160 			firsttrip = 0;
161 		} else {
162 			stp->th_opcode = htons((u_short)ACK);
163 			stp->th_block = htons((u_short)(block));
164 			size = 4;
165 			block++;
166 		}
167 		timeout = 0;
168 		(void) setjmp(timeoutbuf);
169 		if (trace)
170 			tpacket("sent", &to, stp, size);
171 		if (sendto(f, sbuf, size, 0, (caddr_t)&to,
172 		    sizeof (to)) != size) {
173 			alarm(0);
174 			perror("tftp: sendto");
175 			aborted = 1;
176 			goto done;
177 		}
178 		do {
179 again:
180 			alarm(rexmtval);
181 			do {
182 				fromlen = sizeof (from);
183 				n = recvfrom(f, rbuf, sizeof (rbuf), 0,
184 				    (caddr_t)&from, &fromlen);
185 			} while (n <= 0);
186 			alarm(0);
187 			if (n < 0) {
188 				perror("tftp: recvfrom");
189 				aborted = 1;
190 				goto done;
191 			}
192 			if (to.sin_addr.s_addr != from.sin_addr.s_addr) {
193 				tpacket("discarded (wrong host)",
194 				    &from, rtp, n);
195 				goto again;
196 			}
197 			if (to.sin_port = sin.sin_port)
198 				to.sin_port = from.sin_port;
199 			if (to.sin_port != from.sin_port) {
200 				tpacket("discarded (wrong port)",
201 				    &from, rtp, n);
202 				goto again;
203 			}
204 			if (trace)
205 				tpacket("received", &from, rtp, n);
206 			rtp->th_opcode = ntohs(rtp->th_opcode);
207 			rtp->th_block = ntohs(rtp->th_block);
208 			if (rtp->th_opcode == ERROR) {
209 				printf("Error code %d: %s\n", rtp->th_code,
210 					rtp->th_msg);
211 				aborted = 1;
212 				goto done;
213 			}
214 		} while (rtp->th_opcode != DATA && rtp->th_block != block);
215 		size = write(fd, rtp->th_data, n - 4);
216 		if (size < 0) {
217 			perror("tftp: write");
218 			nak(&to, errno + 100);
219 			aborted = 1;
220 			goto done;
221 		}
222 		amount += size;
223 	} while (size == SEGSIZE);
224 done:
225 	stp->th_opcode = htons((u_short)ACK);
226 	stp->th_block = htons((u_short)block);
227 	(void) sendto(f, sbuf, 4, 0, &to, sizeof (to));
228 	(void) close(fd);
229 	if (!aborted && amount > 0) {
230 		delta = time(0) - start;
231 		printf("Received %d bytes in %d seconds.\n", amount, delta);
232 	}
233 	return (aborted);
234 }
235 
236 makerequest(request, name)
237 	int request;
238 	char *name;
239 {
240 	register struct tftphdr *stp;
241 	int size;
242 	register char *cp;
243 
244 	stp = (struct tftphdr *)sbuf;
245 	stp->th_opcode = htons((u_short)request);
246 	strcpy(stp->th_stuff, name);
247 	size = strlen(name);
248 	cp = stp->th_stuff + strlen(name);
249 	*cp++ = '\0';
250 	strcpy(cp, mode);
251 	cp += sizeof ("netascii") - 1;
252 	*cp++ = '\0';
253 	return (cp - sbuf);
254 }
255 
256 struct errmsg {
257 	int	e_code;
258 	char	*e_msg;
259 } errmsgs[] = {
260 	{ EUNDEF,	"Undefined error code" },
261 	{ ENOTFOUND,	"File not found" },
262 	{ EACCESS,	"Access violation" },
263 	{ ENOSPACE,	"Disk full or allocation exceeded" },
264 	{ EBADOP,	"Illegal TFTP operation" },
265 	{ EBADID,	"Unknown transfer ID" },
266 	{ EEXISTS,	"File already exists" },
267 	{ ENOUSER,	"No such user" },
268 	{ -1,		0 }
269 };
270 
271 /*
272  * Send a nak packet (error message).
273  * Error code passed in is one of the
274  * standard TFTP codes, or a UNIX errno
275  * offset by 100.
276  */
277 nak(to, error)
278 	struct sockaddr_in *to;
279 	int error;
280 {
281 	register struct tftphdr *stp;
282 	int length;
283 	register struct errmsg *pe;
284 	extern char *sys_errlist[];
285 
286 	stp = (struct tftphdr *)sbuf;
287 	stp->th_opcode = htons((u_short)ERROR);
288 	stp->th_code = htons((u_short)error);
289 	for (pe = errmsgs; pe->e_code >= 0; pe++)
290 		if (pe->e_code == error)
291 			break;
292 	if (pe->e_code < 0)
293 		pe->e_msg = sys_errlist[error - 100];
294 	strcpy(stp->th_msg, pe->e_msg);
295 	length = strlen(pe->e_msg) + 4;
296 	if (trace)
297 		tpacket("sent", to, stp, length);
298 	if (sendto(f, sbuf, length, 0, to, sizeof (*to)) != length)
299 		perror("tftp: nak");
300 }
301 
302 tpacket(s, sin, tp, n)
303 	struct sockaddr_in *sin;
304 	struct tftphdr *tp;
305 	int n;
306 {
307 	static char *opcodes[] =
308 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
309 	register char *cp, *file;
310 	u_short op = ntohs(tp->th_opcode);
311 	char *index();
312 
313 	printf("%s ", s);
314 	if (sin) {
315 		struct hostent *hp = gethostbyaddr(&sin->sin_addr,
316 		     sizeof (sin->sin_addr), AF_INET);
317 
318 		printf("%s.%d ",
319 		    hp == 0 ? inet_ntoa(sin->sin_addr) : hp->h_name,
320 		    ntohs(sin->sin_port));
321 	}
322 	if (op < RRQ || op > ERROR)
323 		printf("opcode=%x ", op);
324 	else
325 		printf("%s ", opcodes[op]);
326 	switch (op) {
327 
328 	case RRQ:
329 	case WRQ:
330 		n -= 2;
331 		file = cp = tp->th_stuff;
332 		cp = index(cp, '\0');
333 		printf("<file=%s, mode=%s>\n", file, cp + 1);
334 		break;
335 
336 	case DATA:
337 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
338 		break;
339 
340 	case ACK:
341 		printf("<block=%d>\n", ntohs(tp->th_block));
342 		break;
343 
344 	case ERROR:
345 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
346 		break;
347 
348 	default:
349 		putchar('\n');
350 		break;
351 	}
352 }
353