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 /* 35 * Simple TFTP implementation for libsa. 36 * Assumes: 37 * - socket descriptor (int) at dev->d_opendata, dev stored at 38 * open_file->f_devdata 39 * - server host IP in global rootip 40 * Restrictions: 41 * - read only 42 * - lseek only with SEEK_SET or SEEK_CUR 43 * - no big time differences between transfers (<tftp timeout) 44 */ 45 46 #include <sys/types.h> 47 #include <sys/stat.h> 48 #include <netinet/in.h> 49 #include <netinet/udp.h> 50 #include <netinet/in_systm.h> 51 #include <arpa/tftp.h> 52 53 #include <string.h> 54 55 #include "stand.h" 56 #include "net.h" 57 #include "netif.h" 58 59 #include "tftp.h" 60 61 struct tftp_handle; 62 struct tftprecv_extra; 63 64 static ssize_t recvtftp(struct iodesc *, void **, void **, time_t, void *); 65 static int tftp_open(const char *, struct open_file *); 66 static int tftp_close(struct open_file *); 67 static int tftp_parse_oack(struct tftp_handle *, char *, size_t); 68 static int tftp_read(struct open_file *, void *, size_t, size_t *); 69 static off_t tftp_seek(struct open_file *, off_t, int); 70 static int tftp_set_blksize(struct tftp_handle *, const char *); 71 static int tftp_stat(struct open_file *, struct stat *); 72 static int tftp_preload(struct open_file *); 73 74 struct fs_ops tftp_fsops = { 75 .fs_name = "tftp", 76 .fo_open = tftp_open, 77 .fo_close = tftp_close, 78 .fo_read = tftp_read, 79 .fo_write = null_write, 80 .fo_seek = tftp_seek, 81 .fo_stat = tftp_stat, 82 .fo_preload = tftp_preload, 83 .fo_readdir = null_readdir 84 }; 85 86 static int tftpport = 2000; 87 static int is_open = 0; 88 89 /* 90 * The legacy TFTP_BLKSIZE value was SEGSIZE(512). 91 * TFTP_REQUESTED_BLKSIZE of 1428 is (Ethernet MTU, less the TFTP, UDP and 92 * IP header lengths). 93 */ 94 #define TFTP_REQUESTED_BLKSIZE 1428 95 96 /* 97 * Choose a blksize big enough so we can test with Ethernet 98 * Jumbo frames in the future. 99 */ 100 #define TFTP_MAX_BLKSIZE 9008 101 #define TFTP_TRIES 2 102 103 struct tftp_handle { 104 struct iodesc *iodesc; 105 int currblock; /* contents of lastdata */ 106 unsigned int islastblock:1; /* flag */ 107 unsigned int tries:4; /* number of read attempts */ 108 int validsize; 109 int off; 110 char *path; /* saved for re-requests */ 111 unsigned int tftp_blksize; 112 unsigned long tftp_tsize; 113 void *pkt; 114 struct tftphdr *tftp_hdr; 115 char *tftp_cache; 116 bool lastacksent; 117 }; 118 119 struct tftprecv_extra { 120 struct tftp_handle *tftp_handle; 121 unsigned short rtype; /* Received type */ 122 }; 123 124 #define TFTP_MAX_ERRCODE EOPTNEG 125 static const int tftperrors[TFTP_MAX_ERRCODE + 1] = { 126 0, /* NAK */ 127 ENOENT, 128 EPERM, 129 ENOSPC, 130 EINVAL, /* ??? */ 131 EINVAL, /* ??? */ 132 EEXIST, 133 EINVAL, /* ??? */ 134 EINVAL, /* Option negotiation failed. */ 135 }; 136 137 static int tftp_getnextblock(struct tftp_handle *h); 138 139 /* send error message back. */ 140 static void 141 tftp_senderr(struct tftp_handle *h, u_short errcode, const char *msg) 142 { 143 struct { 144 u_char header[HEADER_SIZE]; 145 struct tftphdr t; 146 u_char space[63]; /* +1 from t */ 147 } __packed __aligned(4) wbuf; 148 char *wtail; 149 int len; 150 151 len = strlen(msg); 152 if (len > sizeof(wbuf.space)) 153 len = sizeof(wbuf.space); 154 155 wbuf.t.th_opcode = htons((u_short)ERROR); 156 wbuf.t.th_code = htons(errcode); 157 158 wtail = wbuf.t.th_msg; 159 bcopy(msg, wtail, len); 160 wtail[len] = '\0'; 161 wtail += len + 1; 162 163 sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t); 164 } 165 166 static void 167 tftp_sendack(struct tftp_handle *h, u_short block) 168 { 169 struct { 170 u_char header[HEADER_SIZE]; 171 struct tftphdr t; 172 } __packed __aligned(4) wbuf; 173 char *wtail; 174 175 wbuf.t.th_opcode = htons((u_short)ACK); 176 wtail = (char *)&wbuf.t.th_block; 177 wbuf.t.th_block = htons(block); 178 wtail += 2; 179 180 sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t); 181 } 182 183 static ssize_t 184 recvtftp(struct iodesc *d, void **pkt, void **payload, time_t tleft, 185 void *recv_extra) 186 { 187 struct tftprecv_extra *extra; 188 struct tftp_handle *h; 189 struct tftphdr *t; 190 void *ptr = NULL; 191 ssize_t len; 192 int tftp_error; 193 194 errno = 0; 195 extra = recv_extra; 196 h = extra->tftp_handle; 197 198 len = readudp(d, &ptr, (void **)&t, tleft); 199 200 if (len < 4) { 201 free(ptr); 202 return (-1); 203 } 204 205 extra->rtype = ntohs(t->th_opcode); 206 switch (ntohs(t->th_opcode)) { 207 case DATA: { 208 int got; 209 210 if (htons(t->th_block) < (u_short)d->xid) { 211 /* 212 * Apparently our ACK was missed, re-send. 213 */ 214 tftp_sendack(h, htons(t->th_block)); 215 free(ptr); 216 return (-1); 217 } 218 if (htons(t->th_block) != (u_short)d->xid) { 219 /* 220 * Packet from the future, drop this. 221 */ 222 free(ptr); 223 return (-1); 224 } 225 if (d->xid == 1) { 226 /* 227 * First data packet from new port. 228 */ 229 struct udphdr *uh; 230 uh = (struct udphdr *)t - 1; 231 d->destport = uh->uh_sport; 232 } 233 got = len - (t->th_data - (char *)t); 234 *pkt = ptr; 235 *payload = t; 236 return (got); 237 } 238 case ERROR: 239 tftp_error = ntohs(t->th_code); 240 if ((unsigned)tftp_error > TFTP_MAX_ERRCODE) { 241 printf("illegal tftp error %d\n", tftp_error); 242 errno = EIO; 243 } else { 244 #ifdef TFTP_DEBUG 245 printf("tftp-error %d\n", tftp_error); 246 #endif 247 errno = tftperrors[tftp_error]; 248 } 249 free(ptr); 250 /* If we got a NAK return 0, it's usually a directory */ 251 if (tftp_error == 0) 252 return (0); 253 return (-1); 254 case OACK: { 255 struct udphdr *uh; 256 int tftp_oack_len; 257 258 /* 259 * Unexpected OACK. TFTP transfer already in progress. 260 * Drop the pkt. 261 */ 262 if (d->xid != 1) { 263 free(ptr); 264 return (-1); 265 } 266 267 /* 268 * Remember which port this OACK came from, because we need 269 * to send the ACK or errors back to it. 270 */ 271 uh = (struct udphdr *)t - 1; 272 d->destport = uh->uh_sport; 273 274 /* Parse options ACK-ed by the server. */ 275 tftp_oack_len = len - sizeof(t->th_opcode); 276 if (tftp_parse_oack(h, t->th_u.tu_stuff, tftp_oack_len) != 0) { 277 tftp_senderr(h, EOPTNEG, "Malformed OACK"); 278 errno = EIO; 279 free(ptr); 280 return (-1); 281 } 282 *pkt = ptr; 283 *payload = t; 284 return (0); 285 } 286 default: 287 #ifdef TFTP_DEBUG 288 printf("tftp type %d not handled\n", ntohs(t->th_opcode)); 289 #endif 290 free(ptr); 291 return (-1); 292 } 293 } 294 295 /* send request, expect first block (or error) */ 296 static int 297 tftp_makereq(struct tftp_handle *h) 298 { 299 struct { 300 u_char header[HEADER_SIZE]; 301 struct tftphdr t; 302 u_char space[FNAME_SIZE + 6]; 303 } __packed __aligned(4) wbuf; 304 struct tftprecv_extra recv_extra; 305 char *wtail; 306 int l; 307 ssize_t res; 308 void *pkt; 309 struct tftphdr *t; 310 char *tftp_blksize = NULL; 311 int blksize_l; 312 313 /* 314 * Allow overriding default TFTP block size by setting 315 * a tftp.blksize environment variable. 316 */ 317 if ((tftp_blksize = getenv("tftp.blksize")) != NULL) { 318 tftp_set_blksize(h, tftp_blksize); 319 } 320 321 wbuf.t.th_opcode = htons((u_short)RRQ); 322 wtail = wbuf.t.th_stuff; 323 l = strlen(h->path); 324 #ifdef TFTP_PREPEND_PATH 325 if (l > FNAME_SIZE - (sizeof(TFTP_PREPEND_PATH) - 1)) 326 return (ENAMETOOLONG); 327 bcopy(TFTP_PREPEND_PATH, wtail, sizeof(TFTP_PREPEND_PATH) - 1); 328 wtail += sizeof(TFTP_PREPEND_PATH) - 1; 329 #else 330 if (l > FNAME_SIZE) 331 return (ENAMETOOLONG); 332 #endif 333 bcopy(h->path, wtail, l + 1); 334 wtail += l + 1; 335 bcopy("octet", wtail, 6); 336 wtail += 6; 337 bcopy("blksize", wtail, 8); 338 wtail += 8; 339 blksize_l = sprintf(wtail, "%d", h->tftp_blksize); 340 wtail += blksize_l + 1; 341 bcopy("tsize", wtail, 6); 342 wtail += 6; 343 bcopy("0", wtail, 2); 344 wtail += 2; 345 346 h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff)); 347 h->iodesc->destport = htons(IPPORT_TFTP); 348 h->iodesc->xid = 1; /* expected block */ 349 350 h->currblock = 0; 351 h->islastblock = 0; 352 h->validsize = 0; 353 354 pkt = NULL; 355 recv_extra.tftp_handle = h; 356 res = sendrecv(h->iodesc, &sendudp, &wbuf.t, wtail - (char *)&wbuf.t, 357 &recvtftp, &pkt, (void **)&t, &recv_extra); 358 if (res == -1) { 359 free(pkt); 360 return (errno); 361 } 362 363 free(h->pkt); 364 h->pkt = pkt; 365 h->tftp_hdr = t; 366 367 if (recv_extra.rtype == OACK) 368 return (tftp_getnextblock(h)); 369 370 /* Server ignored our blksize request, revert to TFTP default. */ 371 h->tftp_blksize = SEGSIZE; 372 373 switch (recv_extra.rtype) { 374 case DATA: { 375 h->currblock = 1; 376 h->validsize = res; 377 h->islastblock = 0; 378 if (res < h->tftp_blksize) { 379 h->islastblock = 1; /* very short file */ 380 tftp_sendack(h, h->currblock); 381 h->lastacksent = true; 382 } 383 return (0); 384 } 385 case ERROR: 386 default: 387 return (errno); 388 } 389 390 } 391 392 /* ack block, expect next */ 393 static int 394 tftp_getnextblock(struct tftp_handle *h) 395 { 396 struct { 397 u_char header[HEADER_SIZE]; 398 struct tftphdr t; 399 } __packed __aligned(4) wbuf; 400 struct tftprecv_extra recv_extra; 401 char *wtail; 402 int res; 403 void *pkt; 404 struct tftphdr *t; 405 406 wbuf.t.th_opcode = htons((u_short)ACK); 407 wtail = (char *)&wbuf.t.th_block; 408 wbuf.t.th_block = htons((u_short)h->currblock); 409 wtail += 2; 410 411 h->iodesc->xid = h->currblock + 1; /* expected block */ 412 413 pkt = NULL; 414 recv_extra.tftp_handle = h; 415 res = sendrecv(h->iodesc, &sendudp, &wbuf.t, wtail - (char *)&wbuf.t, 416 &recvtftp, &pkt, (void **)&t, &recv_extra); 417 418 if (res == -1) { /* 0 is OK! */ 419 free(pkt); 420 return (errno); 421 } 422 423 free(h->pkt); 424 h->pkt = pkt; 425 h->tftp_hdr = t; 426 h->currblock++; 427 h->validsize = res; 428 if (res < h->tftp_blksize) 429 h->islastblock = 1; /* EOF */ 430 431 if (h->islastblock == 1) { 432 /* Send an ACK for the last block */ 433 wbuf.t.th_block = htons((u_short)h->currblock); 434 sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t); 435 } 436 437 return (0); 438 } 439 440 static int 441 tftp_open(const char *path, struct open_file *f) 442 { 443 struct devdesc *dev; 444 struct tftp_handle *tftpfile; 445 struct iodesc *io; 446 int res; 447 size_t pathsize; 448 const char *extraslash; 449 450 if (netproto != NET_TFTP) 451 return (EINVAL); 452 453 if (f->f_dev->dv_type != DEVT_NET) 454 return (EINVAL); 455 456 if (is_open) 457 return (EBUSY); 458 459 tftpfile = calloc(1, sizeof(*tftpfile)); 460 if (!tftpfile) 461 return (ENOMEM); 462 463 tftpfile->tftp_blksize = TFTP_REQUESTED_BLKSIZE; 464 dev = f->f_devdata; 465 tftpfile->iodesc = io = socktodesc(*(int *)(dev->d_opendata)); 466 if (io == NULL) { 467 free(tftpfile); 468 return (EINVAL); 469 } 470 471 io->destip = rootip; 472 tftpfile->off = 0; 473 pathsize = (strlen(rootpath) + 1 + strlen(path) + 1) * sizeof(char); 474 tftpfile->path = malloc(pathsize); 475 if (tftpfile->path == NULL) { 476 free(tftpfile); 477 return (ENOMEM); 478 } 479 if (rootpath[strlen(rootpath) - 1] == '/' || path[0] == '/') 480 extraslash = ""; 481 else 482 extraslash = "/"; 483 res = snprintf(tftpfile->path, pathsize, "%s%s%s", 484 rootpath, extraslash, path); 485 if (res < 0 || res > pathsize) { 486 free(tftpfile->path); 487 free(tftpfile); 488 return (ENOMEM); 489 } 490 491 res = tftp_makereq(tftpfile); 492 493 if (res) { 494 free(tftpfile->path); 495 free(tftpfile->pkt); 496 free(tftpfile); 497 return (res); 498 } 499 f->f_fsdata = tftpfile; 500 is_open = 1; 501 return (0); 502 } 503 504 static int 505 tftp_read(struct open_file *f, void *addr, size_t size, 506 size_t *resid /* out */) 507 { 508 struct tftp_handle *tftpfile; 509 size_t res; 510 int rc; 511 512 rc = 0; 513 res = size; 514 tftpfile = f->f_fsdata; 515 516 /* Make sure we will not read past file end */ 517 if (tftpfile->tftp_tsize > 0 && 518 tftpfile->off + size > tftpfile->tftp_tsize) { 519 size = tftpfile->tftp_tsize - tftpfile->off; 520 } 521 522 if (tftpfile->tftp_cache != NULL) { 523 bcopy(tftpfile->tftp_cache + tftpfile->off, 524 addr, size); 525 526 addr = (char *)addr + size; 527 tftpfile->off += size; 528 res -= size; 529 goto out; 530 } 531 532 while (size > 0) { 533 int needblock, count; 534 535 twiddle(32); 536 537 needblock = tftpfile->off / tftpfile->tftp_blksize + 1; 538 539 if (tftpfile->currblock > needblock) { /* seek backwards */ 540 tftp_senderr(tftpfile, 0, "No error: read aborted"); 541 rc = tftp_makereq(tftpfile); 542 if (rc != 0) 543 break; 544 } 545 546 while (tftpfile->currblock < needblock) { 547 548 rc = tftp_getnextblock(tftpfile); 549 if (rc) { /* no answer */ 550 #ifdef TFTP_DEBUG 551 printf("tftp: read error\n"); 552 #endif 553 if (tftpfile->tries > TFTP_TRIES) { 554 return (rc); 555 } else { 556 tftpfile->tries++; 557 tftp_makereq(tftpfile); 558 } 559 } 560 if (tftpfile->islastblock) 561 break; 562 } 563 564 if (tftpfile->currblock == needblock) { 565 int offinblock, inbuffer; 566 567 offinblock = tftpfile->off % tftpfile->tftp_blksize; 568 569 inbuffer = tftpfile->validsize - offinblock; 570 if (inbuffer < 0) { 571 #ifdef TFTP_DEBUG 572 printf("tftp: invalid offset %d\n", 573 tftpfile->off); 574 #endif 575 return (EINVAL); 576 } 577 count = (size < inbuffer ? size : inbuffer); 578 bcopy(tftpfile->tftp_hdr->th_data + offinblock, 579 addr, count); 580 581 addr = (char *)addr + count; 582 tftpfile->off += count; 583 size -= count; 584 res -= count; 585 586 if ((tftpfile->islastblock) && (count == inbuffer)) 587 break; /* EOF */ 588 } else { 589 #ifdef TFTP_DEBUG 590 printf("tftp: block %d not found\n", needblock); 591 #endif 592 return (EINVAL); 593 } 594 595 } 596 597 out: 598 if (resid != NULL) 599 *resid = res; 600 return (rc); 601 } 602 603 static int 604 tftp_close(struct open_file *f) 605 { 606 struct tftp_handle *tftpfile; 607 tftpfile = f->f_fsdata; 608 609 if (tftpfile->lastacksent == false) 610 tftp_senderr(tftpfile, 0, "No error: file closed"); 611 612 if (tftpfile) { 613 free(tftpfile->path); 614 free(tftpfile->pkt); 615 free(tftpfile->tftp_cache); 616 free(tftpfile); 617 } 618 is_open = 0; 619 return (0); 620 } 621 622 static int 623 tftp_stat(struct open_file *f, struct stat *sb) 624 { 625 struct tftp_handle *tftpfile; 626 tftpfile = f->f_fsdata; 627 628 sb->st_mode = 0444 | S_IFREG; 629 sb->st_nlink = 1; 630 sb->st_uid = 0; 631 sb->st_gid = 0; 632 sb->st_size = tftpfile->tftp_tsize; 633 return (0); 634 } 635 636 static off_t 637 tftp_seek(struct open_file *f, off_t offset, int where) 638 { 639 struct tftp_handle *tftpfile; 640 tftpfile = f->f_fsdata; 641 642 switch (where) { 643 case SEEK_SET: 644 tftpfile->off = offset; 645 break; 646 case SEEK_CUR: 647 tftpfile->off += offset; 648 break; 649 default: 650 errno = EOFFSET; 651 return (-1); 652 } 653 return (tftpfile->off); 654 } 655 656 static int 657 tftp_preload(struct open_file *f) 658 { 659 struct tftp_handle *tftpfile; 660 char *cache; 661 int rc; 662 #ifdef TFTP_DEBUG 663 time_t start, end; 664 #endif 665 666 tftpfile = f->f_fsdata; 667 cache = malloc(sizeof(char) * tftpfile->tftp_tsize); 668 if (cache == NULL) { 669 printf("Couldn't allocate %ju bytes for preload caching" 670 ", disabling caching\n", 671 (uintmax_t)sizeof(char) * tftpfile->tftp_tsize); 672 return (-1); 673 } 674 675 #ifdef TFTP_DEBUG 676 start = getsecs(); 677 printf("Preloading %s ", tftpfile->path); 678 #endif 679 if (tftpfile->currblock == 1) 680 bcopy(tftpfile->tftp_hdr->th_data, 681 cache, 682 tftpfile->validsize); 683 else 684 tftpfile->currblock = 0; 685 686 while (tftpfile->islastblock == 0) { 687 twiddle(32); 688 rc = tftp_getnextblock(tftpfile); 689 if (rc) { 690 free(cache); 691 printf("Got TFTP error %d, disabling caching\n", rc); 692 return (rc); 693 } 694 bcopy(tftpfile->tftp_hdr->th_data, 695 cache + (tftpfile->tftp_blksize * (tftpfile->currblock - 1)), 696 tftpfile->validsize); 697 } 698 #ifdef TFTP_DEBUG 699 end = getsecs(); 700 printf("\nPreloaded %s (%ju bytes) during %jd seconds\n", 701 tftpfile->path, (intmax_t)tftpfile->tftp_tsize, 702 (intmax_t)end - start); 703 #endif 704 705 tftpfile->tftp_cache = cache; 706 return (0); 707 } 708 709 static int 710 tftp_set_blksize(struct tftp_handle *h, const char *str) 711 { 712 char *endptr; 713 int new_blksize; 714 int ret = 0; 715 716 if (h == NULL || str == NULL) 717 return (ret); 718 719 new_blksize = 720 (unsigned int)strtol(str, &endptr, 0); 721 722 /* 723 * Only accept blksize value if it is numeric. 724 * RFC2348 specifies that acceptable values are 8-65464. 725 * Let's choose a limit less than MAXRSPACE. 726 */ 727 if (*endptr == '\0' && new_blksize >= 8 && 728 new_blksize <= TFTP_MAX_BLKSIZE) { 729 h->tftp_blksize = new_blksize; 730 ret = 1; 731 } 732 733 return (ret); 734 } 735 736 /* 737 * In RFC2347, the TFTP Option Acknowledgement package (OACK) 738 * is used to acknowledge a client's option negotiation request. 739 * The format of an OACK packet is: 740 * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ 741 * | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | 742 * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ 743 * 744 * opc 745 * The opcode field contains a 6, for Option Acknowledgment. 746 * 747 * opt1 748 * The first option acknowledgment, copied from the original 749 * request. 750 * 751 * value1 752 * The acknowledged value associated with the first option. If 753 * and how this value may differ from the original request is 754 * detailed in the specification for the option. 755 * 756 * optN, valueN 757 * The final option/value acknowledgment pair. 758 */ 759 static int 760 tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len) 761 { 762 /* 763 * We parse the OACK strings into an array 764 * of name-value pairs. 765 */ 766 char *tftp_options[128] = { 0 }; 767 char *val = buf; 768 int i = 0; 769 int option_idx = 0; 770 int blksize_is_set = 0; 771 int tsize = 0; 772 773 unsigned int orig_blksize; 774 775 while (option_idx < 128 && i < len) { 776 if (buf[i] == '\0') { 777 if (&buf[i] > val) { 778 tftp_options[option_idx] = val; 779 val = &buf[i] + 1; 780 ++option_idx; 781 } 782 } 783 ++i; 784 } 785 786 /* Save the block size we requested for sanity check later. */ 787 orig_blksize = h->tftp_blksize; 788 789 /* 790 * Parse individual TFTP options. 791 * * "blksize" is specified in RFC2348. 792 * * "tsize" is specified in RFC2349. 793 */ 794 for (i = 0; i < option_idx; i += 2) { 795 if (strcasecmp(tftp_options[i], "blksize") == 0) { 796 if (i + 1 < option_idx) 797 blksize_is_set = 798 tftp_set_blksize(h, tftp_options[i + 1]); 799 } else if (strcasecmp(tftp_options[i], "tsize") == 0) { 800 if (i + 1 < option_idx) 801 tsize = strtol(tftp_options[i + 1], NULL, 10); 802 if (tsize != 0) 803 h->tftp_tsize = tsize; 804 } else { 805 /* 806 * Do not allow any options we did not expect to be 807 * ACKed. 808 */ 809 printf("unexpected tftp option '%s'\n", 810 tftp_options[i]); 811 return (-1); 812 } 813 } 814 815 if (!blksize_is_set) { 816 /* 817 * If TFTP blksize was not set, try defaulting 818 * to the legacy TFTP blksize of SEGSIZE(512) 819 */ 820 h->tftp_blksize = SEGSIZE; 821 } else if (h->tftp_blksize > orig_blksize) { 822 /* 823 * Server should not be proposing block sizes that 824 * exceed what we said we can handle. 825 */ 826 printf("unexpected blksize %u\n", h->tftp_blksize); 827 return (-1); 828 } 829 830 #ifdef TFTP_DEBUG 831 printf("tftp_blksize: %u\n", h->tftp_blksize); 832 printf("tftp_tsize: %lu\n", h->tftp_tsize); 833 #endif 834 return (0); 835 } 836