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