Home | History | Annotate | Line # | Download | only in libsa
tftp.c revision 1.16
      1 /*	$NetBSD: tftp.c,v 1.16 2003/08/31 22:40:49 fvdl 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 
     66 #include "tftp.h"
     67 
     68 extern struct in_addr servip;
     69 
     70 static int      tftpport = 2000;
     71 
     72 #define RSPACE 520		/* max data packet, rounded up */
     73 
     74 struct tftp_handle {
     75 	struct iodesc  *iodesc;
     76 	int             currblock;	/* contents of lastdata */
     77 	int             islastblock;	/* flag */
     78 	int             validsize;
     79 	int             off;
     80 	const char     *path;	/* saved for re-requests */
     81 	struct {
     82 		u_char header[HEADER_SIZE];
     83 		struct tftphdr t;
     84 		u_char space[RSPACE];
     85 	} lastdata;
     86 };
     87 
     88 static const int tftperrors[8] = {
     89 	0,			/* ??? */
     90 	ENOENT,
     91 	EPERM,
     92 	ENOSPC,
     93 	EINVAL,			/* ??? */
     94 	EINVAL,			/* ??? */
     95 	EEXIST,
     96 	EINVAL			/* ??? */
     97 };
     98 
     99 static ssize_t recvtftp __P((struct iodesc *, void *, size_t, time_t));
    100 static int tftp_makereq __P((struct tftp_handle *));
    101 static int tftp_getnextblock __P((struct tftp_handle *));
    102 #ifndef TFTP_NOTERMINATE
    103 static void tftp_terminate __P((struct tftp_handle *));
    104 #endif
    105 
    106 static ssize_t
    107 recvtftp(d, pkt, len, tleft)
    108 	struct iodesc *d;
    109 	void  *pkt;
    110 	size_t len;
    111 	time_t          tleft;
    112 {
    113 	ssize_t n;
    114 	struct tftphdr *t;
    115 
    116 	errno = 0;
    117 
    118 	n = readudp(d, pkt, len, tleft);
    119 
    120 	if (n < 4)
    121 		return (-1);
    122 
    123 	t = (struct tftphdr *) pkt;
    124 	switch (ntohs(t->th_opcode)) {
    125 	case DATA:
    126 		if (htons(t->th_block) != d->xid) {
    127 			/*
    128 			 * Expected block?
    129 			 */
    130 			return (-1);
    131 		}
    132 		if (d->xid == 1) {
    133 			/*
    134 			 * First data packet from new port.
    135 			 */
    136 			struct udphdr *uh;
    137 			uh = (struct udphdr *) pkt - 1;
    138 			d->destport = uh->uh_sport;
    139 		} /* else check uh_sport has not changed??? */
    140 		return (n - (t->th_data - (char *)t));
    141 	case ERROR:
    142 		if ((unsigned) ntohs(t->th_code) >= 8) {
    143 			printf("illegal tftp error %d\n", ntohs(t->th_code));
    144 			errno = EIO;
    145 		} else {
    146 #ifdef DEBUG
    147 			printf("tftp-error %d\n", ntohs(t->th_code));
    148 #endif
    149 			errno = tftperrors[ntohs(t->th_code)];
    150 		}
    151 		return (-1);
    152 	default:
    153 #ifdef DEBUG
    154 		printf("tftp type %d not handled\n", ntohs(t->th_opcode));
    155 #endif
    156 		return (-1);
    157 	}
    158 }
    159 
    160 /* send request, expect first block (or error) */
    161 static int
    162 tftp_makereq(h)
    163 	struct tftp_handle *h;
    164 {
    165 	struct {
    166 		u_char header[HEADER_SIZE];
    167 		struct tftphdr  t;
    168 		u_char space[FNAME_SIZE + 6];
    169 	} wbuf;
    170 	char           *wtail;
    171 	int             l;
    172 	ssize_t         res;
    173 	struct tftphdr *t;
    174 
    175 	wbuf.t.th_opcode = htons((u_short) RRQ);
    176 	wtail = wbuf.t.th_stuff;
    177 	l = strlen(h->path);
    178 	bcopy(h->path, wtail, l + 1);
    179 	wtail += l + 1;
    180 	bcopy("octet", wtail, 6);
    181 	wtail += 6;
    182 
    183 	t = &h->lastdata.t;
    184 
    185 	/* h->iodesc->myport = htons(--tftpport); */
    186 	h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
    187 	h->iodesc->destport = htons(IPPORT_TFTP);
    188 	h->iodesc->xid = 1;	/* expected block */
    189 
    190 	res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
    191 		       recvtftp, t, sizeof(*t) + RSPACE);
    192 
    193 	if (res == -1)
    194 		return (errno);
    195 
    196 	h->currblock = 1;
    197 	h->validsize = res;
    198 	h->islastblock = 0;
    199 	if (res < SEGSIZE)
    200 		h->islastblock = 1;	/* very short file */
    201 	return (0);
    202 }
    203 
    204 /* ack block, expect next */
    205 static int
    206 tftp_getnextblock(h)
    207 	struct tftp_handle *h;
    208 {
    209 	struct {
    210 		u_char header[HEADER_SIZE];
    211 		struct tftphdr t;
    212 	} wbuf;
    213 	char           *wtail;
    214 	int             res;
    215 	struct tftphdr *t;
    216 
    217 	wbuf.t.th_opcode = htons((u_short) ACK);
    218 	wbuf.t.th_block = htons((u_short) h->currblock);
    219 	wtail = (char *) &wbuf.t.th_data;
    220 
    221 	t = &h->lastdata.t;
    222 
    223 	h->iodesc->xid = h->currblock + 1;	/* expected block */
    224 
    225 	res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
    226 		       recvtftp, t, sizeof(*t) + RSPACE);
    227 
    228 	if (res == -1)		/* 0 is OK! */
    229 		return (errno);
    230 
    231 	h->currblock++;
    232 	h->validsize = res;
    233 	if (res < SEGSIZE)
    234 		h->islastblock = 1;	/* EOF */
    235 	return (0);
    236 }
    237 
    238 #ifndef TFTP_NOTERMINATE
    239 static void
    240 tftp_terminate(h)
    241 	struct tftp_handle *h;
    242 {
    243 	struct {
    244 		u_char header[HEADER_SIZE];
    245 		struct tftphdr t;
    246 	} wbuf;
    247 	char           *wtail;
    248 
    249 	if (h->islastblock) {
    250 		wbuf.t.th_opcode = htons((u_short) ACK);
    251 		wbuf.t.th_block = htons((u_short) h->currblock);
    252 	} else {
    253 		wbuf.t.th_opcode = htons((u_short) ERROR);
    254 		wbuf.t.th_code = htons((u_short) ENOSPACE); /* ??? */
    255 	}
    256 	wtail = (char *) &wbuf.t.th_data;
    257 
    258 	(void) sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
    259 }
    260 #endif
    261 
    262 int
    263 tftp_open(path, f)
    264 	const char           *path;
    265 	struct open_file *f;
    266 {
    267 	struct tftp_handle *tftpfile;
    268 	struct iodesc  *io;
    269 	int             res;
    270 
    271 	tftpfile = (struct tftp_handle *) alloc(sizeof(*tftpfile));
    272 	if (!tftpfile)
    273 		return (ENOMEM);
    274 
    275 	tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata));
    276 	io->destip = servip;
    277 	tftpfile->off = 0;
    278 	tftpfile->path = path;	/* XXXXXXX we hope it's static */
    279 
    280 	res = tftp_makereq(tftpfile);
    281 
    282 	if (res) {
    283 		free(tftpfile, sizeof(*tftpfile));
    284 		return (res);
    285 	}
    286 	f->f_fsdata = (void *) tftpfile;
    287 	return (0);
    288 }
    289 
    290 int
    291 tftp_read(f, addr, size, resid)
    292 	struct open_file *f;
    293 	void           *addr;
    294 	size_t          size;
    295 	size_t         *resid;	/* out */
    296 {
    297 	struct tftp_handle *tftpfile;
    298 #if !defined(LIBSA_NO_TWIDDLE)
    299 	static int      tc = 0;
    300 #endif
    301 	tftpfile = (struct tftp_handle *) f->f_fsdata;
    302 
    303 	while (size > 0) {
    304 		int needblock;
    305 		size_t 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 			size_t 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