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