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