xref: /csrg-svn/usr.bin/tftp/tftp.c (revision 14554)
1 #ifndef lint
2 static char sccsid[] = "@(#)tftp.c	4.7 (Berkeley) 08/11/83";
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 
13 #include <arpa/tftp.h>
14 
15 #include <signal.h>
16 #include <stdio.h>
17 #include <errno.h>
18 #include <setjmp.h>
19 
20 extern	int errno;
21 extern	struct sockaddr_in sin;
22 extern	char mode[];
23 int	f;
24 int	trace;
25 int	verbose;
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;
55 	time_t start = time(0), delta;
56 	int fromlen;
57 
58 	signal(SIGALRM, timer);
59 	do {
60 		if (block == 0)
61 			size = makerequest(WRQ, name) - 4;
62 		else {
63 			size = read(fd, tp->th_data, SEGSIZE);
64 			if (size < 0) {
65 				nak(errno + 100);
66 				break;
67 			}
68 			tp->th_opcode = htons((u_short)DATA);
69 			tp->th_block = htons((u_short)block);
70 		}
71 		timeout = 0;
72 		(void) setjmp(timeoutbuf);
73 		if (trace)
74 			tpacket("sent", tp, size + 4);
75 		n = sendto(f, buf, size + 4, 0, (caddr_t)&sin, sizeof (sin));
76 		if (n != size + 4) {
77 			perror("tftp: sendto");
78 			goto abort;
79 		}
80 		do {
81 			alarm(rexmtval);
82 			do {
83 				fromlen = sizeof (from);
84 				n = recvfrom(f, buf, sizeof (buf), 0,
85 				    (caddr_t)&from, &fromlen);
86 			} while (n <= 0);
87 			alarm(0);
88 			if (n < 0) {
89 				perror("tftp: recvfrom");
90 				goto abort;
91 			}
92 			if (trace)
93 				tpacket("received", tp, n);
94 			/* should verify packet came from server */
95 			tp->th_opcode = ntohs(tp->th_opcode);
96 			tp->th_block = ntohs(tp->th_block);
97 			if (tp->th_opcode == ERROR) {
98 				printf("Error code %d: %s\n", tp->th_code,
99 					tp->th_msg);
100 				goto abort;
101 			}
102 		} while (tp->th_opcode != ACK && block != tp->th_block);
103 		if (block > 0)
104 			amount += size;
105 		block++;
106 	} while (size == SEGSIZE || block == 1);
107 abort:
108 	(void) close(fd);
109 	if (amount > 0) {
110 		delta = time(0) - start;
111 		printf("Sent %d bytes in %d seconds.\n", amount, delta);
112 	}
113 }
114 
115 /*
116  * Receive a file.
117  */
118 recvfile(fd, name)
119 	int fd;
120 	char *name;
121 {
122 	register struct tftphdr *tp = (struct tftphdr *)buf;
123 	register int block = 1, n, size, amount = 0;
124 	struct sockaddr_in from;
125 	time_t start = time(0), delta;
126 	int fromlen, firsttrip = 1;
127 
128 	signal(SIGALRM, timer);
129 	do {
130 		if (firsttrip) {
131 			size = makerequest(RRQ, name);
132 			firsttrip = 0;
133 		} else {
134 			tp->th_opcode = htons((u_short)ACK);
135 			tp->th_block = htons((u_short)(block));
136 			size = 4;
137 			block++;
138 		}
139 		timeout = 0;
140 		(void) setjmp(timeoutbuf);
141 		if (trace)
142 			tpacket("sent", tp, size);
143 		if (sendto(f, buf, size, 0, (caddr_t)&sin,
144 		    sizeof (sin)) != size) {
145 			alarm(0);
146 			perror("tftp: sendto");
147 			goto abort;
148 		}
149 		do {
150 			alarm(rexmtval);
151 			do
152 				n = recvfrom(f, buf, sizeof (buf), 0,
153 				    (caddr_t)&from, &fromlen);
154 			while (n <= 0);
155 			alarm(0);
156 			if (n < 0) {
157 				perror("tftp: recvfrom");
158 				goto abort;
159 			}
160 			if (trace)
161 				tpacket("received", tp, n);
162 			/* should verify client address */
163 			tp->th_opcode = ntohs(tp->th_opcode);
164 			tp->th_block = ntohs(tp->th_block);
165 			if (tp->th_opcode == ERROR) {
166 				printf("Error code %d: %s\n", tp->th_code,
167 					tp->th_msg);
168 				goto abort;
169 			}
170 		} while (tp->th_opcode != DATA && block != tp->th_block);
171 		size = write(fd, tp->th_data, n - 4);
172 		if (size < 0) {
173 			nak(errno + 100);
174 			break;
175 		}
176 		amount += size;
177 	} while (size == SEGSIZE);
178 abort:
179 	tp->th_opcode = htons((u_short)ACK);
180 	tp->th_block = htons((u_short)block);
181 	(void) sendto(f, buf, 4, 0, &sin, sizeof (sin));
182 	(void) close(fd);
183 	if (amount > 0) {
184 		delta = time(0) - start;
185 		printf("Received %d bytes in %d seconds.\n", amount, delta);
186 	}
187 }
188 
189 makerequest(request, name)
190 	int request;
191 	char *name;
192 {
193 	register struct tftphdr *tp;
194 	int size;
195 	register char *cp;
196 
197 	tp = (struct tftphdr *)buf;
198 	tp->th_opcode = htons((u_short)request);
199 	strcpy(tp->th_stuff, name);
200 	size = strlen(name);
201 	cp = tp->th_stuff + strlen(name);
202 	*cp++ = '\0';
203 	strcpy(cp, mode);
204 	cp += sizeof ("netascii") - 1;
205 	*cp++ = '\0';
206 	return (cp - buf);
207 }
208 
209 struct errmsg {
210 	int	e_code;
211 	char	*e_msg;
212 } errmsgs[] = {
213 	{ EUNDEF,	"Undefined error code" },
214 	{ ENOTFOUND,	"File not found" },
215 	{ EACCESS,	"Access violation" },
216 	{ ENOSPACE,	"Disk full or allocation exceeded" },
217 	{ EBADOP,	"Illegal TFTP operation" },
218 	{ EBADID,	"Unknown transfer ID" },
219 	{ EEXISTS,	"File already exists" },
220 	{ ENOUSER,	"No such user" },
221 	{ -1,		0 }
222 };
223 
224 /*
225  * Send a nak packet (error message).
226  * Error code passed in is one of the
227  * standard TFTP codes, or a UNIX errno
228  * offset by 100.
229  */
230 nak(error)
231 	int error;
232 {
233 	register struct tftphdr *tp;
234 	int length;
235 	register struct errmsg *pe;
236 	extern char *sys_errlist[];
237 
238 	tp = (struct tftphdr *)buf;
239 	tp->th_opcode = htons((u_short)ERROR);
240 	tp->th_code = htons((u_short)error);
241 	for (pe = errmsgs; pe->e_code >= 0; pe++)
242 		if (pe->e_code == error)
243 			break;
244 	if (pe->e_code < 0)
245 		pe->e_msg = sys_errlist[error - 100];
246 	strcpy(tp->th_msg, pe->e_msg);
247 	length = strlen(pe->e_msg) + 4;
248 	if (trace)
249 		tpacket("sent", tp, length);
250 	if (send(f, &sin, buf, length) != length)
251 		perror("nak");
252 }
253 
254 tpacket(s, tp, n)
255 	struct tftphdr *tp;
256 	int n;
257 {
258 	static char *opcodes[] =
259 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
260 	register char *cp, *file;
261 	u_short op = ntohs(tp->th_opcode);
262 	char *index();
263 
264 	if (op < RRQ || op > ERROR)
265 		printf("%s opcode=%x ", s, op);
266 	else
267 		printf("%s %s ", s, opcodes[op]);
268 	switch (op) {
269 
270 	case RRQ:
271 	case WRQ:
272 		n -= 2;
273 		file = cp = tp->th_stuff;
274 		cp = index(cp, '\0');
275 		printf("<file=%s, mode=%s>\n", file, cp + 1);
276 		break;
277 
278 	case DATA:
279 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
280 		break;
281 
282 	case ACK:
283 		printf("<block=%d>\n", ntohs(tp->th_block));
284 		break;
285 
286 	case ERROR:
287 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
288 		break;
289 	}
290 }
291