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