Commit | Line | Data |
---|---|---|
d0aeaf5a | 1 | /* |
6457cfd6 | 2 | * Copyright (c) 1985 Regents of the University of California. |
d0aeaf5a DF |
3 | * All rights reserved. The Berkeley software License Agreement |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
6 | ||
524aa063 | 7 | #ifndef lint |
15631344 | 8 | static char sccsid[] = "@(#)tftp.c 5.5 (Berkeley) %G%"; |
d0aeaf5a | 9 | #endif not lint |
d3fc13cc | 10 | |
6457cfd6 GM |
11 | /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ |
12 | ||
d3fc13cc SL |
13 | /* |
14 | * TFTP User Program -- Protocol Machines | |
15 | */ | |
16 | #include <sys/types.h> | |
d3fc13cc | 17 | #include <sys/socket.h> |
6457cfd6 | 18 | #include <sys/time.h> |
de3b21e8 SL |
19 | |
20 | #include <netinet/in.h> | |
6457cfd6 | 21 | |
86fe57d4 SL |
22 | #include <arpa/tftp.h> |
23 | ||
d3fc13cc SL |
24 | #include <signal.h> |
25 | #include <stdio.h> | |
26 | #include <errno.h> | |
27 | #include <setjmp.h> | |
de3b21e8 | 28 | |
d3fc13cc | 29 | extern int errno; |
6457cfd6 GM |
30 | |
31 | extern struct sockaddr_in sin; /* filled in by main */ | |
32 | extern int f; /* the opened socket */ | |
33 | extern int trace; | |
34 | extern int verbose; | |
35 | extern int rexmtval; | |
36 | extern int maxtimeout; | |
37 | ||
38 | #define PKTSIZE SEGSIZE+4 | |
39 | char ackbuf[PKTSIZE]; | |
d3fc13cc SL |
40 | int timeout; |
41 | jmp_buf toplevel; | |
61ad9979 | 42 | jmp_buf timeoutbuf; |
d3fc13cc SL |
43 | |
44 | timer() | |
45 | { | |
61ad9979 SL |
46 | |
47 | timeout += rexmtval; | |
48 | if (timeout >= maxtimeout) { | |
d3fc13cc SL |
49 | printf("Transfer timed out.\n"); |
50 | longjmp(toplevel, -1); | |
51 | } | |
61ad9979 | 52 | longjmp(timeoutbuf, 1); |
d3fc13cc SL |
53 | } |
54 | ||
55 | /* | |
56 | * Send the requested file. | |
57 | */ | |
6457cfd6 | 58 | sendfile(fd, name, mode) |
d3fc13cc SL |
59 | int fd; |
60 | char *name; | |
6457cfd6 | 61 | char *mode; |
d3fc13cc | 62 | { |
6457cfd6 GM |
63 | register struct tftphdr *ap; /* data and ack packets */ |
64 | struct tftphdr *r_init(), *dp; | |
65 | register int block = 0, size, n; | |
66 | register unsigned long amount = 0; | |
67 | struct sockaddr_in from; | |
68 | int fromlen; | |
69 | int convert; /* true if doing nl->crlf conversion */ | |
70 | FILE *file; | |
71 | ||
72 | startclock(); /* start stat's clock */ | |
73 | dp = r_init(); /* reset fillbuf/read-ahead code */ | |
74 | ap = (struct tftphdr *)ackbuf; | |
75 | file = fdopen(fd, "r"); | |
76 | convert = !strcmp(mode, "netascii"); | |
77 | ||
61ad9979 | 78 | signal(SIGALRM, timer); |
d3fc13cc | 79 | do { |
61ad9979 | 80 | if (block == 0) |
6457cfd6 | 81 | size = makerequest(WRQ, name, dp, mode) - 4; |
61ad9979 | 82 | else { |
6457cfd6 GM |
83 | /* size = read(fd, dp->th_data, SEGSIZE); */ |
84 | size = readit(file, &dp, convert); | |
d3fc13cc | 85 | if (size < 0) { |
6457cfd6 | 86 | nak(errno + 100); |
d3fc13cc SL |
87 | break; |
88 | } | |
6457cfd6 GM |
89 | dp->th_opcode = htons((u_short)DATA); |
90 | dp->th_block = htons((u_short)block); | |
d3fc13cc SL |
91 | } |
92 | timeout = 0; | |
61ad9979 | 93 | (void) setjmp(timeoutbuf); |
f65510cf | 94 | send_data: |
d3fc13cc | 95 | if (trace) |
6457cfd6 GM |
96 | tpacket("sent", dp, size + 4); |
97 | n = sendto(f, dp, size + 4, 0, (caddr_t)&sin, sizeof (sin)); | |
de3b21e8 | 98 | if (n != size + 4) { |
61ad9979 | 99 | perror("tftp: sendto"); |
6457cfd6 | 100 | goto abort; |
d3fc13cc | 101 | } |
6457cfd6 | 102 | read_ahead(file, convert); |
f65510cf | 103 | for ( ; ; ) { |
61ad9979 SL |
104 | alarm(rexmtval); |
105 | do { | |
106 | fromlen = sizeof (from); | |
6457cfd6 | 107 | n = recvfrom(f, ackbuf, sizeof (ackbuf), 0, |
61ad9979 SL |
108 | (caddr_t)&from, &fromlen); |
109 | } while (n <= 0); | |
d3fc13cc | 110 | alarm(0); |
61ad9979 SL |
111 | if (n < 0) { |
112 | perror("tftp: recvfrom"); | |
6457cfd6 | 113 | goto abort; |
61ad9979 | 114 | } |
6457cfd6 | 115 | sin.sin_port = from.sin_port; /* added */ |
61ad9979 | 116 | if (trace) |
6457cfd6 | 117 | tpacket("received", ap, n); |
61ad9979 | 118 | /* should verify packet came from server */ |
6457cfd6 GM |
119 | ap->th_opcode = ntohs(ap->th_opcode); |
120 | ap->th_block = ntohs(ap->th_block); | |
121 | if (ap->th_opcode == ERROR) { | |
122 | printf("Error code %d: %s\n", ap->th_code, | |
123 | ap->th_msg); | |
124 | goto abort; | |
61ad9979 | 125 | } |
f65510cf | 126 | if (ap->th_opcode == ACK) { |
15631344 GM |
127 | int j; |
128 | ||
f65510cf GM |
129 | if (ap->th_block == block) { |
130 | break; | |
15631344 GM |
131 | } |
132 | /* On an error, try to synchronize | |
133 | * both sides. | |
134 | */ | |
135 | j = synchnet(f); | |
136 | if (j && trace) { | |
137 | printf("discarded %d packets\n", | |
138 | j); | |
139 | } | |
140 | if (ap->th_block == (block-1)) { | |
141 | goto send_data; | |
f65510cf GM |
142 | } |
143 | } | |
144 | } | |
d3fc13cc SL |
145 | if (block > 0) |
146 | amount += size; | |
147 | block++; | |
148 | } while (size == SEGSIZE || block == 1); | |
6457cfd6 GM |
149 | abort: |
150 | fclose(file); | |
151 | stopclock(); | |
152 | if (amount > 0) | |
153 | printstats("Sent", amount); | |
d3fc13cc SL |
154 | } |
155 | ||
156 | /* | |
157 | * Receive a file. | |
158 | */ | |
6457cfd6 | 159 | recvfile(fd, name, mode) |
d3fc13cc SL |
160 | int fd; |
161 | char *name; | |
6457cfd6 | 162 | char *mode; |
d3fc13cc | 163 | { |
6457cfd6 GM |
164 | register struct tftphdr *ap; |
165 | struct tftphdr *dp, *w_init(); | |
166 | register int block = 1, n, size; | |
167 | unsigned long amount = 0; | |
168 | struct sockaddr_in from; | |
169 | int fromlen, firsttrip = 1; | |
170 | FILE *file; | |
171 | int convert; /* true if converting crlf -> lf */ | |
172 | ||
173 | startclock(); | |
174 | dp = w_init(); | |
175 | ap = (struct tftphdr *)ackbuf; | |
176 | file = fdopen(fd, "w"); | |
177 | convert = !strcmp(mode, "netascii"); | |
178 | ||
61ad9979 | 179 | signal(SIGALRM, timer); |
d3fc13cc | 180 | do { |
61ad9979 | 181 | if (firsttrip) { |
6457cfd6 | 182 | size = makerequest(RRQ, name, ap, mode); |
61ad9979 SL |
183 | firsttrip = 0; |
184 | } else { | |
6457cfd6 GM |
185 | ap->th_opcode = htons((u_short)ACK); |
186 | ap->th_block = htons((u_short)(block)); | |
61ad9979 SL |
187 | size = 4; |
188 | block++; | |
189 | } | |
d3fc13cc | 190 | timeout = 0; |
61ad9979 | 191 | (void) setjmp(timeoutbuf); |
6457cfd6 | 192 | send_ack: |
d3fc13cc | 193 | if (trace) |
6457cfd6 GM |
194 | tpacket("sent", ap, size); |
195 | if (sendto(f, ackbuf, size, 0, (caddr_t)&sin, | |
196 | sizeof (sin)) != size) { | |
d3fc13cc | 197 | alarm(0); |
61ad9979 | 198 | perror("tftp: sendto"); |
6457cfd6 | 199 | goto abort; |
d3fc13cc | 200 | } |
6457cfd6 GM |
201 | write_behind(file, convert); |
202 | for ( ; ; ) { | |
61ad9979 | 203 | alarm(rexmtval); |
6457cfd6 | 204 | do { |
fc945f1a | 205 | fromlen = sizeof (from); |
6457cfd6 | 206 | n = recvfrom(f, dp, PKTSIZE, 0, |
61ad9979 | 207 | (caddr_t)&from, &fromlen); |
fc945f1a | 208 | } while (n <= 0); |
61ad9979 SL |
209 | alarm(0); |
210 | if (n < 0) { | |
211 | perror("tftp: recvfrom"); | |
6457cfd6 | 212 | goto abort; |
61ad9979 | 213 | } |
6457cfd6 | 214 | sin.sin_port = from.sin_port; /* added */ |
61ad9979 | 215 | if (trace) |
6457cfd6 GM |
216 | tpacket("received", dp, n); |
217 | /* should verify client address */ | |
218 | dp->th_opcode = ntohs(dp->th_opcode); | |
219 | dp->th_block = ntohs(dp->th_block); | |
220 | if (dp->th_opcode == ERROR) { | |
221 | printf("Error code %d: %s\n", dp->th_code, | |
222 | dp->th_msg); | |
223 | goto abort; | |
224 | } | |
225 | if (dp->th_opcode == DATA) { | |
15631344 GM |
226 | int j; |
227 | ||
228 | if (dp->th_block == block) { | |
6457cfd6 | 229 | break; /* have next packet */ |
15631344 GM |
230 | } |
231 | /* On an error, try to synchronize | |
232 | * both sides. | |
233 | */ | |
234 | j = synchnet(f); | |
235 | if (j && trace) { | |
236 | printf("discarded %d packets\n", j); | |
237 | } | |
238 | if (dp->th_block == (block-1)) { | |
6457cfd6 | 239 | goto send_ack; /* resend ack */ |
15631344 | 240 | } |
61ad9979 | 241 | } |
6457cfd6 GM |
242 | } |
243 | /* size = write(fd, dp->th_data, n - 4); */ | |
244 | size = writeit(file, &dp, n - 4, convert); | |
d3fc13cc | 245 | if (size < 0) { |
6457cfd6 GM |
246 | nak(errno + 100); |
247 | break; | |
d3fc13cc SL |
248 | } |
249 | amount += size; | |
250 | } while (size == SEGSIZE); | |
6457cfd6 GM |
251 | abort: /* ok to ack, since user */ |
252 | ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ | |
253 | ap->th_block = htons((u_short)block); | |
254 | (void) sendto(f, ackbuf, 4, 0, &sin, sizeof (sin)); | |
255 | write_behind(file, convert); /* flush last buffer */ | |
256 | fclose(file); | |
257 | stopclock(); | |
258 | if (amount > 0) | |
259 | printstats("Received", amount); | |
d3fc13cc SL |
260 | } |
261 | ||
6457cfd6 | 262 | makerequest(request, name, tp, mode) |
d3fc13cc | 263 | int request; |
6457cfd6 GM |
264 | char *name, *mode; |
265 | struct tftphdr *tp; | |
d3fc13cc | 266 | { |
d3fc13cc SL |
267 | register char *cp; |
268 | ||
6457cfd6 GM |
269 | tp->th_opcode = htons((u_short)request); |
270 | cp = tp->th_stuff; | |
271 | strcpy(cp, name); | |
272 | cp += strlen(name); | |
d3fc13cc SL |
273 | *cp++ = '\0'; |
274 | strcpy(cp, mode); | |
6457cfd6 | 275 | cp += strlen(mode); |
d3fc13cc | 276 | *cp++ = '\0'; |
6457cfd6 | 277 | return (cp - (char *)tp); |
d3fc13cc SL |
278 | } |
279 | ||
280 | struct errmsg { | |
281 | int e_code; | |
282 | char *e_msg; | |
283 | } errmsgs[] = { | |
284 | { EUNDEF, "Undefined error code" }, | |
285 | { ENOTFOUND, "File not found" }, | |
286 | { EACCESS, "Access violation" }, | |
287 | { ENOSPACE, "Disk full or allocation exceeded" }, | |
288 | { EBADOP, "Illegal TFTP operation" }, | |
289 | { EBADID, "Unknown transfer ID" }, | |
290 | { EEXISTS, "File already exists" }, | |
291 | { ENOUSER, "No such user" }, | |
292 | { -1, 0 } | |
293 | }; | |
294 | ||
295 | /* | |
296 | * Send a nak packet (error message). | |
297 | * Error code passed in is one of the | |
298 | * standard TFTP codes, or a UNIX errno | |
299 | * offset by 100. | |
300 | */ | |
6457cfd6 | 301 | nak(error) |
d3fc13cc SL |
302 | int error; |
303 | { | |
6457cfd6 | 304 | register struct tftphdr *tp; |
d3fc13cc SL |
305 | int length; |
306 | register struct errmsg *pe; | |
307 | extern char *sys_errlist[]; | |
308 | ||
6457cfd6 GM |
309 | tp = (struct tftphdr *)ackbuf; |
310 | tp->th_opcode = htons((u_short)ERROR); | |
311 | tp->th_code = htons((u_short)error); | |
d3fc13cc SL |
312 | for (pe = errmsgs; pe->e_code >= 0; pe++) |
313 | if (pe->e_code == error) | |
314 | break; | |
6457cfd6 | 315 | if (pe->e_code < 0) { |
d3fc13cc | 316 | pe->e_msg = sys_errlist[error - 100]; |
6457cfd6 GM |
317 | tp->th_code = EUNDEF; |
318 | } | |
319 | strcpy(tp->th_msg, pe->e_msg); | |
d3fc13cc SL |
320 | length = strlen(pe->e_msg) + 4; |
321 | if (trace) | |
6457cfd6 GM |
322 | tpacket("sent", tp, length); |
323 | if (sendto(f, ackbuf, length, 0, &sin, sizeof (sin)) != length) | |
324 | perror("nak"); | |
d3fc13cc SL |
325 | } |
326 | ||
6457cfd6 | 327 | tpacket(s, tp, n) |
7457289f | 328 | char *s; |
d3fc13cc SL |
329 | struct tftphdr *tp; |
330 | int n; | |
331 | { | |
332 | static char *opcodes[] = | |
333 | { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; | |
334 | register char *cp, *file; | |
335 | u_short op = ntohs(tp->th_opcode); | |
336 | char *index(); | |
337 | ||
338 | if (op < RRQ || op > ERROR) | |
6457cfd6 | 339 | printf("%s opcode=%x ", s, op); |
d3fc13cc | 340 | else |
6457cfd6 | 341 | printf("%s %s ", s, opcodes[op]); |
d3fc13cc SL |
342 | switch (op) { |
343 | ||
344 | case RRQ: | |
345 | case WRQ: | |
346 | n -= 2; | |
347 | file = cp = tp->th_stuff; | |
348 | cp = index(cp, '\0'); | |
349 | printf("<file=%s, mode=%s>\n", file, cp + 1); | |
350 | break; | |
351 | ||
352 | case DATA: | |
353 | printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); | |
354 | break; | |
355 | ||
356 | case ACK: | |
357 | printf("<block=%d>\n", ntohs(tp->th_block)); | |
358 | break; | |
359 | ||
360 | case ERROR: | |
361 | printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); | |
362 | break; | |
363 | } | |
364 | } | |
6457cfd6 GM |
365 | |
366 | struct timeval tstart; | |
367 | struct timeval tstop; | |
368 | struct timezone zone; | |
369 | ||
370 | startclock() { | |
371 | gettimeofday(&tstart, &zone); | |
372 | } | |
373 | ||
374 | stopclock() { | |
375 | gettimeofday(&tstop, &zone); | |
376 | } | |
377 | ||
378 | printstats(direction, amount) | |
379 | char *direction; | |
380 | unsigned long amount; | |
381 | { | |
382 | double delta; | |
383 | /* compute delta in 1/10's second units */ | |
384 | delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - | |
385 | ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); | |
386 | delta = delta/10.; /* back to seconds */ | |
387 | printf("%s %d bytes in %.1f seconds", direction, amount, delta); | |
388 | if (verbose) | |
389 | printf(" [%.0f bits/sec]", (amount*8.)/delta); | |
390 | putchar('\n'); | |
391 | } | |
392 |