Annotation of sys/lib/libsa/tftp.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: tftp.c,v 1.2 2004/04/02 04:39:51 deraadt Exp $ */
! 2: /* $NetBSD: tftp.c,v 1.15 2003/08/18 15:45:29 dsl Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 1996
! 6: * Matthias Drochner. All rights reserved.
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. Redistributions in binary form must reproduce the above copyright
! 14: * notice, this list of conditions and the following disclaimer in the
! 15: * documentation and/or other materials provided with the distribution.
! 16: *
! 17: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 18: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 19: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 20: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 21: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 22: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 23: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 24: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 25: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 26: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 27: */
! 28:
! 29: /*
! 30: * Simple TFTP implementation for libsa.
! 31: * Assumes:
! 32: * - socket descriptor (int) at open_file->f_devdata
! 33: * - server host IP in global servip
! 34: * Restrictions:
! 35: * - read only
! 36: * - lseek only with SEEK_SET or SEEK_CUR
! 37: * - no big time differences between transfers (<tftp timeout)
! 38: */
! 39:
! 40: /*
! 41: * XXX Does not currently implement:
! 42: * XXX
! 43: * XXX LIBSA_NO_FS_CLOSE
! 44: * XXX LIBSA_NO_FS_SEEK
! 45: * XXX LIBSA_NO_FS_WRITE
! 46: * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?)
! 47: * XXX LIBSA_FS_SINGLECOMPONENT (does this even make sense?)
! 48: */
! 49:
! 50: #include <sys/types.h>
! 51: #include <sys/stat.h>
! 52: #include <netinet/in.h>
! 53: #include <netinet/udp.h>
! 54: #include <netinet/in_systm.h>
! 55: #include <lib/libkern/libkern.h>
! 56:
! 57: #include "stand.h"
! 58: #include "net.h"
! 59: #include "netif.h"
! 60:
! 61: #include "tftp.h"
! 62:
! 63: extern struct in_addr servip;
! 64:
! 65: static int tftpport = 2000;
! 66:
! 67: #define RSPACE 520 /* max data packet, rounded up */
! 68:
! 69: struct tftp_handle {
! 70: struct iodesc *iodesc;
! 71: int currblock; /* contents of lastdata */
! 72: int islastblock; /* flag */
! 73: int validsize;
! 74: int off;
! 75: const char *path; /* saved for re-requests */
! 76: struct {
! 77: u_char header[HEADER_SIZE];
! 78: struct tftphdr t;
! 79: u_char space[RSPACE];
! 80: } lastdata;
! 81: };
! 82:
! 83: static const int tftperrors[8] = {
! 84: 0, /* ??? */
! 85: ENOENT,
! 86: EPERM,
! 87: ENOSPC,
! 88: EINVAL, /* ??? */
! 89: EINVAL, /* ??? */
! 90: EEXIST,
! 91: EINVAL /* ??? */
! 92: };
! 93:
! 94: ssize_t recvtftp(struct iodesc *, void *, size_t, time_t);
! 95: int tftp_makereq(struct tftp_handle *);
! 96: int tftp_getnextblock(struct tftp_handle *);
! 97: #ifndef TFTP_NOTERMINATE
! 98: void tftp_terminate(struct tftp_handle *);
! 99: #endif
! 100:
! 101: ssize_t
! 102: recvtftp(struct iodesc *d, void *pkt, size_t len, time_t tleft)
! 103: {
! 104: ssize_t n;
! 105: struct tftphdr *t;
! 106:
! 107: errno = 0;
! 108:
! 109: n = readudp(d, pkt, len, tleft);
! 110:
! 111: if (n < 4)
! 112: return -1;
! 113:
! 114: t = (struct tftphdr *) pkt;
! 115: switch (ntohs(t->th_opcode)) {
! 116: case DATA:
! 117: if (htons(t->th_block) != d->xid) {
! 118: /*
! 119: * Expected block?
! 120: */
! 121: return -1;
! 122: }
! 123: if (d->xid == 1) {
! 124: /*
! 125: * First data packet from new port.
! 126: */
! 127: struct udphdr *uh;
! 128: uh = (struct udphdr *) pkt - 1;
! 129: d->destport = uh->uh_sport;
! 130: } /* else check uh_sport has not changed??? */
! 131: return (n - (t->th_data - (char *)t));
! 132: case ERROR:
! 133: if ((unsigned) ntohs(t->th_code) >= 8) {
! 134: printf("illegal tftp error %d\n", ntohs(t->th_code));
! 135: errno = EIO;
! 136: } else {
! 137: #ifdef DEBUG
! 138: printf("tftp-error %d\n", ntohs(t->th_code));
! 139: #endif
! 140: errno = tftperrors[ntohs(t->th_code)];
! 141: }
! 142: return -1;
! 143: default:
! 144: #ifdef DEBUG
! 145: printf("tftp type %d not handled\n", ntohs(t->th_opcode));
! 146: #endif
! 147: return -1;
! 148: }
! 149: }
! 150:
! 151: /* send request, expect first block (or error) */
! 152: int
! 153: tftp_makereq(struct tftp_handle *h)
! 154: {
! 155: struct {
! 156: u_char header[HEADER_SIZE];
! 157: struct tftphdr t;
! 158: u_char space[FNAME_SIZE + 6];
! 159: } wbuf;
! 160: char *wtail;
! 161: int l;
! 162: ssize_t res;
! 163: struct tftphdr *t;
! 164:
! 165: bzero(&wbuf, sizeof(wbuf));
! 166:
! 167: wbuf.t.th_opcode = htons((u_short) RRQ);
! 168: wtail = wbuf.t.th_stuff;
! 169: l = strlen(h->path);
! 170: bcopy(h->path, wtail, l + 1);
! 171: wtail += l + 1;
! 172: bcopy("octet", wtail, 6);
! 173: wtail += 6;
! 174:
! 175: t = &h->lastdata.t;
! 176:
! 177: /* h->iodesc->myport = htons(--tftpport); */
! 178: h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
! 179: h->iodesc->destport = htons(IPPORT_TFTP);
! 180: h->iodesc->xid = 1; /* expected block */
! 181:
! 182: res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
! 183: recvtftp, t, sizeof(*t) + RSPACE);
! 184:
! 185: if (res == -1)
! 186: return errno;
! 187:
! 188: h->currblock = 1;
! 189: h->validsize = res;
! 190: h->islastblock = 0;
! 191: if (res < SEGSIZE)
! 192: h->islastblock = 1; /* very short file */
! 193: return 0;
! 194: }
! 195:
! 196: /* ack block, expect next */
! 197: int
! 198: tftp_getnextblock(struct tftp_handle *h)
! 199: {
! 200: struct {
! 201: u_char header[HEADER_SIZE];
! 202: struct tftphdr t;
! 203: } wbuf;
! 204: char *wtail;
! 205: int res;
! 206: struct tftphdr *t;
! 207:
! 208: bzero(&wbuf, sizeof(wbuf));
! 209:
! 210: wbuf.t.th_opcode = htons((u_short) ACK);
! 211: wbuf.t.th_block = htons((u_short) h->currblock);
! 212: wtail = (char *) &wbuf.t.th_data;
! 213:
! 214: t = &h->lastdata.t;
! 215:
! 216: h->iodesc->xid = h->currblock + 1; /* expected block */
! 217:
! 218: res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
! 219: recvtftp, t, sizeof(*t) + RSPACE);
! 220:
! 221: if (res == -1) /* 0 is OK! */
! 222: return errno;
! 223:
! 224: h->currblock++;
! 225: h->validsize = res;
! 226: if (res < SEGSIZE)
! 227: h->islastblock = 1; /* EOF */
! 228: return 0;
! 229: }
! 230:
! 231: #ifndef TFTP_NOTERMINATE
! 232: void
! 233: tftp_terminate(struct tftp_handle *h)
! 234: {
! 235: struct {
! 236: u_char header[HEADER_SIZE];
! 237: struct tftphdr t;
! 238: } wbuf;
! 239: char *wtail;
! 240:
! 241: bzero(&wbuf, sizeof(wbuf));
! 242:
! 243: if (h->islastblock) {
! 244: wbuf.t.th_opcode = htons((u_short) ACK);
! 245: wbuf.t.th_block = htons((u_short) h->currblock);
! 246: } else {
! 247: wbuf.t.th_opcode = htons((u_short) ERROR);
! 248: wbuf.t.th_code = htons((u_short) ENOSPACE); /* ??? */
! 249: }
! 250: wtail = (char *) &wbuf.t.th_data;
! 251:
! 252: (void) sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
! 253: }
! 254: #endif
! 255:
! 256: int
! 257: tftp_open(char *path, struct open_file *f)
! 258: {
! 259: struct tftp_handle *tftpfile;
! 260: struct iodesc *io;
! 261: int res;
! 262:
! 263: tftpfile = (struct tftp_handle *) alloc(sizeof(*tftpfile));
! 264: if (tftpfile == NULL)
! 265: return ENOMEM;
! 266:
! 267: tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata));
! 268: io->destip = servip;
! 269: tftpfile->off = 0;
! 270: tftpfile->path = path; /* XXXXXXX we hope it's static */
! 271:
! 272: res = tftp_makereq(tftpfile);
! 273:
! 274: if (res) {
! 275: free(tftpfile, sizeof(*tftpfile));
! 276: return res;
! 277: }
! 278: f->f_fsdata = (void *) tftpfile;
! 279: return 0;
! 280: }
! 281:
! 282: int
! 283: tftp_read(struct open_file *f, void *addr, size_t size, size_t *resid)
! 284: {
! 285: struct tftp_handle *tftpfile;
! 286: #if !defined(LIBSA_NO_TWIDDLE)
! 287: static int tc = 0;
! 288: #endif
! 289: tftpfile = (struct tftp_handle *) f->f_fsdata;
! 290:
! 291: while (size > 0) {
! 292: int needblock;
! 293: size_t count;
! 294:
! 295: needblock = tftpfile->off / SEGSIZE + 1;
! 296:
! 297: if (tftpfile->currblock > needblock) { /* seek backwards */
! 298: #ifndef TFTP_NOTERMINATE
! 299: tftp_terminate(tftpfile);
! 300: #endif
! 301: /* Don't bother to check retval: it worked for open() */
! 302: tftp_makereq(tftpfile);
! 303: }
! 304:
! 305: while (tftpfile->currblock < needblock) {
! 306: int res;
! 307:
! 308: #if !defined(LIBSA_NO_TWIDDLE)
! 309: if ((tc++ % 16) == 0)
! 310: twiddle();
! 311: #endif
! 312: res = tftp_getnextblock(tftpfile);
! 313: if (res) { /* no answer */
! 314: #ifdef DEBUG
! 315: printf("tftp: read error (block %d->%d)\n",
! 316: tftpfile->currblock, needblock);
! 317: #endif
! 318: return res;
! 319: }
! 320: if (tftpfile->islastblock)
! 321: break;
! 322: }
! 323:
! 324: if (tftpfile->currblock == needblock) {
! 325: size_t offinblock, inbuffer;
! 326:
! 327: offinblock = tftpfile->off % SEGSIZE;
! 328:
! 329: inbuffer = tftpfile->validsize - offinblock;
! 330: if (inbuffer < 0) {
! 331: #ifdef DEBUG
! 332: printf("tftp: invalid offset %d\n",
! 333: tftpfile->off);
! 334: #endif
! 335: return EINVAL;
! 336: }
! 337: count = (size < inbuffer ? size : inbuffer);
! 338: bcopy(tftpfile->lastdata.t.th_data + offinblock,
! 339: addr, count);
! 340:
! 341: addr = (caddr_t)addr + count;
! 342: tftpfile->off += count;
! 343: size -= count;
! 344:
! 345: if ((tftpfile->islastblock) && (count == inbuffer))
! 346: break; /* EOF */
! 347: } else {
! 348: #ifdef DEBUG
! 349: printf("tftp: block %d not found\n", needblock);
! 350: #endif
! 351: return EINVAL;
! 352: }
! 353:
! 354: }
! 355:
! 356: if (resid != NULL)
! 357: *resid = size;
! 358: return 0;
! 359: }
! 360:
! 361: int
! 362: tftp_close(struct open_file *f)
! 363: {
! 364: struct tftp_handle *tftpfile;
! 365: tftpfile = (struct tftp_handle *) f->f_fsdata;
! 366:
! 367: #ifdef TFTP_NOTERMINATE
! 368: /* let it time out ... */
! 369: #else
! 370: tftp_terminate(tftpfile);
! 371: #endif
! 372:
! 373: free(tftpfile, sizeof(*tftpfile));
! 374: return 0;
! 375: }
! 376:
! 377: int
! 378: tftp_write(struct open_file *f, void *start, size_t size, size_t *resid)
! 379: {
! 380: return EROFS;
! 381: }
! 382:
! 383: int
! 384: tftp_stat(struct open_file *f, struct stat *sb)
! 385: {
! 386: struct tftp_handle *tftpfile;
! 387: tftpfile = (struct tftp_handle *) f->f_fsdata;
! 388:
! 389: sb->st_mode = 0444;
! 390: sb->st_nlink = 1;
! 391: sb->st_uid = 0;
! 392: sb->st_gid = 0;
! 393: sb->st_size = -1;
! 394:
! 395: return 0;
! 396: }
! 397:
! 398: off_t
! 399: tftp_seek(struct open_file *f, off_t offset, int where)
! 400: {
! 401: struct tftp_handle *tftpfile;
! 402: tftpfile = (struct tftp_handle *) f->f_fsdata;
! 403:
! 404: switch (where) {
! 405: case SEEK_SET:
! 406: tftpfile->off = offset;
! 407: break;
! 408: case SEEK_CUR:
! 409: tftpfile->off += offset;
! 410: break;
! 411: default:
! 412: errno = EOFFSET;
! 413: return -1;
! 414: }
! 415:
! 416: return (tftpfile->off);
! 417: }
! 418:
! 419: /*
! 420: * Not implemented.
! 421: */
! 422: #ifndef NO_READDIR
! 423: int
! 424: tftp_readdir(struct open_file *f, char *name)
! 425: {
! 426: return EROFS;
! 427: }
! 428: #endif
CVSweb