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