138451Smsmith/*	$NetBSD: tftp.c,v 1.4 1997/09/17 16:57:07 drochner Exp $	 */
238451Smsmith
338451Smsmith/*
438451Smsmith * Copyright (c) 1996
538451Smsmith *	Matthias Drochner.  All rights reserved.
638451Smsmith *
738451Smsmith * Redistribution and use in source and binary forms, with or without
838451Smsmith * modification, are permitted provided that the following conditions
938451Smsmith * are met:
1038451Smsmith * 1. Redistributions of source code must retain the above copyright
1138451Smsmith *    notice, this list of conditions and the following disclaimer.
1238451Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1338451Smsmith *    notice, this list of conditions and the following disclaimer in the
1438451Smsmith *    documentation and/or other materials provided with the distribution.
1538451Smsmith * 3. All advertising materials mentioning features or use of this software
1638451Smsmith *    must display the following acknowledgement:
1738451Smsmith *	This product includes software developed for the NetBSD Project
1838451Smsmith *	by Matthias Drochner.
1938451Smsmith * 4. The name of the author may not be used to endorse or promote products
2038451Smsmith *    derived from this software without specific prior written permission.
2138451Smsmith *
2238451Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2338451Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2438451Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2538451Smsmith * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2638451Smsmith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2738451Smsmith * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2838451Smsmith * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2938451Smsmith * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3038451Smsmith * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3138451Smsmith * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3238451Smsmith */
3338451Smsmith
3484221Sdillon#include <sys/cdefs.h>
3584221Sdillon__FBSDID("$FreeBSD$");
3684221Sdillon
3738451Smsmith/*
3838451Smsmith * Simple TFTP implementation for libsa.
3938451Smsmith * Assumes:
4038451Smsmith *  - socket descriptor (int) at open_file->f_devdata
4138451Smsmith *  - server host IP in global servip
4238451Smsmith * Restrictions:
4338451Smsmith *  - read only
4438451Smsmith *  - lseek only with SEEK_SET or SEEK_CUR
4538451Smsmith *  - no big time differences between transfers (<tftp timeout)
4638451Smsmith */
4738451Smsmith
4838451Smsmith#include <sys/types.h>
4938451Smsmith#include <sys/stat.h>
5038451Smsmith#include <netinet/in.h>
5138451Smsmith#include <netinet/udp.h>
5238451Smsmith#include <netinet/in_systm.h>
5338451Smsmith#include <arpa/tftp.h>
5438451Smsmith
5538451Smsmith#include <string.h>
5638451Smsmith
5738451Smsmith#include "stand.h"
5838451Smsmith#include "net.h"
5938451Smsmith#include "netif.h"
6038451Smsmith
6138451Smsmith#include "tftp.h"
6238451Smsmith
63223124Srodrigcstruct tftp_handle;
64223124Srodrigc
6539468Smsmithstatic int	tftp_open(const char *path, struct open_file *f);
6638451Smsmithstatic int	tftp_close(struct open_file *f);
67223488Srodrigcstatic int	tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len);
6838451Smsmithstatic int	tftp_read(struct open_file *f, void *buf, size_t size, size_t *resid);
6938451Smsmithstatic int	tftp_write(struct open_file *f, void *buf, size_t size, size_t *resid);
7038451Smsmithstatic off_t	tftp_seek(struct open_file *f, off_t offset, int where);
71223124Srodrigcstatic int	tftp_set_blksize(struct tftp_handle *h, const char *str);
7238451Smsmithstatic int	tftp_stat(struct open_file *f, struct stat *sb);
73223488Srodrigcstatic ssize_t sendrecv_tftp(struct tftp_handle *h,
74223124Srodrigc    ssize_t (*sproc)(struct iodesc *, void *, size_t),
75223124Srodrigc    void *sbuf, size_t ssize,
76223124Srodrigc    ssize_t (*rproc)(struct tftp_handle *h, void *, ssize_t, time_t, unsigned short *),
77223124Srodrigc    void *rbuf, size_t rsize, unsigned short *rtype);
7838451Smsmith
7938451Smsmithstruct fs_ops tftp_fsops = {
8059766Sjlemon	"tftp",
8159766Sjlemon	tftp_open,
8259766Sjlemon	tftp_close,
8359766Sjlemon	tftp_read,
8459766Sjlemon	tftp_write,
8559766Sjlemon	tftp_seek,
8659766Sjlemon	tftp_stat,
8759766Sjlemon	null_readdir
8838451Smsmith};
8938451Smsmith
9038451Smsmithextern struct in_addr servip;
9138451Smsmith
9238451Smsmithstatic int      tftpport = 2000;
93223124Srodrigcstatic int	is_open = 0;
9438451Smsmith
95223124Srodrigc/*
96223488Srodrigc * The legacy TFTP_BLKSIZE value was SEGSIZE(512).
97223124Srodrigc * TFTP_REQUESTED_BLKSIZE of 1428 is (Ethernet MTU, less the TFTP, UDP and
98223124Srodrigc * IP header lengths).
99223124Srodrigc */
100223124Srodrigc#define TFTP_REQUESTED_BLKSIZE 1428
10138451Smsmith
102223124Srodrigc/*
103223124Srodrigc * Choose a blksize big enough so we can test with Ethernet
104223124Srodrigc * Jumbo frames in the future.
105223488Srodrigc */
106223124Srodrigc#define TFTP_MAX_BLKSIZE 9008
107223124Srodrigc
10838451Smsmithstruct tftp_handle {
10938451Smsmith	struct iodesc  *iodesc;
11038451Smsmith	int             currblock;	/* contents of lastdata */
11138451Smsmith	int             islastblock;	/* flag */
11238451Smsmith	int             validsize;
11338451Smsmith	int             off;
11438451Smsmith	char           *path;	/* saved for re-requests */
115223124Srodrigc	unsigned int	tftp_blksize;
116223488Srodrigc	unsigned long	tftp_tsize;
11738451Smsmith	struct {
11838451Smsmith		u_char header[HEADER_SIZE];
11938451Smsmith		struct tftphdr t;
120223124Srodrigc		u_char space[TFTP_MAX_BLKSIZE];
121223128Srodrigc	} __packed __aligned(4) lastdata;
12238451Smsmith};
12338451Smsmith
124223488Srodrigc#define	TFTP_MAX_ERRCODE EOPTNEG
125223488Srodrigcstatic const int tftperrors[TFTP_MAX_ERRCODE + 1] = {
12638451Smsmith	0,			/* ??? */
12738451Smsmith	ENOENT,
12838451Smsmith	EPERM,
12938451Smsmith	ENOSPC,
13038451Smsmith	EINVAL,			/* ??? */
13138451Smsmith	EINVAL,			/* ??? */
13238451Smsmith	EEXIST,
133223488Srodrigc	EINVAL,			/* ??? */
134223488Srodrigc	EINVAL,			/* Option negotiation failed. */
13538451Smsmith};
13638451Smsmith
137223488Srodrigcstatic int  tftp_getnextblock(struct tftp_handle *h);
138223488Srodrigc
139223488Srodrigc/* send error message back. */
140223488Srodrigcstatic void
141223488Srodrigctftp_senderr(struct tftp_handle *h, u_short errcode, const char *msg)
142223488Srodrigc{
143223488Srodrigc	struct {
144223488Srodrigc		u_char header[HEADER_SIZE];
145223488Srodrigc		struct tftphdr  t;
146223488Srodrigc		u_char space[63]; /* +1 from t */
147223488Srodrigc	} __packed __aligned(4) wbuf;
148223488Srodrigc	char           *wtail;
149223488Srodrigc	int             len;
150223488Srodrigc
151223488Srodrigc	len = strlen(msg);
152223488Srodrigc	if (len > sizeof(wbuf.space))
153223488Srodrigc		len = sizeof(wbuf.space);
154223488Srodrigc
155223488Srodrigc	wbuf.t.th_opcode = htons((u_short) ERROR);
156223488Srodrigc	wbuf.t.th_code   = htons(errcode);
157223488Srodrigc
158223488Srodrigc	wtail = wbuf.t.th_msg;
159223488Srodrigc	bcopy(msg, wtail, len);
160223488Srodrigc	wtail[len] = '\0';
161223488Srodrigc	wtail += len + 1;
162223488Srodrigc
163223488Srodrigc	sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
164223488Srodrigc}
165223488Srodrigc
166223488Srodrigcstatic void
167223488Srodrigctftp_sendack(struct tftp_handle *h)
168223488Srodrigc{
169223488Srodrigc	struct {
170223488Srodrigc		u_char header[HEADER_SIZE];
171223488Srodrigc		struct tftphdr  t;
172223488Srodrigc	} __packed __aligned(4) wbuf;
173223488Srodrigc	char           *wtail;
174223488Srodrigc
175223488Srodrigc	wbuf.t.th_opcode = htons((u_short) ACK);
176223488Srodrigc	wtail = (char *) &wbuf.t.th_block;
177223488Srodrigc	wbuf.t.th_block = htons((u_short) h->currblock);
178223488Srodrigc	wtail += 2;
179223488Srodrigc
180223488Srodrigc	sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
181223488Srodrigc}
182223488Srodrigc
183223488Srodrigcstatic ssize_t
184223124Srodrigcrecvtftp(struct tftp_handle *h, void *pkt, ssize_t len, time_t tleft,
185223124Srodrigc    unsigned short *rtype)
18638451Smsmith{
187223124Srodrigc	struct iodesc *d = h->iodesc;
18838451Smsmith	struct tftphdr *t;
18938451Smsmith
19079034Smikeh	errno = 0;
19179034Smikeh
19238451Smsmith	len = readudp(d, pkt, len, tleft);
19338451Smsmith
19477369Smsmith	if (len < 4)
19538451Smsmith		return (-1);
19638451Smsmith
19738451Smsmith	t = (struct tftphdr *) pkt;
198223124Srodrigc	*rtype = ntohs(t->th_opcode);
19938451Smsmith	switch (ntohs(t->th_opcode)) {
20038451Smsmith	case DATA: {
20138451Smsmith		int got;
20238451Smsmith
20338451Smsmith		if (htons(t->th_block) != d->xid) {
20438451Smsmith			/*
20538451Smsmith			 * Expected block?
20638451Smsmith			 */
20738451Smsmith			return (-1);
20838451Smsmith		}
20938451Smsmith		if (d->xid == 1) {
21038451Smsmith			/*
21138451Smsmith			 * First data packet from new port.
21238451Smsmith			 */
21392913Sobrien			struct udphdr *uh;
21438451Smsmith			uh = (struct udphdr *) pkt - 1;
21538451Smsmith			d->destport = uh->uh_sport;
21638451Smsmith		} /* else check uh_sport has not changed??? */
21738451Smsmith		got = len - (t->th_data - (char *) t);
21838451Smsmith		return got;
21938451Smsmith	}
22038451Smsmith	case ERROR:
221223488Srodrigc		if ((unsigned) ntohs(t->th_code) > TFTP_MAX_ERRCODE) {
22238451Smsmith			printf("illegal tftp error %d\n", ntohs(t->th_code));
22338451Smsmith			errno = EIO;
22438451Smsmith		} else {
225221366Srodrigc#ifdef TFTP_DEBUG
22638451Smsmith			printf("tftp-error %d\n", ntohs(t->th_code));
22738451Smsmith#endif
22838451Smsmith			errno = tftperrors[ntohs(t->th_code)];
22938451Smsmith		}
23038451Smsmith		return (-1);
231223124Srodrigc	case OACK: {
232223124Srodrigc		struct udphdr *uh;
233223488Srodrigc		int tftp_oack_len;
234223488Srodrigc
235223488Srodrigc		/*
236223488Srodrigc		 * Unexpected OACK. TFTP transfer already in progress.
237223488Srodrigc		 * Drop the pkt.
238223488Srodrigc		 */
239223488Srodrigc		if (d->xid != 1) {
240223488Srodrigc			return (-1);
241223488Srodrigc		}
242223488Srodrigc
243223124Srodrigc		/*
244223488Srodrigc		 * Remember which port this OACK came from, because we need
245223488Srodrigc		 * to send the ACK or errors back to it.
246223124Srodrigc		 */
247223124Srodrigc		uh = (struct udphdr *) pkt - 1;
248223124Srodrigc		d->destport = uh->uh_sport;
249223488Srodrigc
250223488Srodrigc		/* Parse options ACK-ed by the server. */
251223488Srodrigc		tftp_oack_len = len - sizeof(t->th_opcode);
252223488Srodrigc		if (tftp_parse_oack(h, t->th_u.tu_stuff, tftp_oack_len) != 0) {
253223488Srodrigc			tftp_senderr(h, EOPTNEG, "Malformed OACK");
254223488Srodrigc			errno = EIO;
255223488Srodrigc			return (-1);
256223488Srodrigc		}
257223124Srodrigc		return (0);
258223124Srodrigc	}
25938451Smsmith	default:
260221366Srodrigc#ifdef TFTP_DEBUG
26138451Smsmith		printf("tftp type %d not handled\n", ntohs(t->th_opcode));
26238451Smsmith#endif
26338451Smsmith		return (-1);
26438451Smsmith	}
26538451Smsmith}
26638451Smsmith
26738451Smsmith/* send request, expect first block (or error) */
268223488Srodrigcstatic int
269221358Srodrigctftp_makereq(struct tftp_handle *h)
27038451Smsmith{
27138451Smsmith	struct {
27238451Smsmith		u_char header[HEADER_SIZE];
27338451Smsmith		struct tftphdr  t;
27438451Smsmith		u_char space[FNAME_SIZE + 6];
275223128Srodrigc	} __packed __aligned(4) wbuf;
27638451Smsmith	char           *wtail;
27738451Smsmith	int             l;
27838451Smsmith	ssize_t         res;
27938451Smsmith	struct tftphdr *t;
280223124Srodrigc	char *tftp_blksize = NULL;
281223124Srodrigc	int blksize_l;
282223124Srodrigc	unsigned short rtype = 0;
28338451Smsmith
284223124Srodrigc	/*
285223124Srodrigc	 * Allow overriding default TFTP block size by setting
286223124Srodrigc	 * a tftp.blksize environment variable.
287223124Srodrigc	 */
288223124Srodrigc	if ((tftp_blksize = getenv("tftp.blksize")) != NULL) {
289223124Srodrigc		tftp_set_blksize(h, tftp_blksize);
290223124Srodrigc	}
291223124Srodrigc
29238451Smsmith	wbuf.t.th_opcode = htons((u_short) RRQ);
29338451Smsmith	wtail = wbuf.t.th_stuff;
29438451Smsmith	l = strlen(h->path);
295228798Sed#ifdef TFTP_PREPEND_PATH
296228798Sed	if (l > FNAME_SIZE - (sizeof(TFTP_PREPEND_PATH) - 1))
297228798Sed		return (ENAMETOOLONG);
298228798Sed	bcopy(TFTP_PREPEND_PATH, wtail, sizeof(TFTP_PREPEND_PATH) - 1);
299228798Sed	wtail += sizeof(TFTP_PREPEND_PATH) - 1;
300228798Sed#else
301223124Srodrigc	if (l > FNAME_SIZE)
302223124Srodrigc		return (ENAMETOOLONG);
303228798Sed#endif
30438451Smsmith	bcopy(h->path, wtail, l + 1);
30538451Smsmith	wtail += l + 1;
30638451Smsmith	bcopy("octet", wtail, 6);
30738451Smsmith	wtail += 6;
308223124Srodrigc	bcopy("blksize", wtail, 8);
309223124Srodrigc	wtail += 8;
310223124Srodrigc	blksize_l = sprintf(wtail, "%d", h->tftp_blksize);
311223124Srodrigc	wtail += blksize_l + 1;
312223124Srodrigc	bcopy("tsize", wtail, 6);
313223124Srodrigc	wtail += 6;
314223124Srodrigc	bcopy("0", wtail, 2);
315223124Srodrigc	wtail += 2;
31638451Smsmith
31738451Smsmith	t = &h->lastdata.t;
31838451Smsmith
31938451Smsmith	/* h->iodesc->myport = htons(--tftpport); */
32038451Smsmith	h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
32138451Smsmith	h->iodesc->destport = htons(IPPORT_TFTP);
32238451Smsmith	h->iodesc->xid = 1;	/* expected block */
32338451Smsmith
324223488Srodrigc	h->currblock = 0;
325223488Srodrigc	h->islastblock = 0;
326223488Srodrigc	h->validsize = 0;
327223488Srodrigc
328223124Srodrigc	res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
329223124Srodrigc		       &recvtftp, t, sizeof(*t) + h->tftp_blksize, &rtype);
33038451Smsmith
331223488Srodrigc	if (rtype == OACK)
332223488Srodrigc		return (tftp_getnextblock(h));
33338451Smsmith
334223488Srodrigc	/* Server ignored our blksize request, revert to TFTP default. */
335223488Srodrigc	h->tftp_blksize = SEGSIZE;
336223488Srodrigc
337223124Srodrigc	switch (rtype) {
338223124Srodrigc		case DATA: {
339223124Srodrigc			h->currblock = 1;
340223124Srodrigc			h->validsize = res;
341223124Srodrigc			h->islastblock = 0;
342223488Srodrigc			if (res < h->tftp_blksize) {
343223124Srodrigc				h->islastblock = 1;	/* very short file */
344223488Srodrigc				tftp_sendack(h);
345223488Srodrigc			}
346223124Srodrigc			return (0);
347223124Srodrigc		}
348223124Srodrigc		case ERROR:
349223124Srodrigc		default:
350223124Srodrigc			return (errno);
351223124Srodrigc	}
352223124Srodrigc
35338451Smsmith}
35438451Smsmith
35538451Smsmith/* ack block, expect next */
35638451Smsmithstatic int
357221358Srodrigctftp_getnextblock(struct tftp_handle *h)
35838451Smsmith{
35938451Smsmith	struct {
36038451Smsmith		u_char header[HEADER_SIZE];
36138451Smsmith		struct tftphdr t;
362223128Srodrigc	} __packed __aligned(4) wbuf;
36338451Smsmith	char           *wtail;
36438451Smsmith	int             res;
36538451Smsmith	struct tftphdr *t;
366223124Srodrigc	unsigned short rtype = 0;
36738451Smsmith	wbuf.t.th_opcode = htons((u_short) ACK);
36838451Smsmith	wtail = (char *) &wbuf.t.th_block;
36938451Smsmith	wbuf.t.th_block = htons((u_short) h->currblock);
37038451Smsmith	wtail += 2;
37138451Smsmith
37238451Smsmith	t = &h->lastdata.t;
37338451Smsmith
37438451Smsmith	h->iodesc->xid = h->currblock + 1;	/* expected block */
37538451Smsmith
376223124Srodrigc	res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
377223124Srodrigc		       &recvtftp, t, sizeof(*t) + h->tftp_blksize, &rtype);
37838451Smsmith
37938451Smsmith	if (res == -1)		/* 0 is OK! */
38038451Smsmith		return (errno);
38138451Smsmith
38238451Smsmith	h->currblock++;
38338451Smsmith	h->validsize = res;
384223124Srodrigc	if (res < h->tftp_blksize)
38538451Smsmith		h->islastblock = 1;	/* EOF */
386223124Srodrigc
387223124Srodrigc	if (h->islastblock == 1) {
388223124Srodrigc		/* Send an ACK for the last block */
389223124Srodrigc		wbuf.t.th_block = htons((u_short) h->currblock);
390223124Srodrigc		sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t);
391223124Srodrigc	}
392223124Srodrigc
39338451Smsmith	return (0);
39438451Smsmith}
39538451Smsmith
396223488Srodrigcstatic int
397221358Srodrigctftp_open(const char *path, struct open_file *f)
39838451Smsmith{
39938451Smsmith	struct tftp_handle *tftpfile;
40038451Smsmith	struct iodesc  *io;
40138451Smsmith	int             res;
40238451Smsmith
403256244Skan	if (strcmp(f->f_dev->dv_name, "net") != 0) {
404256244Skan#ifdef __i386__
405256244Skan		if (strcmp(f->f_dev->dv_name, "pxe") != 0)
406256244Skan			return (EINVAL);
407256244Skan#else
40899558Sjake		return (EINVAL);
40999558Sjake#endif
410256244Skan	}
411111776Smarcel
412223124Srodrigc	if (is_open)
413223124Srodrigc		return (EBUSY);
414223124Srodrigc
41538451Smsmith	tftpfile = (struct tftp_handle *) malloc(sizeof(*tftpfile));
41638451Smsmith	if (!tftpfile)
41738451Smsmith		return (ENOMEM);
41838451Smsmith
419223124Srodrigc	memset(tftpfile, 0, sizeof(*tftpfile));
420223124Srodrigc	tftpfile->tftp_blksize = TFTP_REQUESTED_BLKSIZE;
42138451Smsmith	tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata));
42269739Sps	if (io == NULL)
42369739Sps		return (EINVAL);
42469739Sps
42538451Smsmith	io->destip = servip;
42638451Smsmith	tftpfile->off = 0;
42739468Smsmith	tftpfile->path = strdup(path);
42839468Smsmith	if (tftpfile->path == NULL) {
42939468Smsmith	    free(tftpfile);
43039468Smsmith	    return(ENOMEM);
43139468Smsmith	}
43238451Smsmith
433193189Sed	res = tftp_makereq(tftpfile);
43438451Smsmith
43538451Smsmith	if (res) {
43639468Smsmith		free(tftpfile->path);
43738451Smsmith		free(tftpfile);
43838451Smsmith		return (res);
43938451Smsmith	}
44038451Smsmith	f->f_fsdata = (void *) tftpfile;
441223124Srodrigc	is_open = 1;
44238451Smsmith	return (0);
44338451Smsmith}
44438451Smsmith
445223488Srodrigcstatic int
446221358Srodrigctftp_read(struct open_file *f, void *addr, size_t size,
447221358Srodrigc    size_t *resid /* out */)
44838451Smsmith{
44938451Smsmith	struct tftp_handle *tftpfile;
45038451Smsmith	tftpfile = (struct tftp_handle *) f->f_fsdata;
45138451Smsmith
45238451Smsmith	while (size > 0) {
45338451Smsmith		int needblock, count;
45438451Smsmith
455278602Sian		twiddle(32);
45638451Smsmith
457223124Srodrigc		needblock = tftpfile->off / tftpfile->tftp_blksize + 1;
45838451Smsmith
459223488Srodrigc		if (tftpfile->currblock > needblock) {	/* seek backwards */
460223488Srodrigc			tftp_senderr(tftpfile, 0, "No error: read aborted");
46138451Smsmith			tftp_makereq(tftpfile);	/* no error check, it worked
46238451Smsmith						 * for open */
463223488Srodrigc		}
46438451Smsmith
46538451Smsmith		while (tftpfile->currblock < needblock) {
46638451Smsmith			int res;
46738451Smsmith
46838451Smsmith			res = tftp_getnextblock(tftpfile);
46938451Smsmith			if (res) {	/* no answer */
470221366Srodrigc#ifdef TFTP_DEBUG
47138451Smsmith				printf("tftp: read error\n");
47238451Smsmith#endif
47338451Smsmith				return (res);
47438451Smsmith			}
47538451Smsmith			if (tftpfile->islastblock)
47638451Smsmith				break;
47738451Smsmith		}
47838451Smsmith
47938451Smsmith		if (tftpfile->currblock == needblock) {
48038451Smsmith			int offinblock, inbuffer;
48138451Smsmith
482223124Srodrigc			offinblock = tftpfile->off % tftpfile->tftp_blksize;
48338451Smsmith
48438451Smsmith			inbuffer = tftpfile->validsize - offinblock;
48538451Smsmith			if (inbuffer < 0) {
486221366Srodrigc#ifdef TFTP_DEBUG
48738451Smsmith				printf("tftp: invalid offset %d\n",
48838451Smsmith				    tftpfile->off);
48938451Smsmith#endif
49038451Smsmith				return (EINVAL);
49138451Smsmith			}
49238451Smsmith			count = (size < inbuffer ? size : inbuffer);
49338451Smsmith			bcopy(tftpfile->lastdata.t.th_data + offinblock,
49438451Smsmith			    addr, count);
49538451Smsmith
496136093Sstefanf			addr = (char *)addr + count;
49738451Smsmith			tftpfile->off += count;
49838451Smsmith			size -= count;
49938451Smsmith
50038451Smsmith			if ((tftpfile->islastblock) && (count == inbuffer))
50138451Smsmith				break;	/* EOF */
50238451Smsmith		} else {
503221366Srodrigc#ifdef TFTP_DEBUG
50438451Smsmith			printf("tftp: block %d not found\n", needblock);
50538451Smsmith#endif
50638451Smsmith			return (EINVAL);
50738451Smsmith		}
50838451Smsmith
50938451Smsmith	}
51038451Smsmith
51138451Smsmith	if (resid)
51238451Smsmith		*resid = size;
51338451Smsmith	return (0);
51438451Smsmith}
51538451Smsmith
51638451Smsmithstatic int
517221358Srodrigctftp_close(struct open_file *f)
51838451Smsmith{
51938451Smsmith	struct tftp_handle *tftpfile;
52038451Smsmith	tftpfile = (struct tftp_handle *) f->f_fsdata;
52138451Smsmith
52238451Smsmith	/* let it time out ... */
52338451Smsmith
52439468Smsmith	if (tftpfile) {
52539468Smsmith		free(tftpfile->path);
52638451Smsmith		free(tftpfile);
52739468Smsmith	}
528223124Srodrigc	is_open = 0;
52938451Smsmith	return (0);
53038451Smsmith}
53138451Smsmith
532223488Srodrigcstatic int
533221358Srodrigctftp_write(struct open_file *f __unused, void *start __unused, size_t size __unused,
534223124Srodrigc    size_t *resid __unused /* out */)
53538451Smsmith{
53638451Smsmith	return (EROFS);
53738451Smsmith}
53838451Smsmith
53938451Smsmithstatic int
540223124Srodrigctftp_stat(struct open_file *f, struct stat *sb)
54138451Smsmith{
54238451Smsmith	struct tftp_handle *tftpfile;
54338451Smsmith	tftpfile = (struct tftp_handle *) f->f_fsdata;
54438451Smsmith
54559086Sps	sb->st_mode = 0444 | S_IFREG;
54638451Smsmith	sb->st_nlink = 1;
54738451Smsmith	sb->st_uid = 0;
54838451Smsmith	sb->st_gid = 0;
54938451Smsmith	sb->st_size = -1;
55038451Smsmith	return (0);
55138451Smsmith}
55238451Smsmith
553223488Srodrigcstatic off_t
554221358Srodrigctftp_seek(struct open_file *f, off_t offset, int where)
55538451Smsmith{
55638451Smsmith	struct tftp_handle *tftpfile;
55738451Smsmith	tftpfile = (struct tftp_handle *) f->f_fsdata;
55838451Smsmith
55938451Smsmith	switch (where) {
56038451Smsmith	case SEEK_SET:
56138451Smsmith		tftpfile->off = offset;
56238451Smsmith		break;
56338451Smsmith	case SEEK_CUR:
56438451Smsmith		tftpfile->off += offset;
56538451Smsmith		break;
56638451Smsmith	default:
56738451Smsmith		errno = EOFFSET;
56838451Smsmith		return (-1);
56938451Smsmith	}
57038451Smsmith	return (tftpfile->off);
57138451Smsmith}
572223122Srodrigc
573223122Srodrigcstatic ssize_t
574223488Srodrigcsendrecv_tftp(struct tftp_handle *h,
575223124Srodrigc    ssize_t (*sproc)(struct iodesc *, void *, size_t),
576223124Srodrigc    void *sbuf, size_t ssize,
577223124Srodrigc    ssize_t (*rproc)(struct tftp_handle *, void *, ssize_t, time_t, unsigned short *),
578223124Srodrigc    void *rbuf, size_t rsize, unsigned short *rtype)
579223122Srodrigc{
580223124Srodrigc	struct iodesc *d = h->iodesc;
581223122Srodrigc	ssize_t cc;
582223122Srodrigc	time_t t, t1, tleft;
583223122Srodrigc
584223122Srodrigc#ifdef TFTP_DEBUG
585223122Srodrigc	if (debug)
586223122Srodrigc		printf("sendrecv: called\n");
587223122Srodrigc#endif
588223122Srodrigc
589223122Srodrigc	tleft = MINTMO;
590223122Srodrigc	t = t1 = getsecs();
591223122Srodrigc	for (;;) {
592223122Srodrigc		if ((getsecs() - t) > MAXTMO) {
593223122Srodrigc			errno = ETIMEDOUT;
594223122Srodrigc			return -1;
595223122Srodrigc		}
596223122Srodrigc
597223122Srodrigc		cc = (*sproc)(d, sbuf, ssize);
598223122Srodrigc		if (cc != -1 && cc < ssize)
599223122Srodrigc			panic("sendrecv: short write! (%zd < %zu)",
600223122Srodrigc			    cc, ssize);
601223122Srodrigc
602223122Srodrigc		if (cc == -1) {
603223122Srodrigc			/* Error on transmit; wait before retrying */
604223122Srodrigc			while ((getsecs() - t1) < tleft);
605223122Srodrigc			continue;
606223122Srodrigc		}
607223122Srodrigc
608223123Srodrigcrecvnext:
609223122Srodrigc		/* Try to get a packet and process it. */
610223124Srodrigc		cc = (*rproc)(h, rbuf, rsize, tleft, rtype);
611223122Srodrigc		/* Return on data, EOF or real error. */
612223122Srodrigc		if (cc != -1 || errno != 0)
613223122Srodrigc			return (cc);
614223123Srodrigc		if ((getsecs() - t1) < tleft) {
615223123Srodrigc		    goto recvnext;
616223123Srodrigc		}
617223122Srodrigc
618223122Srodrigc		/* Timed out or didn't get the packet we're waiting for */
619223122Srodrigc		tleft += MINTMO;
620223122Srodrigc		if (tleft > (2 * MINTMO)) {
621223122Srodrigc			tleft = (2 * MINTMO);
622223122Srodrigc		}
623223122Srodrigc		t1 = getsecs();
624223122Srodrigc	}
625223122Srodrigc}
626223124Srodrigc
627223124Srodrigcstatic int
628223124Srodrigctftp_set_blksize(struct tftp_handle *h, const char *str)
629223124Srodrigc{
630223124Srodrigc        char *endptr;
631223124Srodrigc	int new_blksize;
632223124Srodrigc	int ret = 0;
633223124Srodrigc
634223124Srodrigc	if (h == NULL || str == NULL)
635223124Srodrigc		return (ret);
636223124Srodrigc
637223124Srodrigc	new_blksize =
638223124Srodrigc	    (unsigned int)strtol(str, &endptr, 0);
639223124Srodrigc
640223124Srodrigc	/*
641223124Srodrigc	 * Only accept blksize value if it is numeric.
642223488Srodrigc	 * RFC2348 specifies that acceptable values are 8-65464.
643223488Srodrigc	 * Let's choose a limit less than MAXRSPACE.
644223488Srodrigc	 */
645223124Srodrigc	if (*endptr == '\0' && new_blksize >= 8
646223124Srodrigc	    && new_blksize <= TFTP_MAX_BLKSIZE) {
647223124Srodrigc		h->tftp_blksize = new_blksize;
648223124Srodrigc		ret = 1;
649223124Srodrigc	}
650223124Srodrigc
651223124Srodrigc	return (ret);
652223124Srodrigc}
653223124Srodrigc
654223124Srodrigc/*
655223124Srodrigc * In RFC2347, the TFTP Option Acknowledgement package (OACK)
656223124Srodrigc * is used to acknowledge a client's option negotiation request.
657223124Srodrigc * The format of an OACK packet is:
658223124Srodrigc *    +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
659223124Srodrigc *    |  opc  |  opt1  | 0 | value1 | 0 |  optN  | 0 | valueN | 0 |
660223124Srodrigc *    +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
661223124Srodrigc *
662223124Srodrigc *    opc
663223124Srodrigc *       The opcode field contains a 6, for Option Acknowledgment.
664223124Srodrigc *
665223124Srodrigc *    opt1
666223124Srodrigc *       The first option acknowledgment, copied from the original
667223124Srodrigc *       request.
668223124Srodrigc *
669223124Srodrigc *    value1
670223124Srodrigc *       The acknowledged value associated with the first option.  If
671223124Srodrigc *       and how this value may differ from the original request is
672223124Srodrigc *       detailed in the specification for the option.
673223124Srodrigc *
674223124Srodrigc *    optN, valueN
675223124Srodrigc *       The final option/value acknowledgment pair.
676223124Srodrigc */
677223488Srodrigcstatic int
678223124Srodrigctftp_parse_oack(struct tftp_handle *h, char *buf, size_t len)
679223124Srodrigc{
680223124Srodrigc	/*
681223124Srodrigc	 *  We parse the OACK strings into an array
682223124Srodrigc	 *  of name-value pairs.
683223124Srodrigc	 */
684223124Srodrigc	char *tftp_options[128] = { 0 };
685223124Srodrigc	char *val = buf;
686223124Srodrigc	int i = 0;
687223124Srodrigc	int option_idx = 0;
688223124Srodrigc	int blksize_is_set = 0;
689223124Srodrigc	int tsize = 0;
690223124Srodrigc
691223488Srodrigc	unsigned int orig_blksize;
692223488Srodrigc
693223488Srodrigc	while (option_idx < 128 && i < len) {
694223488Srodrigc		if (buf[i] == '\0') {
695223488Srodrigc			if (&buf[i] > val) {
696223488Srodrigc				tftp_options[option_idx] = val;
697223488Srodrigc				val = &buf[i] + 1;
698223488Srodrigc				++option_idx;
699223488Srodrigc			}
700223488Srodrigc		}
701223488Srodrigc		++i;
702223124Srodrigc	}
703223124Srodrigc
704223488Srodrigc	/* Save the block size we requested for sanity check later. */
705223488Srodrigc	orig_blksize = h->tftp_blksize;
706223488Srodrigc
707223124Srodrigc	/*
708223124Srodrigc	 * Parse individual TFTP options.
709223124Srodrigc	 *    * "blksize" is specified in RFC2348.
710223124Srodrigc	 *    * "tsize" is specified in RFC2349.
711223124Srodrigc	 */
712223124Srodrigc	for (i = 0; i < option_idx; i += 2) {
713223124Srodrigc	    if (strcasecmp(tftp_options[i], "blksize") == 0) {
714223488Srodrigc		if (i + 1 < option_idx)
715223124Srodrigc			blksize_is_set =
716223124Srodrigc			    tftp_set_blksize(h, tftp_options[i + 1]);
717223124Srodrigc	    } else if (strcasecmp(tftp_options[i], "tsize") == 0) {
718223488Srodrigc		if (i + 1 < option_idx)
719223124Srodrigc			tsize = strtol(tftp_options[i + 1], (char **)NULL, 10);
720223488Srodrigc	    } else {
721223488Srodrigc		/* Do not allow any options we did not expect to be ACKed. */
722223488Srodrigc		printf("unexpected tftp option '%s'\n", tftp_options[i]);
723223488Srodrigc		return (-1);
724223124Srodrigc	    }
725223124Srodrigc	}
726223124Srodrigc
727223124Srodrigc	if (!blksize_is_set) {
728223124Srodrigc		/*
729223124Srodrigc		 * If TFTP blksize was not set, try defaulting
730223488Srodrigc		 * to the legacy TFTP blksize of SEGSIZE(512)
731223124Srodrigc		 */
732223488Srodrigc		h->tftp_blksize = SEGSIZE;
733223488Srodrigc	} else if (h->tftp_blksize > orig_blksize) {
734223488Srodrigc		/*
735223488Srodrigc		 * Server should not be proposing block sizes that
736223488Srodrigc		 * exceed what we said we can handle.
737223488Srodrigc		 */
738223488Srodrigc		printf("unexpected blksize %u\n", h->tftp_blksize);
739223488Srodrigc		return (-1);
740223124Srodrigc	}
741223124Srodrigc
742223124Srodrigc#ifdef TFTP_DEBUG
743223124Srodrigc	printf("tftp_blksize: %u\n", h->tftp_blksize);
744223124Srodrigc	printf("tftp_tsize: %lu\n", h->tftp_tsize);
745223124Srodrigc#endif
746223488Srodrigc	return 0;
747223124Srodrigc}
748