Commit | Line | Data |
---|---|---|
d0aeaf5a DF |
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 | ||
524aa063 | 7 | #ifndef lint |
d0aeaf5a DF |
8 | static char sccsid[] = "@(#)tftp.c 5.1 (Berkeley) %G%"; |
9 | #endif not lint | |
d3fc13cc SL |
10 | |
11 | /* | |
12 | * TFTP User Program -- Protocol Machines | |
13 | */ | |
14 | #include <sys/types.h> | |
d3fc13cc | 15 | #include <sys/socket.h> |
de3b21e8 SL |
16 | |
17 | #include <netinet/in.h> | |
fc945f1a | 18 | #include <arpa/inet.h> |
86fe57d4 SL |
19 | #include <arpa/tftp.h> |
20 | ||
d3fc13cc SL |
21 | #include <signal.h> |
22 | #include <stdio.h> | |
23 | #include <errno.h> | |
24 | #include <setjmp.h> | |
fc945f1a | 25 | #include <netdb.h> |
de3b21e8 | 26 | |
d3fc13cc SL |
27 | extern int errno; |
28 | extern struct sockaddr_in sin; | |
29 | extern char mode[]; | |
30 | int f; | |
31 | int trace; | |
d3fc13cc | 32 | int connected; |
debfddc0 SL |
33 | char sbuf[BUFSIZ]; /* send buffer */ |
34 | char rbuf[BUFSIZ]; /* receive buffer */ | |
61ad9979 SL |
35 | int rexmtval; |
36 | int maxtimeout; | |
d3fc13cc SL |
37 | int timeout; |
38 | jmp_buf toplevel; | |
61ad9979 | 39 | jmp_buf timeoutbuf; |
d3fc13cc SL |
40 | |
41 | timer() | |
42 | { | |
61ad9979 SL |
43 | |
44 | timeout += rexmtval; | |
45 | if (timeout >= maxtimeout) { | |
d3fc13cc SL |
46 | printf("Transfer timed out.\n"); |
47 | longjmp(toplevel, -1); | |
48 | } | |
61ad9979 | 49 | longjmp(timeoutbuf, 1); |
d3fc13cc SL |
50 | } |
51 | ||
52 | /* | |
53 | * Send the requested file. | |
54 | */ | |
55 | sendfile(fd, name) | |
56 | int fd; | |
57 | char *name; | |
58 | { | |
debfddc0 SL |
59 | register struct tftphdr *stp = (struct tftphdr *)sbuf; |
60 | register struct tftphdr *rtp = (struct tftphdr *)rbuf; | |
d3fc13cc | 61 | register int block = 0, size, n, amount = 0; |
fc945f1a | 62 | struct sockaddr_in from, to; |
d3fc13cc | 63 | time_t start = time(0), delta; |
fc945f1a | 64 | int fromlen, aborted = 0; |
d3fc13cc | 65 | |
fc945f1a | 66 | to = sin; |
61ad9979 | 67 | signal(SIGALRM, timer); |
d3fc13cc | 68 | do { |
61ad9979 SL |
69 | if (block == 0) |
70 | size = makerequest(WRQ, name) - 4; | |
71 | else { | |
debfddc0 | 72 | size = read(fd, stp->th_data, SEGSIZE); |
d3fc13cc | 73 | if (size < 0) { |
fc945f1a | 74 | nak(&to, errno + 100); |
d3fc13cc SL |
75 | break; |
76 | } | |
debfddc0 SL |
77 | stp->th_opcode = htons((u_short)DATA); |
78 | stp->th_block = htons((u_short)block); | |
d3fc13cc SL |
79 | } |
80 | timeout = 0; | |
61ad9979 | 81 | (void) setjmp(timeoutbuf); |
d3fc13cc | 82 | if (trace) |
debfddc0 SL |
83 | tpacket("sent", &to, stp, size + 4); |
84 | n = sendto(f, sbuf, size + 4, 0, (caddr_t)&to, sizeof (to)); | |
de3b21e8 | 85 | if (n != size + 4) { |
61ad9979 | 86 | perror("tftp: sendto"); |
fc945f1a SL |
87 | aborted = 1; |
88 | goto done; | |
d3fc13cc | 89 | } |
61ad9979 | 90 | do { |
fc945f1a | 91 | again: |
61ad9979 SL |
92 | alarm(rexmtval); |
93 | do { | |
94 | fromlen = sizeof (from); | |
debfddc0 | 95 | n = recvfrom(f, rbuf, sizeof (rbuf), 0, |
61ad9979 SL |
96 | (caddr_t)&from, &fromlen); |
97 | } while (n <= 0); | |
d3fc13cc | 98 | alarm(0); |
61ad9979 SL |
99 | if (n < 0) { |
100 | perror("tftp: recvfrom"); | |
fc945f1a SL |
101 | aborted = 1; |
102 | goto done; | |
103 | } | |
104 | if (to.sin_addr.s_addr != from.sin_addr.s_addr) { | |
debfddc0 SL |
105 | tpacket("discarded (wrong host)", |
106 | &from, rtp, n); | |
fc945f1a SL |
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) { | |
debfddc0 SL |
112 | tpacket("discarded (wrong port)", |
113 | &from, rtp, n); | |
fc945f1a | 114 | goto again; |
61ad9979 SL |
115 | } |
116 | if (trace) | |
debfddc0 | 117 | tpacket("received", &from, rtp, n); |
61ad9979 | 118 | /* should verify packet came from server */ |
debfddc0 SL |
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); | |
fc945f1a SL |
124 | aborted = 1; |
125 | goto done; | |
61ad9979 | 126 | } |
debfddc0 | 127 | } while (rtp->th_opcode != ACK && block != rtp->th_block); |
d3fc13cc SL |
128 | if (block > 0) |
129 | amount += size; | |
130 | block++; | |
131 | } while (size == SEGSIZE || block == 1); | |
fc945f1a | 132 | if (!aborted && amount > 0) { |
d3fc13cc SL |
133 | delta = time(0) - start; |
134 | printf("Sent %d bytes in %d seconds.\n", amount, delta); | |
135 | } | |
fc945f1a SL |
136 | done: |
137 | (void) close(fd); | |
138 | return (aborted); | |
d3fc13cc SL |
139 | } |
140 | ||
141 | /* | |
142 | * Receive a file. | |
143 | */ | |
144 | recvfile(fd, name) | |
145 | int fd; | |
146 | char *name; | |
147 | { | |
debfddc0 SL |
148 | register struct tftphdr *stp = (struct tftphdr *)sbuf; |
149 | register struct tftphdr *rtp = (struct tftphdr *)rbuf; | |
d3fc13cc | 150 | register int block = 1, n, size, amount = 0; |
fc945f1a | 151 | struct sockaddr_in from, to; |
d3fc13cc | 152 | time_t start = time(0), delta; |
fc945f1a | 153 | int fromlen, firsttrip = 1, aborted = 0; |
d3fc13cc | 154 | |
fc945f1a | 155 | to = sin; |
61ad9979 | 156 | signal(SIGALRM, timer); |
d3fc13cc | 157 | do { |
61ad9979 SL |
158 | if (firsttrip) { |
159 | size = makerequest(RRQ, name); | |
160 | firsttrip = 0; | |
161 | } else { | |
debfddc0 SL |
162 | stp->th_opcode = htons((u_short)ACK); |
163 | stp->th_block = htons((u_short)(block)); | |
61ad9979 SL |
164 | size = 4; |
165 | block++; | |
166 | } | |
d3fc13cc | 167 | timeout = 0; |
61ad9979 | 168 | (void) setjmp(timeoutbuf); |
d3fc13cc | 169 | if (trace) |
debfddc0 SL |
170 | tpacket("sent", &to, stp, size); |
171 | if (sendto(f, sbuf, size, 0, (caddr_t)&to, | |
fc945f1a | 172 | sizeof (to)) != size) { |
d3fc13cc | 173 | alarm(0); |
61ad9979 | 174 | perror("tftp: sendto"); |
fc945f1a SL |
175 | aborted = 1; |
176 | goto done; | |
d3fc13cc | 177 | } |
61ad9979 | 178 | do { |
fc945f1a | 179 | again: |
61ad9979 | 180 | alarm(rexmtval); |
fc945f1a SL |
181 | do { |
182 | fromlen = sizeof (from); | |
debfddc0 | 183 | n = recvfrom(f, rbuf, sizeof (rbuf), 0, |
61ad9979 | 184 | (caddr_t)&from, &fromlen); |
fc945f1a | 185 | } while (n <= 0); |
61ad9979 SL |
186 | alarm(0); |
187 | if (n < 0) { | |
188 | perror("tftp: recvfrom"); | |
fc945f1a SL |
189 | aborted = 1; |
190 | goto done; | |
191 | } | |
192 | if (to.sin_addr.s_addr != from.sin_addr.s_addr) { | |
debfddc0 SL |
193 | tpacket("discarded (wrong host)", |
194 | &from, rtp, n); | |
fc945f1a SL |
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) { | |
debfddc0 SL |
200 | tpacket("discarded (wrong port)", |
201 | &from, rtp, n); | |
fc945f1a | 202 | goto again; |
61ad9979 SL |
203 | } |
204 | if (trace) | |
debfddc0 SL |
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); | |
fc945f1a SL |
211 | aborted = 1; |
212 | goto done; | |
61ad9979 | 213 | } |
debfddc0 SL |
214 | } while (rtp->th_opcode != DATA && rtp->th_block != block); |
215 | size = write(fd, rtp->th_data, n - 4); | |
d3fc13cc | 216 | if (size < 0) { |
fc945f1a SL |
217 | perror("tftp: write"); |
218 | nak(&to, errno + 100); | |
219 | aborted = 1; | |
220 | goto done; | |
d3fc13cc SL |
221 | } |
222 | amount += size; | |
223 | } while (size == SEGSIZE); | |
fc945f1a | 224 | done: |
debfddc0 SL |
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)); | |
d3fc13cc | 228 | (void) close(fd); |
fc945f1a | 229 | if (!aborted && amount > 0) { |
d3fc13cc SL |
230 | delta = time(0) - start; |
231 | printf("Received %d bytes in %d seconds.\n", amount, delta); | |
232 | } | |
fc945f1a | 233 | return (aborted); |
d3fc13cc SL |
234 | } |
235 | ||
236 | makerequest(request, name) | |
237 | int request; | |
238 | char *name; | |
239 | { | |
debfddc0 | 240 | register struct tftphdr *stp; |
d3fc13cc SL |
241 | int size; |
242 | register char *cp; | |
243 | ||
debfddc0 SL |
244 | stp = (struct tftphdr *)sbuf; |
245 | stp->th_opcode = htons((u_short)request); | |
246 | strcpy(stp->th_stuff, name); | |
d3fc13cc | 247 | size = strlen(name); |
debfddc0 | 248 | cp = stp->th_stuff + strlen(name); |
d3fc13cc SL |
249 | *cp++ = '\0'; |
250 | strcpy(cp, mode); | |
251 | cp += sizeof ("netascii") - 1; | |
252 | *cp++ = '\0'; | |
debfddc0 | 253 | return (cp - sbuf); |
d3fc13cc SL |
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 | */ | |
fc945f1a SL |
277 | nak(to, error) |
278 | struct sockaddr_in *to; | |
d3fc13cc SL |
279 | int error; |
280 | { | |
debfddc0 | 281 | register struct tftphdr *stp; |
d3fc13cc SL |
282 | int length; |
283 | register struct errmsg *pe; | |
284 | extern char *sys_errlist[]; | |
285 | ||
debfddc0 SL |
286 | stp = (struct tftphdr *)sbuf; |
287 | stp->th_opcode = htons((u_short)ERROR); | |
288 | stp->th_code = htons((u_short)error); | |
d3fc13cc SL |
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]; | |
debfddc0 | 294 | strcpy(stp->th_msg, pe->e_msg); |
d3fc13cc SL |
295 | length = strlen(pe->e_msg) + 4; |
296 | if (trace) | |
debfddc0 SL |
297 | tpacket("sent", to, stp, length); |
298 | if (sendto(f, sbuf, length, 0, to, sizeof (*to)) != length) | |
fc945f1a | 299 | perror("tftp: nak"); |
d3fc13cc SL |
300 | } |
301 | ||
fc945f1a SL |
302 | tpacket(s, sin, tp, n) |
303 | struct sockaddr_in *sin; | |
d3fc13cc SL |
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 | ||
fc945f1a SL |
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 | } | |
d3fc13cc | 322 | if (op < RRQ || op > ERROR) |
fc945f1a | 323 | printf("opcode=%x ", op); |
d3fc13cc | 324 | else |
fc945f1a | 325 | printf("%s ", opcodes[op]); |
d3fc13cc SL |
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; | |
6e87e2a6 SL |
347 | |
348 | default: | |
349 | putchar('\n'); | |
350 | break; | |
d3fc13cc SL |
351 | } |
352 | } |