1230557Sjimharris/* $NetBSD: tftp.c,v 1.4 1997/09/17 16:57:07 drochner Exp $ */ 2230557Sjimharris 3230557Sjimharris/* 4230557Sjimharris * Copyright (c) 1996 5230557Sjimharris * Matthias Drochner. All rights reserved. 6230557Sjimharris * 7230557Sjimharris * Redistribution and use in source and binary forms, with or without 8230557Sjimharris * modification, are permitted provided that the following conditions 9230557Sjimharris * are met: 10230557Sjimharris * 1. Redistributions of source code must retain the above copyright 11230557Sjimharris * notice, this list of conditions and the following disclaimer. 12230557Sjimharris * 2. Redistributions in binary form must reproduce the above copyright 13230557Sjimharris * notice, this list of conditions and the following disclaimer in the 14230557Sjimharris * documentation and/or other materials provided with the distribution. 15230557Sjimharris * 3. All advertising materials mentioning features or use of this software 16230557Sjimharris * must display the following acknowledgement: 17230557Sjimharris * This product includes software developed for the NetBSD Project 18230557Sjimharris * by Matthias Drochner. 19230557Sjimharris * 4. The name of the author may not be used to endorse or promote products 20230557Sjimharris * derived from this software without specific prior written permission. 21230557Sjimharris * 22230557Sjimharris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23230557Sjimharris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24230557Sjimharris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25230557Sjimharris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26230557Sjimharris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27230557Sjimharris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28230557Sjimharris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29230557Sjimharris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30230557Sjimharris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31230557Sjimharris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32230557Sjimharris */ 33230557Sjimharris 34230557Sjimharris#include <sys/cdefs.h> 35230557Sjimharris__FBSDID("$FreeBSD$"); 36230557Sjimharris 37230557Sjimharris/* 38230557Sjimharris * Simple TFTP implementation for libsa. 39230557Sjimharris * Assumes: 40230557Sjimharris * - socket descriptor (int) at open_file->f_devdata 41230557Sjimharris * - server host IP in global servip 42230557Sjimharris * Restrictions: 43230557Sjimharris * - read only 44230557Sjimharris * - lseek only with SEEK_SET or SEEK_CUR 45230557Sjimharris * - no big time differences between transfers (<tftp timeout) 46230557Sjimharris */ 47230557Sjimharris 48230557Sjimharris#include <sys/types.h> 49230557Sjimharris#include <sys/stat.h> 50230557Sjimharris#include <netinet/in.h> 51230557Sjimharris#include <netinet/udp.h> 52230557Sjimharris#include <netinet/in_systm.h> 53230557Sjimharris#include <arpa/tftp.h> 54230557Sjimharris 55230557Sjimharris#include <string.h> 56230557Sjimharris 57230557Sjimharris#include "stand.h" 58230557Sjimharris#include "net.h" 59230557Sjimharris#include "netif.h" 60230557Sjimharris 61230557Sjimharris#include "tftp.h" 62230557Sjimharris 63230557Sjimharrisstruct tftp_handle; 64230557Sjimharris 65230557Sjimharrisstatic int tftp_open(const char *path, struct open_file *f); 66230557Sjimharrisstatic int tftp_close(struct open_file *f); 67230557Sjimharrisstatic int tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len); 68230557Sjimharrisstatic int tftp_read(struct open_file *f, void *buf, size_t size, size_t *resid); 69230557Sjimharrisstatic int tftp_write(struct open_file *f, void *buf, size_t size, size_t *resid); 70230557Sjimharrisstatic off_t tftp_seek(struct open_file *f, off_t offset, int where); 71230557Sjimharrisstatic int tftp_set_blksize(struct tftp_handle *h, const char *str); 72230557Sjimharrisstatic int tftp_stat(struct open_file *f, struct stat *sb); 73230557Sjimharrisstatic ssize_t sendrecv_tftp(struct tftp_handle *h, 74230557Sjimharris ssize_t (*sproc)(struct iodesc *, void *, size_t), 75230557Sjimharris void *sbuf, size_t ssize, 76230557Sjimharris ssize_t (*rproc)(struct tftp_handle *h, void *, ssize_t, time_t, unsigned short *), 77230557Sjimharris void *rbuf, size_t rsize, unsigned short *rtype); 78230557Sjimharris 79230557Sjimharrisstruct fs_ops tftp_fsops = { 80230557Sjimharris "tftp", 81230557Sjimharris tftp_open, 82230557Sjimharris tftp_close, 83230557Sjimharris tftp_read, 84230557Sjimharris tftp_write, 85230557Sjimharris tftp_seek, 86230557Sjimharris tftp_stat, 87230557Sjimharris null_readdir 88230557Sjimharris}; 89230557Sjimharris 90230557Sjimharrisextern struct in_addr servip; 91230557Sjimharris 92230557Sjimharrisstatic int tftpport = 2000; 93230557Sjimharrisstatic int is_open = 0; 94230557Sjimharris 95230557Sjimharris/* 96230557Sjimharris * The legacy TFTP_BLKSIZE value was SEGSIZE(512). 97230557Sjimharris * TFTP_REQUESTED_BLKSIZE of 1428 is (Ethernet MTU, less the TFTP, UDP and 98230557Sjimharris * IP header lengths). 99230557Sjimharris */ 100230557Sjimharris#define TFTP_REQUESTED_BLKSIZE 1428 101230557Sjimharris 102230557Sjimharris/* 103230557Sjimharris * Choose a blksize big enough so we can test with Ethernet 104230557Sjimharris * Jumbo frames in the future. 105230557Sjimharris */ 106230557Sjimharris#define TFTP_MAX_BLKSIZE 9008 107230557Sjimharris 108230557Sjimharrisstruct tftp_handle { 109230557Sjimharris struct iodesc *iodesc; 110230557Sjimharris int currblock; /* contents of lastdata */ 111230557Sjimharris int islastblock; /* flag */ 112230557Sjimharris int validsize; 113230557Sjimharris int off; 114230557Sjimharris char *path; /* saved for re-requests */ 115230557Sjimharris unsigned int tftp_blksize; 116230557Sjimharris unsigned long tftp_tsize; 117230557Sjimharris struct { 118230557Sjimharris u_char header[HEADER_SIZE]; 119230557Sjimharris struct tftphdr t; 120230557Sjimharris u_char space[TFTP_MAX_BLKSIZE]; 121230557Sjimharris } __packed __aligned(4) lastdata; 122230557Sjimharris}; 123230557Sjimharris 124230557Sjimharris#define TFTP_MAX_ERRCODE EOPTNEG 125230557Sjimharrisstatic const int tftperrors[TFTP_MAX_ERRCODE + 1] = { 126230557Sjimharris 0, /* ??? */ 127230557Sjimharris ENOENT, 128230557Sjimharris EPERM, 129230557Sjimharris ENOSPC, 130230557Sjimharris EINVAL, /* ??? */ 131230557Sjimharris EINVAL, /* ??? */ 132230557Sjimharris EEXIST, 133230557Sjimharris EINVAL, /* ??? */ 134230557Sjimharris EINVAL, /* Option negotiation failed. */ 135230557Sjimharris}; 136230557Sjimharris 137230557Sjimharrisstatic int tftp_getnextblock(struct tftp_handle *h); 138230557Sjimharris 139230557Sjimharris/* send error message back. */ 140230557Sjimharrisstatic void 141230557Sjimharristftp_senderr(struct tftp_handle *h, u_short errcode, const char *msg) 142230557Sjimharris{ 143230557Sjimharris struct { 144230557Sjimharris u_char header[HEADER_SIZE]; 145230557Sjimharris struct tftphdr t; 146230557Sjimharris u_char space[63]; /* +1 from t */ 147230557Sjimharris } __packed __aligned(4) wbuf; 148230557Sjimharris char *wtail; 149230557Sjimharris int len; 150230557Sjimharris 151230557Sjimharris len = strlen(msg); 152230557Sjimharris if (len > sizeof(wbuf.space)) 153230557Sjimharris len = sizeof(wbuf.space); 154230557Sjimharris 155230557Sjimharris wbuf.t.th_opcode = htons((u_short) ERROR); 156230557Sjimharris wbuf.t.th_code = htons(errcode); 157230557Sjimharris 158230557Sjimharris wtail = wbuf.t.th_msg; 159230557Sjimharris bcopy(msg, wtail, len); 160230557Sjimharris wtail[len] = '\0'; 161230557Sjimharris wtail += len + 1; 162230557Sjimharris 163230557Sjimharris sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t); 164230557Sjimharris} 165230557Sjimharris 166230557Sjimharrisstatic void 167230557Sjimharristftp_sendack(struct tftp_handle *h) 168230557Sjimharris{ 169230557Sjimharris struct { 170230557Sjimharris u_char header[HEADER_SIZE]; 171230557Sjimharris struct tftphdr t; 172230557Sjimharris } __packed __aligned(4) wbuf; 173230557Sjimharris char *wtail; 174230557Sjimharris 175230557Sjimharris wbuf.t.th_opcode = htons((u_short) ACK); 176230557Sjimharris wtail = (char *) &wbuf.t.th_block; 177230557Sjimharris wbuf.t.th_block = htons((u_short) h->currblock); 178230557Sjimharris wtail += 2; 179230557Sjimharris 180230557Sjimharris sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t); 181230557Sjimharris} 182230557Sjimharris 183230557Sjimharrisstatic ssize_t 184230557Sjimharrisrecvtftp(struct tftp_handle *h, void *pkt, ssize_t len, time_t tleft, 185230557Sjimharris unsigned short *rtype) 186230557Sjimharris{ 187230557Sjimharris struct iodesc *d = h->iodesc; 188230557Sjimharris struct tftphdr *t; 189230557Sjimharris 190230557Sjimharris errno = 0; 191230557Sjimharris 192230557Sjimharris len = readudp(d, pkt, len, tleft); 193230557Sjimharris 194230557Sjimharris if (len < 4) 195230557Sjimharris return (-1); 196230557Sjimharris 197230557Sjimharris t = (struct tftphdr *) pkt; 198230557Sjimharris *rtype = ntohs(t->th_opcode); 199230557Sjimharris switch (ntohs(t->th_opcode)) { 200230557Sjimharris case DATA: { 201230557Sjimharris int got; 202230557Sjimharris 203230557Sjimharris if (htons(t->th_block) != d->xid) { 204230557Sjimharris /* 205230557Sjimharris * Expected block? 206230557Sjimharris */ 207230557Sjimharris return (-1); 208230557Sjimharris } 209230557Sjimharris if (d->xid == 1) { 210230557Sjimharris /* 211230557Sjimharris * First data packet from new port. 212230557Sjimharris */ 213230557Sjimharris struct udphdr *uh; 214230557Sjimharris uh = (struct udphdr *) pkt - 1; 215230557Sjimharris d->destport = uh->uh_sport; 216230557Sjimharris } /* else check uh_sport has not changed??? */ 217230557Sjimharris got = len - (t->th_data - (char *) t); 218230557Sjimharris return got; 219230557Sjimharris } 220230557Sjimharris case ERROR: 221230557Sjimharris if ((unsigned) ntohs(t->th_code) > TFTP_MAX_ERRCODE) { 222230557Sjimharris printf("illegal tftp error %d\n", ntohs(t->th_code)); 223230557Sjimharris errno = EIO; 224230557Sjimharris } else { 225230557Sjimharris#ifdef TFTP_DEBUG 226230557Sjimharris printf("tftp-error %d\n", ntohs(t->th_code)); 227230557Sjimharris#endif 228230557Sjimharris errno = tftperrors[ntohs(t->th_code)]; 229230557Sjimharris } 230230557Sjimharris return (-1); 231230557Sjimharris case OACK: { 232230557Sjimharris struct udphdr *uh; 233230557Sjimharris int tftp_oack_len; 234230557Sjimharris 235230557Sjimharris /* 236230557Sjimharris * Unexpected OACK. TFTP transfer already in progress. 237230557Sjimharris * Drop the pkt. 238230557Sjimharris */ 239230557Sjimharris if (d->xid != 1) { 240230557Sjimharris return (-1); 241230557Sjimharris } 242230557Sjimharris 243230557Sjimharris /* 244230557Sjimharris * Remember which port this OACK came from, because we need 245230557Sjimharris * to send the ACK or errors back to it. 246230557Sjimharris */ 247230557Sjimharris uh = (struct udphdr *) pkt - 1; 248230557Sjimharris d->destport = uh->uh_sport; 249230557Sjimharris 250230557Sjimharris /* Parse options ACK-ed by the server. */ 251230557Sjimharris tftp_oack_len = len - sizeof(t->th_opcode); 252230557Sjimharris if (tftp_parse_oack(h, t->th_u.tu_stuff, tftp_oack_len) != 0) { 253230557Sjimharris tftp_senderr(h, EOPTNEG, "Malformed OACK"); 254230557Sjimharris errno = EIO; 255230557Sjimharris return (-1); 256230557Sjimharris } 257230557Sjimharris return (0); 258230557Sjimharris } 259230557Sjimharris default: 260230557Sjimharris#ifdef TFTP_DEBUG 261230557Sjimharris printf("tftp type %d not handled\n", ntohs(t->th_opcode)); 262230557Sjimharris#endif 263230557Sjimharris return (-1); 264230557Sjimharris } 265230557Sjimharris} 266230557Sjimharris 267230557Sjimharris/* send request, expect first block (or error) */ 268230557Sjimharrisstatic int 269230557Sjimharristftp_makereq(struct tftp_handle *h) 270230557Sjimharris{ 271230557Sjimharris struct { 272230557Sjimharris u_char header[HEADER_SIZE]; 273230557Sjimharris struct tftphdr t; 274230557Sjimharris u_char space[FNAME_SIZE + 6]; 275230557Sjimharris } __packed __aligned(4) wbuf; 276230557Sjimharris char *wtail; 277230557Sjimharris int l; 278230557Sjimharris ssize_t res; 279230557Sjimharris struct tftphdr *t; 280230557Sjimharris char *tftp_blksize = NULL; 281230557Sjimharris int blksize_l; 282230557Sjimharris unsigned short rtype = 0; 283230557Sjimharris 284230557Sjimharris /* 285230557Sjimharris * Allow overriding default TFTP block size by setting 286230557Sjimharris * a tftp.blksize environment variable. 287230557Sjimharris */ 288230557Sjimharris if ((tftp_blksize = getenv("tftp.blksize")) != NULL) { 289230557Sjimharris tftp_set_blksize(h, tftp_blksize); 290230557Sjimharris } 291230557Sjimharris 292230557Sjimharris wbuf.t.th_opcode = htons((u_short) RRQ); 293230557Sjimharris wtail = wbuf.t.th_stuff; 294230557Sjimharris l = strlen(h->path); 295230557Sjimharris#ifdef TFTP_PREPEND_PATH 296230557Sjimharris if (l > FNAME_SIZE - (sizeof(TFTP_PREPEND_PATH) - 1)) 297230557Sjimharris return (ENAMETOOLONG); 298230557Sjimharris bcopy(TFTP_PREPEND_PATH, wtail, sizeof(TFTP_PREPEND_PATH) - 1); 299230557Sjimharris wtail += sizeof(TFTP_PREPEND_PATH) - 1; 300230557Sjimharris#else 301230557Sjimharris if (l > FNAME_SIZE) 302230557Sjimharris return (ENAMETOOLONG); 303230557Sjimharris#endif 304230557Sjimharris bcopy(h->path, wtail, l + 1); 305230557Sjimharris wtail += l + 1; 306230557Sjimharris bcopy("octet", wtail, 6); 307230557Sjimharris wtail += 6; 308230557Sjimharris bcopy("blksize", wtail, 8); 309230557Sjimharris wtail += 8; 310230557Sjimharris blksize_l = sprintf(wtail, "%d", h->tftp_blksize); 311230557Sjimharris wtail += blksize_l + 1; 312230557Sjimharris bcopy("tsize", wtail, 6); 313230557Sjimharris wtail += 6; 314230557Sjimharris bcopy("0", wtail, 2); 315230557Sjimharris wtail += 2; 316230557Sjimharris 317230557Sjimharris t = &h->lastdata.t; 318230557Sjimharris 319230557Sjimharris /* h->iodesc->myport = htons(--tftpport); */ 320230557Sjimharris h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff)); 321230557Sjimharris h->iodesc->destport = htons(IPPORT_TFTP); 322230557Sjimharris h->iodesc->xid = 1; /* expected block */ 323230557Sjimharris 324230557Sjimharris h->currblock = 0; 325230557Sjimharris h->islastblock = 0; 326230557Sjimharris h->validsize = 0; 327230557Sjimharris 328230557Sjimharris res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t, 329230557Sjimharris &recvtftp, t, sizeof(*t) + h->tftp_blksize, &rtype); 330230557Sjimharris 331230557Sjimharris if (rtype == OACK) 332230557Sjimharris return (tftp_getnextblock(h)); 333230557Sjimharris 334230557Sjimharris /* Server ignored our blksize request, revert to TFTP default. */ 335230557Sjimharris h->tftp_blksize = SEGSIZE; 336230557Sjimharris 337230557Sjimharris switch (rtype) { 338230557Sjimharris case DATA: { 339230557Sjimharris h->currblock = 1; 340230557Sjimharris h->validsize = res; 341230557Sjimharris h->islastblock = 0; 342230557Sjimharris if (res < h->tftp_blksize) { 343230557Sjimharris h->islastblock = 1; /* very short file */ 344230557Sjimharris tftp_sendack(h); 345230557Sjimharris } 346230557Sjimharris return (0); 347230557Sjimharris } 348230557Sjimharris case ERROR: 349230557Sjimharris default: 350230557Sjimharris return (errno); 351230557Sjimharris } 352230557Sjimharris 353230557Sjimharris} 354230557Sjimharris 355230557Sjimharris/* ack block, expect next */ 356230557Sjimharrisstatic int 357230557Sjimharristftp_getnextblock(struct tftp_handle *h) 358230557Sjimharris{ 359230557Sjimharris struct { 360230557Sjimharris u_char header[HEADER_SIZE]; 361230557Sjimharris struct tftphdr t; 362230557Sjimharris } __packed __aligned(4) wbuf; 363230557Sjimharris char *wtail; 364230557Sjimharris int res; 365230557Sjimharris struct tftphdr *t; 366230557Sjimharris unsigned short rtype = 0; 367230557Sjimharris wbuf.t.th_opcode = htons((u_short) ACK); 368230557Sjimharris wtail = (char *) &wbuf.t.th_block; 369230557Sjimharris wbuf.t.th_block = htons((u_short) h->currblock); 370230557Sjimharris wtail += 2; 371230557Sjimharris 372230557Sjimharris t = &h->lastdata.t; 373230557Sjimharris 374230557Sjimharris h->iodesc->xid = h->currblock + 1; /* expected block */ 375230557Sjimharris 376230557Sjimharris res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t, 377230557Sjimharris &recvtftp, t, sizeof(*t) + h->tftp_blksize, &rtype); 378230557Sjimharris 379230557Sjimharris if (res == -1) /* 0 is OK! */ 380230557Sjimharris return (errno); 381230557Sjimharris 382230557Sjimharris h->currblock++; 383230557Sjimharris h->validsize = res; 384230557Sjimharris if (res < h->tftp_blksize) 385230557Sjimharris h->islastblock = 1; /* EOF */ 386230557Sjimharris 387230557Sjimharris if (h->islastblock == 1) { 388230557Sjimharris /* Send an ACK for the last block */ 389230557Sjimharris wbuf.t.th_block = htons((u_short) h->currblock); 390230557Sjimharris sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t); 391230557Sjimharris } 392230557Sjimharris 393230557Sjimharris return (0); 394230557Sjimharris} 395230557Sjimharris 396230557Sjimharrisstatic int 397230557Sjimharristftp_open(const char *path, struct open_file *f) 398230557Sjimharris{ 399230557Sjimharris struct tftp_handle *tftpfile; 400230557Sjimharris struct iodesc *io; 401230557Sjimharris int res; 402230557Sjimharris 403230557Sjimharris if (strcmp(f->f_dev->dv_name, "net") != 0) { 404230557Sjimharris#ifdef __i386__ 405230557Sjimharris if (strcmp(f->f_dev->dv_name, "pxe") != 0) 406230557Sjimharris return (EINVAL); 407230557Sjimharris#else 408230557Sjimharris return (EINVAL); 409230557Sjimharris#endif 410230557Sjimharris } 411230557Sjimharris 412230557Sjimharris if (is_open) 413230557Sjimharris return (EBUSY); 414230557Sjimharris 415230557Sjimharris tftpfile = (struct tftp_handle *) malloc(sizeof(*tftpfile)); 416230557Sjimharris if (!tftpfile) 417230557Sjimharris return (ENOMEM); 418230557Sjimharris 419230557Sjimharris memset(tftpfile, 0, sizeof(*tftpfile)); 420230557Sjimharris tftpfile->tftp_blksize = TFTP_REQUESTED_BLKSIZE; 421230557Sjimharris tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata)); 422230557Sjimharris if (io == NULL) 423230557Sjimharris return (EINVAL); 424230557Sjimharris 425230557Sjimharris io->destip = servip; 426230557Sjimharris tftpfile->off = 0; 427230557Sjimharris tftpfile->path = strdup(path); 428230557Sjimharris if (tftpfile->path == NULL) { 429230557Sjimharris free(tftpfile); 430230557Sjimharris return(ENOMEM); 431230557Sjimharris } 432230557Sjimharris 433230557Sjimharris res = tftp_makereq(tftpfile); 434230557Sjimharris 435230557Sjimharris if (res) { 436230557Sjimharris free(tftpfile->path); 437230557Sjimharris free(tftpfile); 438230557Sjimharris return (res); 439230557Sjimharris } 440230557Sjimharris f->f_fsdata = (void *) tftpfile; 441230557Sjimharris is_open = 1; 442230557Sjimharris return (0); 443230557Sjimharris} 444230557Sjimharris 445230557Sjimharrisstatic int 446230557Sjimharristftp_read(struct open_file *f, void *addr, size_t size, 447230557Sjimharris size_t *resid /* out */) 448230557Sjimharris{ 449230557Sjimharris struct tftp_handle *tftpfile; 450230557Sjimharris static int tc = 0; 451230557Sjimharris tftpfile = (struct tftp_handle *) f->f_fsdata; 452230557Sjimharris 453230557Sjimharris while (size > 0) { 454230557Sjimharris int needblock, count; 455230557Sjimharris 456230557Sjimharris if (!(tc++ % 16)) 457230557Sjimharris twiddle(); 458230557Sjimharris 459230557Sjimharris needblock = tftpfile->off / tftpfile->tftp_blksize + 1; 460230557Sjimharris 461230557Sjimharris if (tftpfile->currblock > needblock) { /* seek backwards */ 462230557Sjimharris tftp_senderr(tftpfile, 0, "No error: read aborted"); 463 tftp_makereq(tftpfile); /* no error check, it worked 464 * for open */ 465 } 466 467 while (tftpfile->currblock < needblock) { 468 int res; 469 470 res = tftp_getnextblock(tftpfile); 471 if (res) { /* no answer */ 472#ifdef TFTP_DEBUG 473 printf("tftp: read error\n"); 474#endif 475 return (res); 476 } 477 if (tftpfile->islastblock) 478 break; 479 } 480 481 if (tftpfile->currblock == needblock) { 482 int offinblock, inbuffer; 483 484 offinblock = tftpfile->off % tftpfile->tftp_blksize; 485 486 inbuffer = tftpfile->validsize - offinblock; 487 if (inbuffer < 0) { 488#ifdef TFTP_DEBUG 489 printf("tftp: invalid offset %d\n", 490 tftpfile->off); 491#endif 492 return (EINVAL); 493 } 494 count = (size < inbuffer ? size : inbuffer); 495 bcopy(tftpfile->lastdata.t.th_data + offinblock, 496 addr, count); 497 498 addr = (char *)addr + count; 499 tftpfile->off += count; 500 size -= count; 501 502 if ((tftpfile->islastblock) && (count == inbuffer)) 503 break; /* EOF */ 504 } else { 505#ifdef TFTP_DEBUG 506 printf("tftp: block %d not found\n", needblock); 507#endif 508 return (EINVAL); 509 } 510 511 } 512 513 if (resid) 514 *resid = size; 515 return (0); 516} 517 518static int 519tftp_close(struct open_file *f) 520{ 521 struct tftp_handle *tftpfile; 522 tftpfile = (struct tftp_handle *) f->f_fsdata; 523 524 /* let it time out ... */ 525 526 if (tftpfile) { 527 free(tftpfile->path); 528 free(tftpfile); 529 } 530 is_open = 0; 531 return (0); 532} 533 534static int 535tftp_write(struct open_file *f __unused, void *start __unused, size_t size __unused, 536 size_t *resid __unused /* out */) 537{ 538 return (EROFS); 539} 540 541static int 542tftp_stat(struct open_file *f, struct stat *sb) 543{ 544 struct tftp_handle *tftpfile; 545 tftpfile = (struct tftp_handle *) f->f_fsdata; 546 547 sb->st_mode = 0444 | S_IFREG; 548 sb->st_nlink = 1; 549 sb->st_uid = 0; 550 sb->st_gid = 0; 551 sb->st_size = -1; 552 return (0); 553} 554 555static off_t 556tftp_seek(struct open_file *f, off_t offset, int where) 557{ 558 struct tftp_handle *tftpfile; 559 tftpfile = (struct tftp_handle *) f->f_fsdata; 560 561 switch (where) { 562 case SEEK_SET: 563 tftpfile->off = offset; 564 break; 565 case SEEK_CUR: 566 tftpfile->off += offset; 567 break; 568 default: 569 errno = EOFFSET; 570 return (-1); 571 } 572 return (tftpfile->off); 573} 574 575static ssize_t 576sendrecv_tftp(struct tftp_handle *h, 577 ssize_t (*sproc)(struct iodesc *, void *, size_t), 578 void *sbuf, size_t ssize, 579 ssize_t (*rproc)(struct tftp_handle *, void *, ssize_t, time_t, unsigned short *), 580 void *rbuf, size_t rsize, unsigned short *rtype) 581{ 582 struct iodesc *d = h->iodesc; 583 ssize_t cc; 584 time_t t, t1, tleft; 585 586#ifdef TFTP_DEBUG 587 if (debug) 588 printf("sendrecv: called\n"); 589#endif 590 591 tleft = MINTMO; 592 t = t1 = getsecs(); 593 for (;;) { 594 if ((getsecs() - t) > MAXTMO) { 595 errno = ETIMEDOUT; 596 return -1; 597 } 598 599 cc = (*sproc)(d, sbuf, ssize); 600 if (cc != -1 && cc < ssize) 601 panic("sendrecv: short write! (%zd < %zu)", 602 cc, ssize); 603 604 if (cc == -1) { 605 /* Error on transmit; wait before retrying */ 606 while ((getsecs() - t1) < tleft); 607 continue; 608 } 609 610recvnext: 611 /* Try to get a packet and process it. */ 612 cc = (*rproc)(h, rbuf, rsize, tleft, rtype); 613 /* Return on data, EOF or real error. */ 614 if (cc != -1 || errno != 0) 615 return (cc); 616 if ((getsecs() - t1) < tleft) { 617 goto recvnext; 618 } 619 620 /* Timed out or didn't get the packet we're waiting for */ 621 tleft += MINTMO; 622 if (tleft > (2 * MINTMO)) { 623 tleft = (2 * MINTMO); 624 } 625 t1 = getsecs(); 626 } 627} 628 629static int 630tftp_set_blksize(struct tftp_handle *h, const char *str) 631{ 632 char *endptr; 633 int new_blksize; 634 int ret = 0; 635 636 if (h == NULL || str == NULL) 637 return (ret); 638 639 new_blksize = 640 (unsigned int)strtol(str, &endptr, 0); 641 642 /* 643 * Only accept blksize value if it is numeric. 644 * RFC2348 specifies that acceptable values are 8-65464. 645 * Let's choose a limit less than MAXRSPACE. 646 */ 647 if (*endptr == '\0' && new_blksize >= 8 648 && new_blksize <= TFTP_MAX_BLKSIZE) { 649 h->tftp_blksize = new_blksize; 650 ret = 1; 651 } 652 653 return (ret); 654} 655 656/* 657 * In RFC2347, the TFTP Option Acknowledgement package (OACK) 658 * is used to acknowledge a client's option negotiation request. 659 * The format of an OACK packet is: 660 * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ 661 * | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | 662 * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ 663 * 664 * opc 665 * The opcode field contains a 6, for Option Acknowledgment. 666 * 667 * opt1 668 * The first option acknowledgment, copied from the original 669 * request. 670 * 671 * value1 672 * The acknowledged value associated with the first option. If 673 * and how this value may differ from the original request is 674 * detailed in the specification for the option. 675 * 676 * optN, valueN 677 * The final option/value acknowledgment pair. 678 */ 679static int 680tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len) 681{ 682 /* 683 * We parse the OACK strings into an array 684 * of name-value pairs. 685 */ 686 char *tftp_options[128] = { 0 }; 687 char *val = buf; 688 int i = 0; 689 int option_idx = 0; 690 int blksize_is_set = 0; 691 int tsize = 0; 692 693 unsigned int orig_blksize; 694 695 while (option_idx < 128 && i < len) { 696 if (buf[i] == '\0') { 697 if (&buf[i] > val) { 698 tftp_options[option_idx] = val; 699 val = &buf[i] + 1; 700 ++option_idx; 701 } 702 } 703 ++i; 704 } 705 706 /* Save the block size we requested for sanity check later. */ 707 orig_blksize = h->tftp_blksize; 708 709 /* 710 * Parse individual TFTP options. 711 * * "blksize" is specified in RFC2348. 712 * * "tsize" is specified in RFC2349. 713 */ 714 for (i = 0; i < option_idx; i += 2) { 715 if (strcasecmp(tftp_options[i], "blksize") == 0) { 716 if (i + 1 < option_idx) 717 blksize_is_set = 718 tftp_set_blksize(h, tftp_options[i + 1]); 719 } else if (strcasecmp(tftp_options[i], "tsize") == 0) { 720 if (i + 1 < option_idx) 721 tsize = strtol(tftp_options[i + 1], (char **)NULL, 10); 722 } else { 723 /* Do not allow any options we did not expect to be ACKed. */ 724 printf("unexpected tftp option '%s'\n", tftp_options[i]); 725 return (-1); 726 } 727 } 728 729 if (!blksize_is_set) { 730 /* 731 * If TFTP blksize was not set, try defaulting 732 * to the legacy TFTP blksize of SEGSIZE(512) 733 */ 734 h->tftp_blksize = SEGSIZE; 735 } else if (h->tftp_blksize > orig_blksize) { 736 /* 737 * Server should not be proposing block sizes that 738 * exceed what we said we can handle. 739 */ 740 printf("unexpected blksize %u\n", h->tftp_blksize); 741 return (-1); 742 } 743 744#ifdef TFTP_DEBUG 745 printf("tftp_blksize: %u\n", h->tftp_blksize); 746 printf("tftp_tsize: %lu\n", h->tftp_tsize); 747#endif 748 return 0; 749} 750