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