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