]>
Commit | Line | Data |
---|---|---|
b7080c8e A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
07f47057 A |
6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. |
7 | * | |
8 | * This file contains Original Code and/or Modifications of Original Code | |
9 | * as defined in and that are subject to the Apple Public Source License | |
10 | * Version 2.0 (the 'License'). You may not use this file except in | |
11 | * compliance with the License. Please obtain a copy of the License at | |
12 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
13 | * file. | |
b7080c8e A |
14 | * |
15 | * The Original Code and all software distributed under the License are | |
16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
07f47057 A |
19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
20 | * Please see the License for the specific language governing rights and | |
21 | * limitations under the License. | |
b7080c8e A |
22 | * |
23 | * @APPLE_LICENSE_HEADER_END@ | |
24 | */ | |
25 | /* | |
26 | * Copyright (c) 1983, 1993 | |
27 | * The Regents of the University of California. All rights reserved. | |
28 | * | |
29 | * Redistribution and use in source and binary forms, with or without | |
30 | * modification, are permitted provided that the following conditions | |
31 | * are met: | |
32 | * 1. Redistributions of source code must retain the above copyright | |
33 | * notice, this list of conditions and the following disclaimer. | |
34 | * 2. Redistributions in binary form must reproduce the above copyright | |
35 | * notice, this list of conditions and the following disclaimer in the | |
36 | * documentation and/or other materials provided with the distribution. | |
37 | * 3. All advertising materials mentioning features or use of this software | |
38 | * must display the following acknowledgement: | |
39 | * This product includes software developed by the University of | |
40 | * California, Berkeley and its contributors. | |
41 | * 4. Neither the name of the University nor the names of its contributors | |
42 | * may be used to endorse or promote products derived from this software | |
43 | * without specific prior written permission. | |
44 | * | |
45 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
46 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
47 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
48 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
49 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
50 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
51 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
52 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
53 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
54 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
55 | * SUCH DAMAGE. | |
56 | */ | |
57 | ||
58 | ||
59 | /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ | |
60 | ||
61 | /* | |
62 | * TFTP User Program -- Protocol Machines | |
63 | */ | |
64 | #include <sys/types.h> | |
65 | #include <sys/socket.h> | |
66 | #include <sys/time.h> | |
67 | ||
68 | #include <netinet/in.h> | |
69 | ||
70 | #include <arpa/tftp.h> | |
71 | ||
72 | #include <errno.h> | |
73 | #include <setjmp.h> | |
74 | #include <signal.h> | |
75 | #include <stdio.h> | |
76 | #include <unistd.h> | |
77 | ||
78 | #include "extern.h" | |
79 | #include "tftpsubs.h" | |
80 | ||
81 | extern int errno; | |
82 | ||
83 | extern struct sockaddr_in peeraddr; /* filled in by main */ | |
84 | extern int f; /* the opened socket */ | |
85 | extern int trace; | |
86 | extern int verbose; | |
87 | extern int rexmtval; | |
88 | extern int maxtimeout; | |
89 | ||
90 | extern jmp_buf toplevel; /* filled in by main */ | |
91 | ||
92 | #define PKTSIZE SEGSIZE+4 | |
93 | char ackbuf[PKTSIZE]; | |
94 | int timeout; | |
95 | jmp_buf timeoutbuf; | |
96 | ||
97 | static void nak __P((int)); | |
98 | static int makerequest __P((int, const char *, struct tftphdr *, const char *)); | |
99 | static void printstats __P((const char *, unsigned long)); | |
100 | static void startclock __P((void)); | |
101 | static void stopclock __P((void)); | |
102 | static void timer __P((int)); | |
103 | static void tpacket __P((const char *, struct tftphdr *, int)); | |
104 | ||
105 | /* | |
106 | * Send the requested file. | |
107 | */ | |
108 | void | |
109 | tftp_sendfile(fd, name, mode) | |
110 | int fd; | |
111 | char *name; | |
112 | char *mode; | |
113 | { | |
114 | register struct tftphdr *ap; /* data and ack packets */ | |
115 | struct tftphdr *r_init(), *dp; | |
116 | register int n; | |
117 | volatile int block, size, convert; | |
118 | volatile unsigned long amount; | |
119 | struct sockaddr_in from; | |
120 | int fromlen; | |
121 | FILE *file; | |
122 | ||
123 | startclock(); /* start stat's clock */ | |
124 | dp = r_init(); /* reset fillbuf/read-ahead code */ | |
125 | ap = (struct tftphdr *)ackbuf; | |
126 | file = fdopen(fd, "r"); | |
127 | convert = !strcmp(mode, "netascii"); | |
128 | block = 0; | |
129 | amount = 0; | |
130 | ||
131 | signal(SIGALRM, timer); | |
132 | do { | |
133 | if (block == 0) | |
134 | size = makerequest(WRQ, name, dp, mode) - 4; | |
135 | else { | |
136 | /* size = read(fd, dp->th_data, SEGSIZE); */ | |
137 | size = readit(file, &dp, convert); | |
138 | if (size < 0) { | |
139 | nak(errno + 100); | |
140 | break; | |
141 | } | |
142 | dp->th_opcode = htons((u_short)DATA); | |
143 | dp->th_block = htons((u_short)block); | |
144 | } | |
145 | timeout = 0; | |
146 | (void) setjmp(timeoutbuf); | |
147 | send_data: | |
148 | if (trace) | |
149 | tpacket("sent", dp, size + 4); | |
150 | n = sendto(f, dp, size + 4, 0, | |
151 | (struct sockaddr *)&peeraddr, sizeof(peeraddr)); | |
152 | if (n != size + 4) { | |
153 | perror("tftp: sendto"); | |
154 | goto abort; | |
155 | } | |
156 | read_ahead(file, convert); | |
157 | for ( ; ; ) { | |
158 | alarm(rexmtval); | |
159 | do { | |
160 | fromlen = sizeof(from); | |
161 | n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, | |
162 | (struct sockaddr *)&from, &fromlen); | |
163 | } while (n <= 0); | |
164 | alarm(0); | |
165 | if (n < 0) { | |
166 | perror("tftp: recvfrom"); | |
167 | goto abort; | |
168 | } | |
169 | peeraddr.sin_port = from.sin_port; /* added */ | |
170 | if (trace) | |
171 | tpacket("received", ap, n); | |
172 | /* should verify packet came from server */ | |
173 | ap->th_opcode = ntohs(ap->th_opcode); | |
174 | ap->th_block = ntohs(ap->th_block); | |
175 | if (ap->th_opcode == ERROR) { | |
176 | printf("Error code %d: %s\n", ap->th_code, | |
177 | ap->th_msg); | |
178 | goto abort; | |
179 | } | |
180 | if (ap->th_opcode == ACK) { | |
181 | int j; | |
182 | ||
183 | if (ap->th_block == block) { | |
184 | break; | |
185 | } | |
186 | /* On an error, try to synchronize | |
187 | * both sides. | |
188 | */ | |
189 | j = synchnet(f); | |
190 | if (j && trace) { | |
191 | printf("discarded %d packets\n", | |
192 | j); | |
193 | } | |
194 | if (ap->th_block == (block-1)) { | |
195 | goto send_data; | |
196 | } | |
197 | } | |
198 | } | |
199 | if (block > 0) | |
200 | amount += size; | |
201 | block++; | |
202 | } while (size == SEGSIZE || block == 1); | |
203 | abort: | |
204 | fclose(file); | |
205 | stopclock(); | |
206 | if (amount > 0) | |
207 | printstats("Sent", amount); | |
208 | } | |
209 | ||
210 | /* | |
211 | * Receive a file. | |
212 | */ | |
213 | void | |
214 | recvfile(fd, name, mode) | |
215 | int fd; | |
216 | char *name; | |
217 | char *mode; | |
218 | { | |
219 | register struct tftphdr *ap; | |
220 | struct tftphdr *dp, *w_init(); | |
221 | register int n; | |
222 | volatile int block, size, firsttrip; | |
223 | volatile unsigned long amount; | |
224 | struct sockaddr_in from; | |
225 | int fromlen; | |
226 | FILE *file; | |
227 | volatile int convert; /* true if converting crlf -> lf */ | |
228 | ||
229 | startclock(); | |
230 | dp = w_init(); | |
231 | ap = (struct tftphdr *)ackbuf; | |
232 | file = fdopen(fd, "w"); | |
233 | convert = !strcmp(mode, "netascii"); | |
234 | block = 1; | |
235 | firsttrip = 1; | |
236 | amount = 0; | |
237 | ||
238 | signal(SIGALRM, timer); | |
239 | do { | |
240 | if (firsttrip) { | |
241 | size = makerequest(RRQ, name, ap, mode); | |
242 | firsttrip = 0; | |
243 | } else { | |
244 | ap->th_opcode = htons((u_short)ACK); | |
245 | ap->th_block = htons((u_short)(block)); | |
246 | size = 4; | |
247 | block++; | |
248 | } | |
249 | timeout = 0; | |
250 | (void) setjmp(timeoutbuf); | |
251 | send_ack: | |
252 | if (trace) | |
253 | tpacket("sent", ap, size); | |
254 | if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, | |
255 | sizeof(peeraddr)) != size) { | |
256 | alarm(0); | |
257 | perror("tftp: sendto"); | |
258 | goto abort; | |
259 | } | |
260 | write_behind(file, convert); | |
261 | for ( ; ; ) { | |
262 | alarm(rexmtval); | |
263 | do { | |
264 | fromlen = sizeof(from); | |
265 | n = recvfrom(f, dp, PKTSIZE, 0, | |
266 | (struct sockaddr *)&from, &fromlen); | |
267 | } while (n <= 0); | |
268 | alarm(0); | |
269 | if (n < 0) { | |
270 | perror("tftp: recvfrom"); | |
271 | goto abort; | |
272 | } | |
273 | peeraddr.sin_port = from.sin_port; /* added */ | |
274 | if (trace) | |
275 | tpacket("received", dp, n); | |
276 | /* should verify client address */ | |
277 | dp->th_opcode = ntohs(dp->th_opcode); | |
278 | dp->th_block = ntohs(dp->th_block); | |
279 | if (dp->th_opcode == ERROR) { | |
280 | printf("Error code %d: %s\n", dp->th_code, | |
281 | dp->th_msg); | |
282 | goto abort; | |
283 | } | |
284 | if (dp->th_opcode == DATA) { | |
285 | int j; | |
286 | ||
287 | if (dp->th_block == block) { | |
288 | break; /* have next packet */ | |
289 | } | |
290 | /* On an error, try to synchronize | |
291 | * both sides. | |
292 | */ | |
293 | j = synchnet(f); | |
294 | if (j && trace) { | |
295 | printf("discarded %d packets\n", j); | |
296 | } | |
297 | if (dp->th_block == (block-1)) { | |
298 | goto send_ack; /* resend ack */ | |
299 | } | |
300 | } | |
301 | } | |
302 | /* size = write(fd, dp->th_data, n - 4); */ | |
303 | size = writeit(file, &dp, n - 4, convert); | |
304 | if (size < 0) { | |
305 | nak(errno + 100); | |
306 | break; | |
307 | } | |
308 | amount += size; | |
309 | } while (size == SEGSIZE); | |
310 | abort: /* ok to ack, since user */ | |
311 | ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ | |
312 | ap->th_block = htons((u_short)block); | |
313 | (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, | |
314 | sizeof(peeraddr)); | |
315 | write_behind(file, convert); /* flush last buffer */ | |
316 | fclose(file); | |
317 | stopclock(); | |
318 | if (amount > 0) | |
319 | printstats("Received", amount); | |
320 | } | |
321 | ||
322 | static int | |
323 | makerequest(request, name, tp, mode) | |
324 | int request; | |
325 | const char *name; | |
326 | struct tftphdr *tp; | |
327 | const char *mode; | |
328 | { | |
329 | register char *cp; | |
330 | ||
331 | tp->th_opcode = htons((u_short)request); | |
332 | cp = tp->th_stuff; | |
333 | strcpy(cp, name); | |
334 | cp += strlen(name); | |
335 | *cp++ = '\0'; | |
336 | strcpy(cp, mode); | |
337 | cp += strlen(mode); | |
338 | *cp++ = '\0'; | |
339 | return (cp - (char *)tp); | |
340 | } | |
341 | ||
342 | struct errmsg { | |
343 | int e_code; | |
344 | char *e_msg; | |
345 | } errmsgs[] = { | |
346 | { EUNDEF, "Undefined error code" }, | |
347 | { ENOTFOUND, "File not found" }, | |
348 | { EACCESS, "Access violation" }, | |
349 | { ENOSPACE, "Disk full or allocation exceeded" }, | |
350 | { EBADOP, "Illegal TFTP operation" }, | |
351 | { EBADID, "Unknown transfer ID" }, | |
352 | { EEXISTS, "File already exists" }, | |
353 | { ENOUSER, "No such user" }, | |
354 | { -1, 0 } | |
355 | }; | |
356 | ||
357 | /* | |
358 | * Send a nak packet (error message). | |
359 | * Error code passed in is one of the | |
360 | * standard TFTP codes, or a UNIX errno | |
361 | * offset by 100. | |
362 | */ | |
363 | static void | |
364 | nak(error) | |
365 | int error; | |
366 | { | |
367 | register struct errmsg *pe; | |
368 | register struct tftphdr *tp; | |
369 | int length; | |
370 | char *strerror(); | |
371 | ||
372 | tp = (struct tftphdr *)ackbuf; | |
373 | tp->th_opcode = htons((u_short)ERROR); | |
374 | tp->th_code = htons((u_short)error); | |
375 | for (pe = errmsgs; pe->e_code >= 0; pe++) | |
376 | if (pe->e_code == error) | |
377 | break; | |
378 | if (pe->e_code < 0) { | |
379 | pe->e_msg = strerror(error - 100); | |
380 | tp->th_code = EUNDEF; | |
381 | } | |
382 | strcpy(tp->th_msg, pe->e_msg); | |
383 | length = strlen(pe->e_msg) + 4; | |
384 | if (trace) | |
385 | tpacket("sent", tp, length); | |
386 | if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, | |
387 | sizeof(peeraddr)) != length) | |
388 | perror("nak"); | |
389 | } | |
390 | ||
391 | static void | |
392 | tpacket(s, tp, n) | |
393 | const char *s; | |
394 | struct tftphdr *tp; | |
395 | int n; | |
396 | { | |
397 | static char *opcodes[] = | |
398 | { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; | |
399 | register char *cp, *file; | |
400 | u_short op = ntohs(tp->th_opcode); | |
401 | char *index(); | |
402 | ||
403 | if (op < RRQ || op > ERROR) | |
404 | printf("%s opcode=%x ", s, op); | |
405 | else | |
406 | printf("%s %s ", s, opcodes[op]); | |
407 | switch (op) { | |
408 | ||
409 | case RRQ: | |
410 | case WRQ: | |
411 | n -= 2; | |
412 | file = cp = tp->th_stuff; | |
413 | cp = index(cp, '\0'); | |
414 | printf("<file=%s, mode=%s>\n", file, cp + 1); | |
415 | break; | |
416 | ||
417 | case DATA: | |
418 | printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); | |
419 | break; | |
420 | ||
421 | case ACK: | |
422 | printf("<block=%d>\n", ntohs(tp->th_block)); | |
423 | break; | |
424 | ||
425 | case ERROR: | |
426 | printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); | |
427 | break; | |
428 | } | |
429 | } | |
430 | ||
431 | struct timeval tstart; | |
432 | struct timeval tstop; | |
433 | ||
434 | static void | |
435 | startclock() | |
436 | { | |
437 | ||
438 | (void)gettimeofday(&tstart, NULL); | |
439 | } | |
440 | ||
441 | static void | |
442 | stopclock() | |
443 | { | |
444 | ||
445 | (void)gettimeofday(&tstop, NULL); | |
446 | } | |
447 | ||
448 | static void | |
449 | printstats(direction, amount) | |
450 | const char *direction; | |
451 | unsigned long amount; | |
452 | { | |
453 | double delta; | |
454 | /* compute delta in 1/10's second units */ | |
455 | delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - | |
456 | ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); | |
457 | delta = delta/10.; /* back to seconds */ | |
458 | printf("%s %d bytes in %.1f seconds", direction, amount, delta); | |
459 | if (verbose) | |
460 | printf(" [%.0f bits/sec]", (amount*8.)/delta); | |
461 | putchar('\n'); | |
462 | } | |
463 | ||
464 | static void | |
465 | timer(sig) | |
466 | int sig; | |
467 | { | |
468 | ||
469 | timeout += rexmtval; | |
470 | if (timeout >= maxtimeout) { | |
471 | printf("Transfer timed out.\n"); | |
472 | longjmp(toplevel, -1); | |
473 | } | |
474 | longjmp(timeoutbuf, 1); | |
475 | } |