tftp.c revision 332138
1/* $NetBSD: tftp.c,v 1.4 1997/09/17 16:57:07 drochner Exp $ */ 2 3/* 4 * Copyright (c) 1996 5 * Matthias Drochner. 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 for the NetBSD Project 18 * by Matthias Drochner. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34#include <sys/cdefs.h> 35__FBSDID("$FreeBSD: stable/11/stand/libsa/tftp.c 332138 2018-04-06 19:21:36Z kevans $"); 36 37/* 38 * Simple TFTP implementation for libsa. 39 * Assumes: 40 * - socket descriptor (int) at open_file->f_devdata 41 * - server host IP in global servip 42 * Restrictions: 43 * - read only 44 * - lseek only with SEEK_SET or SEEK_CUR 45 * - no big time differences between transfers (<tftp timeout) 46 */ 47 48#include <sys/types.h> 49#include <sys/stat.h> 50#include <netinet/in.h> 51#include <netinet/udp.h> 52#include <netinet/in_systm.h> 53#include <arpa/tftp.h> 54 55#include <string.h> 56 57#include "stand.h" 58#include "net.h" 59#include "netif.h" 60 61#include "tftp.h" 62 63struct tftp_handle; 64struct tftprecv_extra; 65 66static ssize_t recvtftp(struct iodesc *d, void **pkt, void **payload, 67 time_t tleft, void *recv_extra); 68static int tftp_open(const char *path, struct open_file *f); 69static int tftp_close(struct open_file *f); 70static int tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len); 71static int tftp_read(struct open_file *f, void *buf, size_t size, size_t *resid); 72static int tftp_write(struct open_file *f, const void *buf, size_t size, 73 size_t *resid); 74static off_t tftp_seek(struct open_file *f, off_t offset, int where); 75static int tftp_set_blksize(struct tftp_handle *h, const char *str); 76static int tftp_stat(struct open_file *f, struct stat *sb); 77 78struct fs_ops tftp_fsops = { 79 "tftp", 80 tftp_open, 81 tftp_close, 82 tftp_read, 83 tftp_write, 84 tftp_seek, 85 tftp_stat, 86 null_readdir 87}; 88 89extern struct in_addr servip; 90 91static int tftpport = 2000; 92static int is_open = 0; 93 94/* 95 * The legacy TFTP_BLKSIZE value was SEGSIZE(512). 96 * TFTP_REQUESTED_BLKSIZE of 1428 is (Ethernet MTU, less the TFTP, UDP and 97 * IP header lengths). 98 */ 99#define TFTP_REQUESTED_BLKSIZE 1428 100 101/* 102 * Choose a blksize big enough so we can test with Ethernet 103 * Jumbo frames in the future. 104 */ 105#define TFTP_MAX_BLKSIZE 9008 106 107struct tftp_handle { 108 struct iodesc *iodesc; 109 int currblock; /* contents of lastdata */ 110 int islastblock; /* flag */ 111 int validsize; 112 int off; 113 char *path; /* saved for re-requests */ 114 unsigned int tftp_blksize; 115 unsigned long tftp_tsize; 116 void *pkt; 117 struct tftphdr *tftp_hdr; 118}; 119 120struct tftprecv_extra { 121 struct tftp_handle *tftp_handle; 122 unsigned short rtype; /* Received type */ 123}; 124 125#define TFTP_MAX_ERRCODE EOPTNEG 126static const int tftperrors[TFTP_MAX_ERRCODE + 1] = { 127 0, /* ??? */ 128 ENOENT, 129 EPERM, 130 ENOSPC, 131 EINVAL, /* ??? */ 132 EINVAL, /* ??? */ 133 EEXIST, 134 EINVAL, /* ??? */ 135 EINVAL, /* Option negotiation failed. */ 136}; 137 138static int tftp_getnextblock(struct tftp_handle *h); 139 140/* send error message back. */ 141static void 142tftp_senderr(struct tftp_handle *h, u_short errcode, const char *msg) 143{ 144 struct { 145 u_char header[HEADER_SIZE]; 146 struct tftphdr t; 147 u_char space[63]; /* +1 from t */ 148 } __packed __aligned(4) wbuf; 149 char *wtail; 150 int len; 151 152 len = strlen(msg); 153 if (len > sizeof(wbuf.space)) 154 len = sizeof(wbuf.space); 155 156 wbuf.t.th_opcode = htons((u_short) ERROR); 157 wbuf.t.th_code = htons(errcode); 158 159 wtail = wbuf.t.th_msg; 160 bcopy(msg, wtail, len); 161 wtail[len] = '\0'; 162 wtail += len + 1; 163 164 sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t); 165} 166 167static void 168tftp_sendack(struct tftp_handle *h) 169{ 170 struct { 171 u_char header[HEADER_SIZE]; 172 struct tftphdr t; 173 } __packed __aligned(4) wbuf; 174 char *wtail; 175 176 wbuf.t.th_opcode = htons((u_short) ACK); 177 wtail = (char *) &wbuf.t.th_block; 178 wbuf.t.th_block = htons((u_short) h->currblock); 179 wtail += 2; 180 181 sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t); 182} 183 184static ssize_t 185recvtftp(struct iodesc *d, void **pkt, void **payload, time_t tleft, 186 void *recv_extra) 187{ 188 struct tftprecv_extra *extra; 189 struct tftp_handle *h; 190 struct tftphdr *t; 191 unsigned short *rtype; 192 void *ptr = NULL; 193 ssize_t len; 194 195 errno = 0; 196 extra = (struct tftprecv_extra *)recv_extra; 197 h = extra->tftp_handle; 198 199 len = readudp(d, &ptr, (void **)&t, tleft); 200 201 if (len < 4) { 202 free(ptr); 203 return (-1); 204 } 205 206 extra->rtype = ntohs(t->th_opcode); 207 switch (ntohs(t->th_opcode)) { 208 case DATA: { 209 int got; 210 211 if (htons(t->th_block) != (u_short) d->xid) { 212 /* 213 * Expected block? 214 */ 215 free(ptr); 216 return (-1); 217 } 218 if (d->xid == 1) { 219 /* 220 * First data packet from new port. 221 */ 222 struct udphdr *uh; 223 uh = (struct udphdr *) t - 1; 224 d->destport = uh->uh_sport; 225 } /* else check uh_sport has not changed??? */ 226 got = len - (t->th_data - (char *)t); 227 *pkt = ptr; 228 *payload = t; 229 return (got); 230 } 231 case ERROR: 232 if ((unsigned) ntohs(t->th_code) > TFTP_MAX_ERRCODE) { 233 printf("illegal tftp error %d\n", ntohs(t->th_code)); 234 errno = EIO; 235 } else { 236#ifdef TFTP_DEBUG 237 printf("tftp-error %d\n", ntohs(t->th_code)); 238#endif 239 errno = tftperrors[ntohs(t->th_code)]; 240 } 241 free(ptr); 242 return (-1); 243 case OACK: { 244 struct udphdr *uh; 245 int tftp_oack_len; 246 247 /* 248 * Unexpected OACK. TFTP transfer already in progress. 249 * Drop the pkt. 250 */ 251 if (d->xid != 1) { 252 free(ptr); 253 return (-1); 254 } 255 256 /* 257 * Remember which port this OACK came from, because we need 258 * to send the ACK or errors back to it. 259 */ 260 uh = (struct udphdr *) t - 1; 261 d->destport = uh->uh_sport; 262 263 /* Parse options ACK-ed by the server. */ 264 tftp_oack_len = len - sizeof(t->th_opcode); 265 if (tftp_parse_oack(h, t->th_u.tu_stuff, tftp_oack_len) != 0) { 266 tftp_senderr(h, EOPTNEG, "Malformed OACK"); 267 errno = EIO; 268 free(ptr); 269 return (-1); 270 } 271 *pkt = ptr; 272 *payload = t; 273 return (0); 274 } 275 default: 276#ifdef TFTP_DEBUG 277 printf("tftp type %d not handled\n", ntohs(t->th_opcode)); 278#endif 279 free(ptr); 280 return (-1); 281 } 282} 283 284/* send request, expect first block (or error) */ 285static int 286tftp_makereq(struct tftp_handle *h) 287{ 288 struct { 289 u_char header[HEADER_SIZE]; 290 struct tftphdr t; 291 u_char space[FNAME_SIZE + 6]; 292 } __packed __aligned(4) wbuf; 293 struct tftprecv_extra recv_extra; 294 char *wtail; 295 int l; 296 ssize_t res; 297 void *pkt; 298 struct tftphdr *t; 299 char *tftp_blksize = NULL; 300 int blksize_l; 301 302 /* 303 * Allow overriding default TFTP block size by setting 304 * a tftp.blksize environment variable. 305 */ 306 if ((tftp_blksize = getenv("tftp.blksize")) != NULL) { 307 tftp_set_blksize(h, tftp_blksize); 308 } 309 310 wbuf.t.th_opcode = htons((u_short) RRQ); 311 wtail = wbuf.t.th_stuff; 312 l = strlen(h->path); 313#ifdef TFTP_PREPEND_PATH 314 if (l > FNAME_SIZE - (sizeof(TFTP_PREPEND_PATH) - 1)) 315 return (ENAMETOOLONG); 316 bcopy(TFTP_PREPEND_PATH, wtail, sizeof(TFTP_PREPEND_PATH) - 1); 317 wtail += sizeof(TFTP_PREPEND_PATH) - 1; 318#else 319 if (l > FNAME_SIZE) 320 return (ENAMETOOLONG); 321#endif 322 bcopy(h->path, wtail, l + 1); 323 wtail += l + 1; 324 bcopy("octet", wtail, 6); 325 wtail += 6; 326 bcopy("blksize", wtail, 8); 327 wtail += 8; 328 blksize_l = sprintf(wtail, "%d", h->tftp_blksize); 329 wtail += blksize_l + 1; 330 bcopy("tsize", wtail, 6); 331 wtail += 6; 332 bcopy("0", wtail, 2); 333 wtail += 2; 334 335 /* h->iodesc->myport = htons(--tftpport); */ 336 h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff)); 337 h->iodesc->destport = htons(IPPORT_TFTP); 338 h->iodesc->xid = 1; /* expected block */ 339 340 h->currblock = 0; 341 h->islastblock = 0; 342 h->validsize = 0; 343 344 pkt = NULL; 345 recv_extra.tftp_handle = h; 346 res = sendrecv(h->iodesc, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t, 347 (void *)&recvtftp, &pkt, (void **)&t, &recv_extra); 348 if (res == -1) { 349 free(pkt); 350 return (errno); 351 } 352 353 free(h->pkt); 354 h->pkt = pkt; 355 h->tftp_hdr = t; 356 357 if (recv_extra.rtype == OACK) 358 return (tftp_getnextblock(h)); 359 360 /* Server ignored our blksize request, revert to TFTP default. */ 361 h->tftp_blksize = SEGSIZE; 362 363 switch (recv_extra.rtype) { 364 case DATA: { 365 h->currblock = 1; 366 h->validsize = res; 367 h->islastblock = 0; 368 if (res < h->tftp_blksize) { 369 h->islastblock = 1; /* very short file */ 370 tftp_sendack(h); 371 } 372 return (0); 373 } 374 case ERROR: 375 default: 376 return (errno); 377 } 378 379} 380 381/* ack block, expect next */ 382static int 383tftp_getnextblock(struct tftp_handle *h) 384{ 385 struct { 386 u_char header[HEADER_SIZE]; 387 struct tftphdr t; 388 } __packed __aligned(4) wbuf; 389 struct tftprecv_extra recv_extra; 390 char *wtail; 391 int res; 392 void *pkt; 393 struct tftphdr *t; 394 wbuf.t.th_opcode = htons((u_short) ACK); 395 wtail = (char *) &wbuf.t.th_block; 396 wbuf.t.th_block = htons((u_short) h->currblock); 397 wtail += 2; 398 399 h->iodesc->xid = h->currblock + 1; /* expected block */ 400 401 pkt = NULL; 402 recv_extra.tftp_handle = h; 403 res = sendrecv(h->iodesc, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t, 404 (void *)&recvtftp, &pkt, (void **)&t, &recv_extra); 405 406 if (res == -1) { /* 0 is OK! */ 407 free(pkt); 408 return (errno); 409 } 410 411 free(h->pkt); 412 h->pkt = pkt; 413 h->tftp_hdr = t; 414 h->currblock++; 415 h->validsize = res; 416 if (res < h->tftp_blksize) 417 h->islastblock = 1; /* EOF */ 418 419 if (h->islastblock == 1) { 420 /* Send an ACK for the last block */ 421 wbuf.t.th_block = htons((u_short) h->currblock); 422 sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t); 423 } 424 425 return (0); 426} 427 428static int 429tftp_open(const char *path, struct open_file *f) 430{ 431 struct tftp_handle *tftpfile; 432 struct iodesc *io; 433 int res; 434 size_t pathsize; 435 const char *extraslash; 436 437 if (netproto != NET_TFTP) 438 return (EINVAL); 439 440 if (f->f_dev->dv_type != DEVT_NET) 441 return (EINVAL); 442 443 if (is_open) 444 return (EBUSY); 445 446 tftpfile = (struct tftp_handle *) malloc(sizeof(*tftpfile)); 447 if (!tftpfile) 448 return (ENOMEM); 449 450 memset(tftpfile, 0, sizeof(*tftpfile)); 451 tftpfile->tftp_blksize = TFTP_REQUESTED_BLKSIZE; 452 tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata)); 453 if (io == NULL) 454 return (EINVAL); 455 456 io->destip = servip; 457 tftpfile->off = 0; 458 pathsize = (strlen(rootpath) + 1 + strlen(path) + 1) * sizeof(char); 459 tftpfile->path = malloc(pathsize); 460 if (tftpfile->path == NULL) { 461 free(tftpfile); 462 return(ENOMEM); 463 } 464 if (rootpath[strlen(rootpath) - 1] == '/' || path[0] == '/') 465 extraslash = ""; 466 else 467 extraslash = "/"; 468 res = snprintf(tftpfile->path, pathsize, "%s%s%s", 469 rootpath, extraslash, path); 470 if (res < 0 || res > pathsize) { 471 free(tftpfile->path); 472 free(tftpfile); 473 return(ENOMEM); 474 } 475 476 res = tftp_makereq(tftpfile); 477 478 if (res) { 479 free(tftpfile->path); 480 free(tftpfile->pkt); 481 free(tftpfile); 482 return (res); 483 } 484 f->f_fsdata = (void *) tftpfile; 485 is_open = 1; 486 return (0); 487} 488 489static int 490tftp_read(struct open_file *f, void *addr, size_t size, 491 size_t *resid /* out */) 492{ 493 struct tftp_handle *tftpfile; 494 tftpfile = (struct tftp_handle *) f->f_fsdata; 495 496 while (size > 0) { 497 int needblock, count; 498 499 twiddle(32); 500 501 needblock = tftpfile->off / tftpfile->tftp_blksize + 1; 502 503 if (tftpfile->currblock > needblock) { /* seek backwards */ 504 tftp_senderr(tftpfile, 0, "No error: read aborted"); 505 tftp_makereq(tftpfile); /* no error check, it worked 506 * for open */ 507 } 508 509 while (tftpfile->currblock < needblock) { 510 int res; 511 512 res = tftp_getnextblock(tftpfile); 513 if (res) { /* no answer */ 514#ifdef TFTP_DEBUG 515 printf("tftp: read error\n"); 516#endif 517 return (res); 518 } 519 if (tftpfile->islastblock) 520 break; 521 } 522 523 if (tftpfile->currblock == needblock) { 524 int offinblock, inbuffer; 525 526 offinblock = tftpfile->off % tftpfile->tftp_blksize; 527 528 inbuffer = tftpfile->validsize - offinblock; 529 if (inbuffer < 0) { 530#ifdef TFTP_DEBUG 531 printf("tftp: invalid offset %d\n", 532 tftpfile->off); 533#endif 534 return (EINVAL); 535 } 536 count = (size < inbuffer ? size : inbuffer); 537 bcopy(tftpfile->tftp_hdr->th_data + offinblock, 538 addr, count); 539 540 addr = (char *)addr + count; 541 tftpfile->off += count; 542 size -= count; 543 544 if ((tftpfile->islastblock) && (count == inbuffer)) 545 break; /* EOF */ 546 } else { 547#ifdef TFTP_DEBUG 548 printf("tftp: block %d not found\n", needblock); 549#endif 550 return (EINVAL); 551 } 552 553 } 554 555 if (resid) 556 *resid = size; 557 return (0); 558} 559 560static int 561tftp_close(struct open_file *f) 562{ 563 struct tftp_handle *tftpfile; 564 tftpfile = (struct tftp_handle *) f->f_fsdata; 565 566 /* let it time out ... */ 567 568 if (tftpfile) { 569 free(tftpfile->path); 570 free(tftpfile->pkt); 571 free(tftpfile); 572 } 573 is_open = 0; 574 return (0); 575} 576 577static int 578tftp_write(struct open_file *f __unused, const void *start __unused, 579 size_t size __unused, size_t *resid __unused /* out */) 580{ 581 return (EROFS); 582} 583 584static int 585tftp_stat(struct open_file *f, struct stat *sb) 586{ 587 struct tftp_handle *tftpfile; 588 tftpfile = (struct tftp_handle *) f->f_fsdata; 589 590 sb->st_mode = 0444 | S_IFREG; 591 sb->st_nlink = 1; 592 sb->st_uid = 0; 593 sb->st_gid = 0; 594 sb->st_size = (off_t) tftpfile->tftp_tsize; 595 return (0); 596} 597 598static off_t 599tftp_seek(struct open_file *f, off_t offset, int where) 600{ 601 struct tftp_handle *tftpfile; 602 tftpfile = (struct tftp_handle *) f->f_fsdata; 603 604 switch (where) { 605 case SEEK_SET: 606 tftpfile->off = offset; 607 break; 608 case SEEK_CUR: 609 tftpfile->off += offset; 610 break; 611 default: 612 errno = EOFFSET; 613 return (-1); 614 } 615 return (tftpfile->off); 616} 617 618static int 619tftp_set_blksize(struct tftp_handle *h, const char *str) 620{ 621 char *endptr; 622 int new_blksize; 623 int ret = 0; 624 625 if (h == NULL || str == NULL) 626 return (ret); 627 628 new_blksize = 629 (unsigned int)strtol(str, &endptr, 0); 630 631 /* 632 * Only accept blksize value if it is numeric. 633 * RFC2348 specifies that acceptable values are 8-65464. 634 * Let's choose a limit less than MAXRSPACE. 635 */ 636 if (*endptr == '\0' && new_blksize >= 8 637 && new_blksize <= TFTP_MAX_BLKSIZE) { 638 h->tftp_blksize = new_blksize; 639 ret = 1; 640 } 641 642 return (ret); 643} 644 645/* 646 * In RFC2347, the TFTP Option Acknowledgement package (OACK) 647 * is used to acknowledge a client's option negotiation request. 648 * The format of an OACK packet is: 649 * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ 650 * | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | 651 * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ 652 * 653 * opc 654 * The opcode field contains a 6, for Option Acknowledgment. 655 * 656 * opt1 657 * The first option acknowledgment, copied from the original 658 * request. 659 * 660 * value1 661 * The acknowledged value associated with the first option. If 662 * and how this value may differ from the original request is 663 * detailed in the specification for the option. 664 * 665 * optN, valueN 666 * The final option/value acknowledgment pair. 667 */ 668static int 669tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len) 670{ 671 /* 672 * We parse the OACK strings into an array 673 * of name-value pairs. 674 */ 675 char *tftp_options[128] = { 0 }; 676 char *val = buf; 677 int i = 0; 678 int option_idx = 0; 679 int blksize_is_set = 0; 680 int tsize = 0; 681 682 unsigned int orig_blksize; 683 684 while (option_idx < 128 && i < len) { 685 if (buf[i] == '\0') { 686 if (&buf[i] > val) { 687 tftp_options[option_idx] = val; 688 val = &buf[i] + 1; 689 ++option_idx; 690 } 691 } 692 ++i; 693 } 694 695 /* Save the block size we requested for sanity check later. */ 696 orig_blksize = h->tftp_blksize; 697 698 /* 699 * Parse individual TFTP options. 700 * * "blksize" is specified in RFC2348. 701 * * "tsize" is specified in RFC2349. 702 */ 703 for (i = 0; i < option_idx; i += 2) { 704 if (strcasecmp(tftp_options[i], "blksize") == 0) { 705 if (i + 1 < option_idx) 706 blksize_is_set = 707 tftp_set_blksize(h, tftp_options[i + 1]); 708 } else if (strcasecmp(tftp_options[i], "tsize") == 0) { 709 if (i + 1 < option_idx) 710 tsize = strtol(tftp_options[i + 1], (char **)NULL, 10); 711 if (tsize != 0) 712 h->tftp_tsize = tsize; 713 } else { 714 /* Do not allow any options we did not expect to be ACKed. */ 715 printf("unexpected tftp option '%s'\n", tftp_options[i]); 716 return (-1); 717 } 718 } 719 720 if (!blksize_is_set) { 721 /* 722 * If TFTP blksize was not set, try defaulting 723 * to the legacy TFTP blksize of SEGSIZE(512) 724 */ 725 h->tftp_blksize = SEGSIZE; 726 } else if (h->tftp_blksize > orig_blksize) { 727 /* 728 * Server should not be proposing block sizes that 729 * exceed what we said we can handle. 730 */ 731 printf("unexpected blksize %u\n", h->tftp_blksize); 732 return (-1); 733 } 734 735#ifdef TFTP_DEBUG 736 printf("tftp_blksize: %u\n", h->tftp_blksize); 737 printf("tftp_tsize: %lu\n", h->tftp_tsize); 738#endif 739 return 0; 740} 741