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