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