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