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