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