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