]> git.saurik.com Git - apple/network_cmds.git/blame - tftp.tproj/tftp.c
network_cmds-245.19.tar.gz
[apple/network_cmds.git] / tftp.tproj / tftp.c
CommitLineData
2b484d24
A
1/* $NetBSD: tftp.c,v 1.18 2003/08/07 11:16:14 agc Exp $ */
2
b7080c8e
A
3/*
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
2b484d24 15 * 3. Neither the name of the University nor the names of its contributors
b7080c8e
A
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
2b484d24
A
32#include <sys/cdefs.h>
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93";
36#else
37__RCSID("$NetBSD: tftp.c,v 1.18 2003/08/07 11:16:14 agc Exp $");
38#endif
39#endif /* not lint */
b7080c8e
A
40
41/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
42
43/*
44 * TFTP User Program -- Protocol Machines
45 */
46#include <sys/types.h>
2b484d24 47#include <sys/param.h>
b7080c8e 48#include <sys/socket.h>
2b484d24 49#include <sys/stat.h>
b7080c8e
A
50#include <sys/time.h>
51
52#include <netinet/in.h>
53
2b484d24 54#include "tftp.h"
b7080c8e 55
2b484d24 56#include <err.h>
b7080c8e
A
57#include <errno.h>
58#include <setjmp.h>
59#include <signal.h>
60#include <stdio.h>
2b484d24
A
61#include <stdlib.h>
62#include <string.h>
b7080c8e 63#include <unistd.h>
2b484d24 64#include <netdb.h>
b7080c8e
A
65
66#include "extern.h"
67#include "tftpsubs.h"
68
2b484d24 69extern struct sockaddr_storage peeraddr; /* filled in by main */
b7080c8e
A
70extern int f; /* the opened socket */
71extern int trace;
72extern int verbose;
2b484d24 73extern int def_rexmtval;
b7080c8e
A
74extern int rexmtval;
75extern int maxtimeout;
2b484d24
A
76extern int tsize;
77extern int tout;
78extern int def_blksize;
79extern int blksize;
b7080c8e 80
b7080c8e
A
81char ackbuf[PKTSIZE];
82int timeout;
2b484d24 83extern jmp_buf toplevel;
b7080c8e
A
84jmp_buf timeoutbuf;
85
2b484d24
A
86static void nak __P((int, struct sockaddr *));
87static int makerequest __P((int, const char *, struct tftphdr *, const char *, off_t));
b7080c8e
A
88static void printstats __P((const char *, unsigned long));
89static void startclock __P((void));
90static void stopclock __P((void));
91static void timer __P((int));
92static void tpacket __P((const char *, struct tftphdr *, int));
2b484d24
A
93static int cmpport __P((struct sockaddr *, struct sockaddr *));
94
95static void get_options(struct tftphdr *, int);
96
97static void
98get_options(struct tftphdr *ap, int size)
99{
100 unsigned long val;
101 char *opt, *endp, *nextopt, *valp;
102 int l;
103
104 size -= 2; /* skip over opcode */
105 opt = ap->th_stuff;
106 endp = opt + size - 1;
107 *endp = '\0';
108
109 while (opt < endp) {
110 l = strlen(opt) + 1;
111 valp = opt + l;
112 if (valp < endp) {
113 val = strtoul(valp, NULL, 10);
114 l = strlen(valp) + 1;
115 nextopt = valp + l;
116 if (val == ULONG_MAX && errno == ERANGE) {
117 /* Report illegal value */
118 opt = nextopt;
119 continue;
120 }
121 } else {
122 /* Badly formed OACK */
123 break;
124 }
125 if (strcmp(opt, "tsize") == 0) {
126 /* cool, but we'll ignore it */
127 } else if (strcmp(opt, "timeout") == 0) {
128 if (val >= 1 && val <= 255) {
129 rexmtval = val;
130 } else {
131 /* Report error? */
132 }
133 } else if (strcmp(opt, "blksize") == 0) {
134 if (val >= 8 && val <= MAXSEGSIZE) {
135 blksize = val;
136 } else {
137 /* Report error? */
138 }
139 } else {
140 /* unknown option */
141 }
142 opt = nextopt;
143 }
144}
b7080c8e
A
145
146/*
147 * Send the requested file.
148 */
149void
2b484d24 150sendfile(fd, name, mode)
b7080c8e
A
151 int fd;
152 char *name;
153 char *mode;
154{
2b484d24
A
155 struct tftphdr *ap; /* data and ack packets */
156 struct tftphdr *dp;
157 int n;
158 volatile unsigned int block;
159 volatile int size, convert;
b7080c8e 160 volatile unsigned long amount;
2b484d24
A
161 struct sockaddr_storage from;
162 struct stat sbuf;
163 off_t filesize=0;
b7080c8e
A
164 int fromlen;
165 FILE *file;
2b484d24
A
166 struct sockaddr_storage peer;
167 struct sockaddr_storage serv; /* valid server port number */
b7080c8e
A
168
169 startclock(); /* start stat's clock */
170 dp = r_init(); /* reset fillbuf/read-ahead code */
171 ap = (struct tftphdr *)ackbuf;
2b484d24
A
172 if (tsize) {
173 if (fstat(fd, &sbuf) == 0) {
174 filesize = sbuf.st_size;
175 } else {
176 filesize = -1ULL;
177 }
178 }
b7080c8e
A
179 file = fdopen(fd, "r");
180 convert = !strcmp(mode, "netascii");
181 block = 0;
182 amount = 0;
2b484d24
A
183 memcpy(&peer, &peeraddr, peeraddr.ss_len);
184 memset(&serv, 0, sizeof(serv));
b7080c8e
A
185
186 signal(SIGALRM, timer);
187 do {
188 if (block == 0)
2b484d24 189 size = makerequest(WRQ, name, dp, mode, filesize) - 4;
b7080c8e
A
190 else {
191 /* size = read(fd, dp->th_data, SEGSIZE); */
2b484d24 192 size = readit(file, &dp, blksize, convert);
b7080c8e 193 if (size < 0) {
2b484d24 194 nak(errno + 100, (struct sockaddr *)&peer);
b7080c8e
A
195 break;
196 }
197 dp->th_opcode = htons((u_short)DATA);
198 dp->th_block = htons((u_short)block);
199 }
200 timeout = 0;
201 (void) setjmp(timeoutbuf);
202send_data:
203 if (trace)
204 tpacket("sent", dp, size + 4);
205 n = sendto(f, dp, size + 4, 0,
2b484d24 206 (struct sockaddr *)&peer, peer.ss_len);
b7080c8e 207 if (n != size + 4) {
2b484d24 208 warn("sendto");
b7080c8e
A
209 goto abort;
210 }
2b484d24
A
211 if (block)
212 read_ahead(file, blksize, convert);
b7080c8e
A
213 for ( ; ; ) {
214 alarm(rexmtval);
215 do {
216 fromlen = sizeof(from);
217 n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
218 (struct sockaddr *)&from, &fromlen);
219 } while (n <= 0);
220 alarm(0);
221 if (n < 0) {
2b484d24 222 warn("recvfrom");
b7080c8e
A
223 goto abort;
224 }
2b484d24
A
225 if (!serv.ss_family)
226 serv = from;
227 else if (!cmpport((struct sockaddr *)&serv,
228 (struct sockaddr *)&from)) {
229 warn("server port mismatch");
230 goto abort;
231 }
232 peer = from;
b7080c8e
A
233 if (trace)
234 tpacket("received", ap, n);
235 /* should verify packet came from server */
236 ap->th_opcode = ntohs(ap->th_opcode);
237 ap->th_block = ntohs(ap->th_block);
238 if (ap->th_opcode == ERROR) {
239 printf("Error code %d: %s\n", ap->th_code,
240 ap->th_msg);
241 goto abort;
242 }
243 if (ap->th_opcode == ACK) {
244 int j;
245
2b484d24
A
246 if (ap->th_block == 0) {
247 /*
248 * If the extended options are enabled,
249 * the server just refused 'em all.
250 * The only one that _really_
251 * matters is blksize, but we'll
252 * clear timeout, too.
253 */
254 blksize = def_blksize;
255 rexmtval = def_rexmtval;
256 }
b7080c8e
A
257 if (ap->th_block == block) {
258 break;
259 }
260 /* On an error, try to synchronize
261 * both sides.
262 */
2b484d24 263 j = synchnet(f, blksize+4);
b7080c8e
A
264 if (j && trace) {
265 printf("discarded %d packets\n",
266 j);
267 }
268 if (ap->th_block == (block-1)) {
269 goto send_data;
270 }
271 }
2b484d24
A
272 if (ap->th_opcode == OACK) {
273 if (block == 0) {
274 blksize = def_blksize;
275 rexmtval = def_rexmtval;
276 get_options(ap, n);
277 break;
278 }
279 }
b7080c8e
A
280 }
281 if (block > 0)
282 amount += size;
283 block++;
2b484d24 284 } while (size == blksize || block == 1);
b7080c8e
A
285abort:
286 fclose(file);
287 stopclock();
288 if (amount > 0)
289 printstats("Sent", amount);
290}
291
292/*
293 * Receive a file.
294 */
295void
296recvfile(fd, name, mode)
297 int fd;
298 char *name;
299 char *mode;
300{
2b484d24
A
301 struct tftphdr *ap;
302 struct tftphdr *dp;
303 int n, oack=0;
304 volatile unsigned int block;
305 volatile int size, firsttrip;
b7080c8e 306 volatile unsigned long amount;
2b484d24
A
307 struct sockaddr_storage from;
308 int fromlen, readlen;
b7080c8e
A
309 FILE *file;
310 volatile int convert; /* true if converting crlf -> lf */
2b484d24
A
311 struct sockaddr_storage peer;
312 struct sockaddr_storage serv; /* valid server port number */
b7080c8e
A
313
314 startclock();
315 dp = w_init();
316 ap = (struct tftphdr *)ackbuf;
317 file = fdopen(fd, "w");
318 convert = !strcmp(mode, "netascii");
319 block = 1;
320 firsttrip = 1;
321 amount = 0;
2b484d24
A
322 memcpy(&peer, &peeraddr, peeraddr.ss_len);
323 memset(&serv, 0, sizeof(serv));
b7080c8e
A
324
325 signal(SIGALRM, timer);
326 do {
327 if (firsttrip) {
2b484d24
A
328 size = makerequest(RRQ, name, ap, mode, 0);
329 readlen = PKTSIZE;
b7080c8e
A
330 firsttrip = 0;
331 } else {
332 ap->th_opcode = htons((u_short)ACK);
333 ap->th_block = htons((u_short)(block));
2b484d24 334 readlen = blksize+4;
b7080c8e
A
335 size = 4;
336 block++;
337 }
338 timeout = 0;
339 (void) setjmp(timeoutbuf);
340send_ack:
341 if (trace)
342 tpacket("sent", ap, size);
2b484d24
A
343 if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peer,
344 peer.ss_len) != size) {
b7080c8e 345 alarm(0);
2b484d24 346 warn("sendto");
b7080c8e
A
347 goto abort;
348 }
349 write_behind(file, convert);
350 for ( ; ; ) {
351 alarm(rexmtval);
352 do {
353 fromlen = sizeof(from);
2b484d24 354 n = recvfrom(f, dp, readlen, 0,
b7080c8e
A
355 (struct sockaddr *)&from, &fromlen);
356 } while (n <= 0);
357 alarm(0);
358 if (n < 0) {
2b484d24 359 warn("recvfrom");
b7080c8e
A
360 goto abort;
361 }
2b484d24
A
362 if (!serv.ss_family)
363 serv = from;
364 else if (!cmpport((struct sockaddr *)&serv,
365 (struct sockaddr *)&from)) {
366 warn("server port mismatch");
367 goto abort;
368 }
369 peer = from;
b7080c8e
A
370 if (trace)
371 tpacket("received", dp, n);
372 /* should verify client address */
373 dp->th_opcode = ntohs(dp->th_opcode);
374 dp->th_block = ntohs(dp->th_block);
375 if (dp->th_opcode == ERROR) {
376 printf("Error code %d: %s\n", dp->th_code,
377 dp->th_msg);
378 goto abort;
379 }
380 if (dp->th_opcode == DATA) {
381 int j;
382
2b484d24
A
383 if (dp->th_block == 1 && !oack) {
384 /* no OACK, revert to defaults */
385 blksize = def_blksize;
386 rexmtval = def_rexmtval;
387 }
b7080c8e
A
388 if (dp->th_block == block) {
389 break; /* have next packet */
390 }
391 /* On an error, try to synchronize
392 * both sides.
393 */
2b484d24 394 j = synchnet(f, blksize);
b7080c8e
A
395 if (j && trace) {
396 printf("discarded %d packets\n", j);
397 }
398 if (dp->th_block == (block-1)) {
399 goto send_ack; /* resend ack */
400 }
401 }
2b484d24
A
402 if (dp->th_opcode == OACK) {
403 if (block == 1) {
404 oack = 1;
405 blksize = def_blksize;
406 rexmtval = def_rexmtval;
407 get_options(dp, n);
408 ap->th_opcode = htons(ACK);
409 ap->th_block = 0;
410 readlen = blksize+4;
411 size = 4;
412 goto send_ack;
413 }
414 }
b7080c8e
A
415 }
416 /* size = write(fd, dp->th_data, n - 4); */
417 size = writeit(file, &dp, n - 4, convert);
418 if (size < 0) {
2b484d24 419 nak(errno + 100, (struct sockaddr *)&peer);
b7080c8e
A
420 break;
421 }
422 amount += size;
2b484d24 423 } while (size == blksize || block == 1);
b7080c8e
A
424abort: /* ok to ack, since user */
425 ap->th_opcode = htons((u_short)ACK); /* has seen err msg */
426 ap->th_block = htons((u_short)block);
2b484d24
A
427 (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peer,
428 peer.ss_len);
b7080c8e
A
429 write_behind(file, convert); /* flush last buffer */
430 fclose(file);
431 stopclock();
432 if (amount > 0)
433 printstats("Received", amount);
434}
435
436static int
2b484d24 437makerequest(request, name, tp, mode, filesize)
b7080c8e
A
438 int request;
439 const char *name;
440 struct tftphdr *tp;
441 const char *mode;
2b484d24 442 off_t filesize;
b7080c8e 443{
2b484d24 444 char *cp;
b7080c8e
A
445
446 tp->th_opcode = htons((u_short)request);
2b484d24 447#ifndef __SVR4
b7080c8e 448 cp = tp->th_stuff;
2b484d24
A
449#else
450 cp = (void *)&tp->th_stuff;
451#endif
b7080c8e
A
452 strcpy(cp, name);
453 cp += strlen(name);
454 *cp++ = '\0';
455 strcpy(cp, mode);
456 cp += strlen(mode);
457 *cp++ = '\0';
2b484d24
A
458 if (tsize) {
459 strcpy(cp, "tsize");
460 cp += strlen(cp);
461 *cp++ = '\0';
462 sprintf(cp, "%lu", (unsigned long) filesize);
463 cp += strlen(cp);
464 *cp++ = '\0';
465 }
466 if (tout) {
467 strcpy(cp, "timeout");
468 cp += strlen(cp);
469 *cp++ = '\0';
470 sprintf(cp, "%d", rexmtval);
471 cp += strlen(cp);
472 *cp++ = '\0';
473 }
474 if (blksize != SEGSIZE) {
475 strcpy(cp, "blksize");
476 cp += strlen(cp);
477 *cp++ = '\0';
478 sprintf(cp, "%d", blksize);
479 cp += strlen(cp);
480 *cp++ = '\0';
481 }
b7080c8e
A
482 return (cp - (char *)tp);
483}
484
2b484d24 485const struct errmsg {
b7080c8e 486 int e_code;
2b484d24 487 const char *e_msg;
b7080c8e
A
488} errmsgs[] = {
489 { EUNDEF, "Undefined error code" },
490 { ENOTFOUND, "File not found" },
491 { EACCESS, "Access violation" },
492 { ENOSPACE, "Disk full or allocation exceeded" },
493 { EBADOP, "Illegal TFTP operation" },
494 { EBADID, "Unknown transfer ID" },
495 { EEXISTS, "File already exists" },
496 { ENOUSER, "No such user" },
2b484d24 497 { EOPTNEG, "Option negotiation failed" },
b7080c8e
A
498 { -1, 0 }
499};
500
501/*
502 * Send a nak packet (error message).
503 * Error code passed in is one of the
504 * standard TFTP codes, or a UNIX errno
505 * offset by 100.
506 */
507static void
2b484d24 508nak(error, peer)
b7080c8e 509 int error;
2b484d24 510 struct sockaddr *peer;
b7080c8e 511{
2b484d24
A
512 const struct errmsg *pe;
513 struct tftphdr *tp;
b7080c8e 514 int length;
2b484d24 515 size_t msglen;
b7080c8e
A
516
517 tp = (struct tftphdr *)ackbuf;
518 tp->th_opcode = htons((u_short)ERROR);
2b484d24 519 msglen = sizeof(ackbuf) - (&tp->th_msg[0] - ackbuf);
b7080c8e
A
520 for (pe = errmsgs; pe->e_code >= 0; pe++)
521 if (pe->e_code == error)
522 break;
523 if (pe->e_code < 0) {
b7080c8e 524 tp->th_code = EUNDEF;
2b484d24
A
525 strlcpy(tp->th_msg, strerror(error - 100), msglen);
526 } else {
527 tp->th_code = htons((u_short)error);
528 strlcpy(tp->th_msg, pe->e_msg, msglen);
b7080c8e 529 }
2b484d24
A
530 length = strlen(tp->th_msg);
531 msglen = &tp->th_msg[length + 1] - ackbuf;
b7080c8e 532 if (trace)
2b484d24
A
533 tpacket("sent", tp, (int)msglen);
534 if (sendto(f, ackbuf, msglen, 0, peer, peer->sa_len) != msglen)
535 warn("nak");
b7080c8e
A
536}
537
538static void
539tpacket(s, tp, n)
540 const char *s;
541 struct tftphdr *tp;
542 int n;
543{
544 static char *opcodes[] =
2b484d24
A
545 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
546 char *cp, *file, *endp, *opt, *spc;
b7080c8e 547 u_short op = ntohs(tp->th_opcode);
2b484d24 548 int i, o;
b7080c8e 549
2b484d24 550 if (op < RRQ || op > OACK)
b7080c8e
A
551 printf("%s opcode=%x ", s, op);
552 else
553 printf("%s %s ", s, opcodes[op]);
554 switch (op) {
555
556 case RRQ:
557 case WRQ:
558 n -= 2;
2b484d24
A
559#ifndef __SVR4
560 cp = tp->th_stuff;
561#else
562 cp = (void *) &tp->th_stuff;
563#endif
564 endp = cp + n - 1;
565 if (*endp != '\0') { /* Shouldn't happen, but... */
566 *endp = '\0';
567 }
568 file = cp;
569 cp = strchr(cp, '\0') + 1;
570 printf("<file=%s, mode=%s", file, cp);
571 cp = strchr(cp, '\0') + 1;
572 o = 0;
573 while (cp < endp) {
574 i = strlen(cp) + 1;
575 if (o) {
576 printf(", %s=%s", opt, cp);
577 } else {
578 opt = cp;
579 }
580 o = (o+1) % 2;
581 cp += i;
582 }
583 printf(">\n");
b7080c8e
A
584 break;
585
586 case DATA:
587 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
588 break;
589
590 case ACK:
591 printf("<block=%d>\n", ntohs(tp->th_block));
592 break;
593
594 case ERROR:
595 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
596 break;
2b484d24
A
597
598 case OACK:
599 o = 0;
600 n -= 2;
601 cp = tp->th_stuff;
602 endp = cp + n - 1;
603 if (*endp != '\0') { /* Shouldn't happen, but... */
604 *endp = '\0';
605 }
606 printf("<");
607 spc = "";
608 while (cp < endp) {
609 i = strlen(cp) + 1;
610 if (o) {
611 printf("%s%s=%s", spc, opt, cp);
612 spc = ", ";
613 } else {
614 opt = cp;
615 }
616 o = (o+1) % 2;
617 cp += i;
618 }
619 printf(">\n");
620 break;
b7080c8e
A
621 }
622}
623
624struct timeval tstart;
625struct timeval tstop;
626
627static void
628startclock()
629{
630
631 (void)gettimeofday(&tstart, NULL);
632}
633
634static void
635stopclock()
636{
637
638 (void)gettimeofday(&tstop, NULL);
639}
640
641static void
642printstats(direction, amount)
643 const char *direction;
644 unsigned long amount;
645{
646 double delta;
2b484d24
A
647
648 /* compute delta in 1/10's second units */
b7080c8e
A
649 delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
650 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
651 delta = delta/10.; /* back to seconds */
2b484d24 652 printf("%s %ld bytes in %.1f seconds", direction, amount, delta);
b7080c8e
A
653 if (verbose)
654 printf(" [%.0f bits/sec]", (amount*8.)/delta);
655 putchar('\n');
656}
657
658static void
659timer(sig)
660 int sig;
661{
662
663 timeout += rexmtval;
664 if (timeout >= maxtimeout) {
665 printf("Transfer timed out.\n");
666 longjmp(toplevel, -1);
667 }
668 longjmp(timeoutbuf, 1);
669}
2b484d24
A
670
671static int
672cmpport(sa, sb)
673 struct sockaddr *sa;
674 struct sockaddr *sb;
675{
676 char a[NI_MAXSERV], b[NI_MAXSERV];
677
678 if (getnameinfo(sa, sa->sa_len, NULL, 0, a, sizeof(a), NI_NUMERICSERV))
679 return 0;
680 if (getnameinfo(sb, sb->sa_len, NULL, 0, b, sizeof(b), NI_NUMERICSERV))
681 return 0;
682 if (strcmp(a, b) != 0)
683 return 0;
684
685 return 1;
686}