Home | History | Annotate | Line # | Download | only in libsa
tftp.c revision 1.8
      1 /*	$NetBSD: tftp.c,v 1.8 1999/11/11 20:23:17 thorpej 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 #ifdef _STANDALONE
     62 #include "/usr/include/arpa/tftp.h" /* XXX */
     63 #else
     64 #include <arpa/tftp.h>
     65 #endif
     66 
     67 #include "stand.h"
     68 #include "net.h"
     69 #include "netif.h"
     70 
     71 #include "tftp.h"
     72 
     73 extern struct in_addr servip;
     74 
     75 static int      tftpport = 2000;
     76 
     77 #define RSPACE 520		/* max data packet, rounded up */
     78 
     79 struct tftp_handle {
     80 	struct iodesc  *iodesc;
     81 	int             currblock;	/* contents of lastdata */
     82 	int             islastblock;	/* flag */
     83 	int             validsize;
     84 	int             off;
     85 	char           *path;	/* saved for re-requests */
     86 	struct {
     87 		u_char header[HEADER_SIZE];
     88 		struct tftphdr t;
     89 		u_char space[RSPACE];
     90 	} lastdata;
     91 };
     92 
     93 static int tftperrors[8] = {
     94 	0,			/* ??? */
     95 	ENOENT,
     96 	EPERM,
     97 	ENOSPC,
     98 	EINVAL,			/* ??? */
     99 	EINVAL,			/* ??? */
    100 	EEXIST,
    101 	EINVAL			/* ??? */
    102 };
    103 
    104 static ssize_t recvtftp __P((struct iodesc *, void *, size_t, time_t));
    105 static int tftp_makereq __P((struct tftp_handle *));
    106 static int tftp_getnextblock __P((struct tftp_handle *));
    107 #ifndef TFTP_NOTERMINATE
    108 static void tftp_terminate __P((struct tftp_handle *));
    109 #endif
    110 
    111 static ssize_t
    112 recvtftp(d, pkt, len, tleft)
    113 	register struct iodesc *d;
    114 	register void  *pkt;
    115 	register size_t len;
    116 	time_t          tleft;
    117 {
    118 	ssize_t n;
    119 	struct tftphdr *t;
    120 
    121 	errno = 0;
    122 
    123 	n = readudp(d, pkt, len, tleft);
    124 
    125 	if (n < 4)
    126 		return (-1);
    127 
    128 	t = (struct tftphdr *) pkt;
    129 	switch (ntohs(t->th_opcode)) {
    130 	case DATA:
    131 		if (htons(t->th_block) != d->xid) {
    132 			/*
    133 			 * Expected block?
    134 			 */
    135 			return (-1);
    136 		}
    137 		if (d->xid == 1) {
    138 			/*
    139 			 * First data packet from new port.
    140 			 */
    141 			register struct udphdr *uh;
    142 			uh = (struct udphdr *) pkt - 1;
    143 			d->destport = uh->uh_sport;
    144 		} /* else check uh_sport has not changed??? */
    145 		return (n - (t->th_data - (char *)t));
    146 	case ERROR:
    147 		if ((unsigned) ntohs(t->th_code) >= 8) {
    148 			printf("illegal tftp error %d\n", ntohs(t->th_code));
    149 			errno = EIO;
    150 		} else {
    151 #ifdef DEBUG
    152 			printf("tftp-error %d\n", ntohs(t->th_code));
    153 #endif
    154 			errno = tftperrors[ntohs(t->th_code)];
    155 		}
    156 		return (-1);
    157 	default:
    158 #ifdef DEBUG
    159 		printf("tftp type %d not handled\n", ntohs(t->th_opcode));
    160 #endif
    161 		return (-1);
    162 	}
    163 }
    164 
    165 /* send request, expect first block (or error) */
    166 static int
    167 tftp_makereq(h)
    168 	struct tftp_handle *h;
    169 {
    170 	struct {
    171 		u_char header[HEADER_SIZE];
    172 		struct tftphdr  t;
    173 		u_char space[FNAME_SIZE + 6];
    174 	} wbuf;
    175 	char           *wtail;
    176 	int             l;
    177 	ssize_t         res;
    178 	struct tftphdr *t;
    179 
    180 	wbuf.t.th_opcode = htons((u_short) RRQ);
    181 	wtail = wbuf.t.th_stuff;
    182 	l = strlen(h->path);
    183 	bcopy(h->path, wtail, l + 1);
    184 	wtail += l + 1;
    185 	bcopy("octet", wtail, 6);
    186 	wtail += 6;
    187 
    188 	t = &h->lastdata.t;
    189 
    190 	/* h->iodesc->myport = htons(--tftpport); */
    191 	h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
    192 	h->iodesc->destport = htons(IPPORT_TFTP);
    193 	h->iodesc->xid = 1;	/* expected block */
    194 
    195 	res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
    196 		       recvtftp, t, sizeof(*t) + RSPACE);
    197 
    198 	if (res == -1)
    199 		return (errno);
    200 
    201 	h->currblock = 1;
    202 	h->validsize = res;
    203 	h->islastblock = 0;
    204 	if (res < SEGSIZE)
    205 		h->islastblock = 1;	/* very short file */
    206 	return (0);
    207 }
    208 
    209 /* ack block, expect next */
    210 static int
    211 tftp_getnextblock(h)
    212 	struct tftp_handle *h;
    213 {
    214 	struct {
    215 		u_char header[HEADER_SIZE];
    216 		struct tftphdr t;
    217 	} wbuf;
    218 	char           *wtail;
    219 	int             res;
    220 	struct tftphdr *t;
    221 
    222 	wbuf.t.th_opcode = htons((u_short) ACK);
    223 	wbuf.t.th_block = htons((u_short) h->currblock);
    224 	wtail = (char *) &wbuf.t.th_data;
    225 
    226 	t = &h->lastdata.t;
    227 
    228 	h->iodesc->xid = h->currblock + 1;	/* expected block */
    229 
    230 	res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
    231 		       recvtftp, t, sizeof(*t) + RSPACE);
    232 
    233 	if (res == -1)		/* 0 is OK! */
    234 		return (errno);
    235 
    236 	h->currblock++;
    237 	h->validsize = res;
    238 	if (res < SEGSIZE)
    239 		h->islastblock = 1;	/* EOF */
    240 	return (0);
    241 }
    242 
    243 #ifndef TFTP_NOTERMINATE
    244 static void
    245 tftp_terminate(h)
    246 	struct tftp_handle *h;
    247 {
    248 	struct {
    249 		u_char header[HEADER_SIZE];
    250 		struct tftphdr t;
    251 	} wbuf;
    252 	char           *wtail;
    253 
    254 	if (h->islastblock) {
    255 		wbuf.t.th_opcode = htons((u_short) ACK);
    256 		wbuf.t.th_block = htons((u_short) h->currblock);
    257 	} else {
    258 		wbuf.t.th_opcode = htons((u_short) ERROR);
    259 		wbuf.t.th_code = htons((u_short) ENOSPACE); /* ??? */
    260 	}
    261 	wtail = (char *) &wbuf.t.th_data;
    262 
    263 	(void) sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
    264 }
    265 #endif
    266 
    267 int
    268 tftp_open(path, f)
    269 	char           *path;
    270 	struct open_file *f;
    271 {
    272 	struct tftp_handle *tftpfile;
    273 	struct iodesc  *io;
    274 	int             res;
    275 
    276 	tftpfile = (struct tftp_handle *) alloc(sizeof(*tftpfile));
    277 	if (!tftpfile)
    278 		return (ENOMEM);
    279 
    280 	tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata));
    281 	io->destip = servip;
    282 	tftpfile->off = 0;
    283 	tftpfile->path = path;	/* XXXXXXX we hope it's static */
    284 
    285 	res = tftp_makereq(tftpfile);
    286 
    287 	if (res) {
    288 		free(tftpfile, sizeof(*tftpfile));
    289 		return (res);
    290 	}
    291 	f->f_fsdata = (void *) tftpfile;
    292 	return (0);
    293 }
    294 
    295 int
    296 tftp_read(f, addr, size, resid)
    297 	struct open_file *f;
    298 	void           *addr;
    299 	size_t          size;
    300 	size_t         *resid;	/* out */
    301 {
    302 	struct tftp_handle *tftpfile;
    303 #if !defined(LIBSA_NO_TWIDDLE)
    304 	static int      tc = 0;
    305 #endif
    306 	tftpfile = (struct tftp_handle *) f->f_fsdata;
    307 
    308 	while (size > 0) {
    309 		int needblock, count;
    310 
    311 #if !defined(LIBSA_NO_TWIDDLE)
    312 		if (!(tc++ % 16))
    313 			twiddle();
    314 #endif
    315 
    316 		needblock = tftpfile->off / SEGSIZE + 1;
    317 
    318 		if (tftpfile->currblock > needblock) {	/* seek backwards */
    319 #ifndef TFTP_NOTERMINATE
    320 			tftp_terminate(tftpfile);
    321 #endif
    322 			tftp_makereq(tftpfile);	/* no error check, it worked
    323 						 * for open */
    324 		}
    325 
    326 		while (tftpfile->currblock < needblock) {
    327 			int res;
    328 
    329 			res = tftp_getnextblock(tftpfile);
    330 			if (res) {	/* no answer */
    331 #ifdef DEBUG
    332 				printf("tftp: read error (block %d->%d)\n",
    333 				       tftpfile->currblock, needblock);
    334 #endif
    335 				return (res);
    336 			}
    337 			if (tftpfile->islastblock)
    338 				break;
    339 		}
    340 
    341 		if (tftpfile->currblock == needblock) {
    342 			int offinblock, inbuffer;
    343 
    344 			offinblock = tftpfile->off % SEGSIZE;
    345 
    346 			inbuffer = tftpfile->validsize - offinblock;
    347 			if (inbuffer < 0) {
    348 #ifdef DEBUG
    349 				printf("tftp: invalid offset %d\n",
    350 				    tftpfile->off);
    351 #endif
    352 				return (EINVAL);
    353 			}
    354 			count = (size < inbuffer ? size : inbuffer);
    355 			bcopy(tftpfile->lastdata.t.th_data + offinblock,
    356 			    addr, count);
    357 
    358 			addr = (caddr_t)addr + count;
    359 			tftpfile->off += count;
    360 			size -= count;
    361 
    362 			if ((tftpfile->islastblock) && (count == inbuffer))
    363 				break;	/* EOF */
    364 		} else {
    365 #ifdef DEBUG
    366 			printf("tftp: block %d not found\n", needblock);
    367 #endif
    368 			return (EINVAL);
    369 		}
    370 
    371 	}
    372 
    373 	if (resid)
    374 		*resid = size;
    375 	return (0);
    376 }
    377 
    378 int
    379 tftp_close(f)
    380 	struct open_file *f;
    381 {
    382 	struct tftp_handle *tftpfile;
    383 	tftpfile = (struct tftp_handle *) f->f_fsdata;
    384 
    385 #ifdef TFTP_NOTERMINATE
    386 	/* let it time out ... */
    387 #else
    388 	tftp_terminate(tftpfile);
    389 #endif
    390 
    391 	free(tftpfile, sizeof(*tftpfile));
    392 	return (0);
    393 }
    394 
    395 int
    396 tftp_write(f, start, size, resid)
    397 	struct open_file *f;
    398 	void           *start;
    399 	size_t          size;
    400 	size_t         *resid;	/* out */
    401 {
    402 	return (EROFS);
    403 }
    404 
    405 int
    406 tftp_stat(f, sb)
    407 	struct open_file *f;
    408 	struct stat    *sb;
    409 {
    410 	struct tftp_handle *tftpfile;
    411 	tftpfile = (struct tftp_handle *) f->f_fsdata;
    412 
    413 	sb->st_mode = 0444;
    414 	sb->st_nlink = 1;
    415 	sb->st_uid = 0;
    416 	sb->st_gid = 0;
    417 	sb->st_size = -1;
    418 	return (0);
    419 }
    420 
    421 off_t
    422 tftp_seek(f, offset, where)
    423 	struct open_file *f;
    424 	off_t           offset;
    425 	int             where;
    426 {
    427 	struct tftp_handle *tftpfile;
    428 	tftpfile = (struct tftp_handle *) f->f_fsdata;
    429 
    430 	switch (where) {
    431 	case SEEK_SET:
    432 		tftpfile->off = offset;
    433 		break;
    434 	case SEEK_CUR:
    435 		tftpfile->off += offset;
    436 		break;
    437 	default:
    438 		errno = EOFFSET;
    439 		return (-1);
    440 	}
    441 	return (tftpfile->off);
    442 }
    443