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