1230557Sjimharris/*	$NetBSD: tftp.c,v 1.4 1997/09/17 16:57:07 drochner Exp $	 */
2230557Sjimharris
3230557Sjimharris/*
4230557Sjimharris * Copyright (c) 1996
5230557Sjimharris *	Matthias Drochner.  All rights reserved.
6230557Sjimharris *
7230557Sjimharris * Redistribution and use in source and binary forms, with or without
8230557Sjimharris * modification, are permitted provided that the following conditions
9230557Sjimharris * are met:
10230557Sjimharris * 1. Redistributions of source code must retain the above copyright
11230557Sjimharris *    notice, this list of conditions and the following disclaimer.
12230557Sjimharris * 2. Redistributions in binary form must reproduce the above copyright
13230557Sjimharris *    notice, this list of conditions and the following disclaimer in the
14230557Sjimharris *    documentation and/or other materials provided with the distribution.
15230557Sjimharris * 3. All advertising materials mentioning features or use of this software
16230557Sjimharris *    must display the following acknowledgement:
17230557Sjimharris *	This product includes software developed for the NetBSD Project
18230557Sjimharris *	by Matthias Drochner.
19230557Sjimharris * 4. The name of the author may not be used to endorse or promote products
20230557Sjimharris *    derived from this software without specific prior written permission.
21230557Sjimharris *
22230557Sjimharris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23230557Sjimharris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24230557Sjimharris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25230557Sjimharris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26230557Sjimharris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27230557Sjimharris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28230557Sjimharris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29230557Sjimharris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30230557Sjimharris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31230557Sjimharris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32230557Sjimharris */
33230557Sjimharris
34230557Sjimharris#include <sys/cdefs.h>
35230557Sjimharris__FBSDID("$FreeBSD$");
36230557Sjimharris
37230557Sjimharris/*
38230557Sjimharris * Simple TFTP implementation for libsa.
39230557Sjimharris * Assumes:
40230557Sjimharris *  - socket descriptor (int) at open_file->f_devdata
41230557Sjimharris *  - server host IP in global servip
42230557Sjimharris * Restrictions:
43230557Sjimharris *  - read only
44230557Sjimharris *  - lseek only with SEEK_SET or SEEK_CUR
45230557Sjimharris *  - no big time differences between transfers (<tftp timeout)
46230557Sjimharris */
47230557Sjimharris
48230557Sjimharris#include <sys/types.h>
49230557Sjimharris#include <sys/stat.h>
50230557Sjimharris#include <netinet/in.h>
51230557Sjimharris#include <netinet/udp.h>
52230557Sjimharris#include <netinet/in_systm.h>
53230557Sjimharris#include <arpa/tftp.h>
54230557Sjimharris
55230557Sjimharris#include <string.h>
56230557Sjimharris
57230557Sjimharris#include "stand.h"
58230557Sjimharris#include "net.h"
59230557Sjimharris#include "netif.h"
60230557Sjimharris
61230557Sjimharris#include "tftp.h"
62230557Sjimharris
63230557Sjimharrisstruct tftp_handle;
64230557Sjimharris
65230557Sjimharrisstatic int	tftp_open(const char *path, struct open_file *f);
66230557Sjimharrisstatic int	tftp_close(struct open_file *f);
67230557Sjimharrisstatic int	tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len);
68230557Sjimharrisstatic int	tftp_read(struct open_file *f, void *buf, size_t size, size_t *resid);
69230557Sjimharrisstatic int	tftp_write(struct open_file *f, void *buf, size_t size, size_t *resid);
70230557Sjimharrisstatic off_t	tftp_seek(struct open_file *f, off_t offset, int where);
71230557Sjimharrisstatic int	tftp_set_blksize(struct tftp_handle *h, const char *str);
72230557Sjimharrisstatic int	tftp_stat(struct open_file *f, struct stat *sb);
73230557Sjimharrisstatic ssize_t sendrecv_tftp(struct tftp_handle *h,
74230557Sjimharris    ssize_t (*sproc)(struct iodesc *, void *, size_t),
75230557Sjimharris    void *sbuf, size_t ssize,
76230557Sjimharris    ssize_t (*rproc)(struct tftp_handle *h, void *, ssize_t, time_t, unsigned short *),
77230557Sjimharris    void *rbuf, size_t rsize, unsigned short *rtype);
78230557Sjimharris
79230557Sjimharrisstruct fs_ops tftp_fsops = {
80230557Sjimharris	"tftp",
81230557Sjimharris	tftp_open,
82230557Sjimharris	tftp_close,
83230557Sjimharris	tftp_read,
84230557Sjimharris	tftp_write,
85230557Sjimharris	tftp_seek,
86230557Sjimharris	tftp_stat,
87230557Sjimharris	null_readdir
88230557Sjimharris};
89230557Sjimharris
90230557Sjimharrisextern struct in_addr servip;
91230557Sjimharris
92230557Sjimharrisstatic int      tftpport = 2000;
93230557Sjimharrisstatic int	is_open = 0;
94230557Sjimharris
95230557Sjimharris/*
96230557Sjimharris * The legacy TFTP_BLKSIZE value was SEGSIZE(512).
97230557Sjimharris * TFTP_REQUESTED_BLKSIZE of 1428 is (Ethernet MTU, less the TFTP, UDP and
98230557Sjimharris * IP header lengths).
99230557Sjimharris */
100230557Sjimharris#define TFTP_REQUESTED_BLKSIZE 1428
101230557Sjimharris
102230557Sjimharris/*
103230557Sjimharris * Choose a blksize big enough so we can test with Ethernet
104230557Sjimharris * Jumbo frames in the future.
105230557Sjimharris */
106230557Sjimharris#define TFTP_MAX_BLKSIZE 9008
107230557Sjimharris
108230557Sjimharrisstruct tftp_handle {
109230557Sjimharris	struct iodesc  *iodesc;
110230557Sjimharris	int             currblock;	/* contents of lastdata */
111230557Sjimharris	int             islastblock;	/* flag */
112230557Sjimharris	int             validsize;
113230557Sjimharris	int             off;
114230557Sjimharris	char           *path;	/* saved for re-requests */
115230557Sjimharris	unsigned int	tftp_blksize;
116230557Sjimharris	unsigned long	tftp_tsize;
117230557Sjimharris	struct {
118230557Sjimharris		u_char header[HEADER_SIZE];
119230557Sjimharris		struct tftphdr t;
120230557Sjimharris		u_char space[TFTP_MAX_BLKSIZE];
121230557Sjimharris	} __packed __aligned(4) lastdata;
122230557Sjimharris};
123230557Sjimharris
124230557Sjimharris#define	TFTP_MAX_ERRCODE EOPTNEG
125230557Sjimharrisstatic const int tftperrors[TFTP_MAX_ERRCODE + 1] = {
126230557Sjimharris	0,			/* ??? */
127230557Sjimharris	ENOENT,
128230557Sjimharris	EPERM,
129230557Sjimharris	ENOSPC,
130230557Sjimharris	EINVAL,			/* ??? */
131230557Sjimharris	EINVAL,			/* ??? */
132230557Sjimharris	EEXIST,
133230557Sjimharris	EINVAL,			/* ??? */
134230557Sjimharris	EINVAL,			/* Option negotiation failed. */
135230557Sjimharris};
136230557Sjimharris
137230557Sjimharrisstatic int  tftp_getnextblock(struct tftp_handle *h);
138230557Sjimharris
139230557Sjimharris/* send error message back. */
140230557Sjimharrisstatic void
141230557Sjimharristftp_senderr(struct tftp_handle *h, u_short errcode, const char *msg)
142230557Sjimharris{
143230557Sjimharris	struct {
144230557Sjimharris		u_char header[HEADER_SIZE];
145230557Sjimharris		struct tftphdr  t;
146230557Sjimharris		u_char space[63]; /* +1 from t */
147230557Sjimharris	} __packed __aligned(4) wbuf;
148230557Sjimharris	char           *wtail;
149230557Sjimharris	int             len;
150230557Sjimharris
151230557Sjimharris	len = strlen(msg);
152230557Sjimharris	if (len > sizeof(wbuf.space))
153230557Sjimharris		len = sizeof(wbuf.space);
154230557Sjimharris
155230557Sjimharris	wbuf.t.th_opcode = htons((u_short) ERROR);
156230557Sjimharris	wbuf.t.th_code   = htons(errcode);
157230557Sjimharris
158230557Sjimharris	wtail = wbuf.t.th_msg;
159230557Sjimharris	bcopy(msg, wtail, len);
160230557Sjimharris	wtail[len] = '\0';
161230557Sjimharris	wtail += len + 1;
162230557Sjimharris
163230557Sjimharris	sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
164230557Sjimharris}
165230557Sjimharris
166230557Sjimharrisstatic void
167230557Sjimharristftp_sendack(struct tftp_handle *h)
168230557Sjimharris{
169230557Sjimharris	struct {
170230557Sjimharris		u_char header[HEADER_SIZE];
171230557Sjimharris		struct tftphdr  t;
172230557Sjimharris	} __packed __aligned(4) wbuf;
173230557Sjimharris	char           *wtail;
174230557Sjimharris
175230557Sjimharris	wbuf.t.th_opcode = htons((u_short) ACK);
176230557Sjimharris	wtail = (char *) &wbuf.t.th_block;
177230557Sjimharris	wbuf.t.th_block = htons((u_short) h->currblock);
178230557Sjimharris	wtail += 2;
179230557Sjimharris
180230557Sjimharris	sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
181230557Sjimharris}
182230557Sjimharris
183230557Sjimharrisstatic ssize_t
184230557Sjimharrisrecvtftp(struct tftp_handle *h, void *pkt, ssize_t len, time_t tleft,
185230557Sjimharris    unsigned short *rtype)
186230557Sjimharris{
187230557Sjimharris	struct iodesc *d = h->iodesc;
188230557Sjimharris	struct tftphdr *t;
189230557Sjimharris
190230557Sjimharris	errno = 0;
191230557Sjimharris
192230557Sjimharris	len = readudp(d, pkt, len, tleft);
193230557Sjimharris
194230557Sjimharris	if (len < 4)
195230557Sjimharris		return (-1);
196230557Sjimharris
197230557Sjimharris	t = (struct tftphdr *) pkt;
198230557Sjimharris	*rtype = ntohs(t->th_opcode);
199230557Sjimharris	switch (ntohs(t->th_opcode)) {
200230557Sjimharris	case DATA: {
201230557Sjimharris		int got;
202230557Sjimharris
203230557Sjimharris		if (htons(t->th_block) != d->xid) {
204230557Sjimharris			/*
205230557Sjimharris			 * Expected block?
206230557Sjimharris			 */
207230557Sjimharris			return (-1);
208230557Sjimharris		}
209230557Sjimharris		if (d->xid == 1) {
210230557Sjimharris			/*
211230557Sjimharris			 * First data packet from new port.
212230557Sjimharris			 */
213230557Sjimharris			struct udphdr *uh;
214230557Sjimharris			uh = (struct udphdr *) pkt - 1;
215230557Sjimharris			d->destport = uh->uh_sport;
216230557Sjimharris		} /* else check uh_sport has not changed??? */
217230557Sjimharris		got = len - (t->th_data - (char *) t);
218230557Sjimharris		return got;
219230557Sjimharris	}
220230557Sjimharris	case ERROR:
221230557Sjimharris		if ((unsigned) ntohs(t->th_code) > TFTP_MAX_ERRCODE) {
222230557Sjimharris			printf("illegal tftp error %d\n", ntohs(t->th_code));
223230557Sjimharris			errno = EIO;
224230557Sjimharris		} else {
225230557Sjimharris#ifdef TFTP_DEBUG
226230557Sjimharris			printf("tftp-error %d\n", ntohs(t->th_code));
227230557Sjimharris#endif
228230557Sjimharris			errno = tftperrors[ntohs(t->th_code)];
229230557Sjimharris		}
230230557Sjimharris		return (-1);
231230557Sjimharris	case OACK: {
232230557Sjimharris		struct udphdr *uh;
233230557Sjimharris		int tftp_oack_len;
234230557Sjimharris
235230557Sjimharris		/*
236230557Sjimharris		 * Unexpected OACK. TFTP transfer already in progress.
237230557Sjimharris		 * Drop the pkt.
238230557Sjimharris		 */
239230557Sjimharris		if (d->xid != 1) {
240230557Sjimharris			return (-1);
241230557Sjimharris		}
242230557Sjimharris
243230557Sjimharris		/*
244230557Sjimharris		 * Remember which port this OACK came from, because we need
245230557Sjimharris		 * to send the ACK or errors back to it.
246230557Sjimharris		 */
247230557Sjimharris		uh = (struct udphdr *) pkt - 1;
248230557Sjimharris		d->destport = uh->uh_sport;
249230557Sjimharris
250230557Sjimharris		/* Parse options ACK-ed by the server. */
251230557Sjimharris		tftp_oack_len = len - sizeof(t->th_opcode);
252230557Sjimharris		if (tftp_parse_oack(h, t->th_u.tu_stuff, tftp_oack_len) != 0) {
253230557Sjimharris			tftp_senderr(h, EOPTNEG, "Malformed OACK");
254230557Sjimharris			errno = EIO;
255230557Sjimharris			return (-1);
256230557Sjimharris		}
257230557Sjimharris		return (0);
258230557Sjimharris	}
259230557Sjimharris	default:
260230557Sjimharris#ifdef TFTP_DEBUG
261230557Sjimharris		printf("tftp type %d not handled\n", ntohs(t->th_opcode));
262230557Sjimharris#endif
263230557Sjimharris		return (-1);
264230557Sjimharris	}
265230557Sjimharris}
266230557Sjimharris
267230557Sjimharris/* send request, expect first block (or error) */
268230557Sjimharrisstatic int
269230557Sjimharristftp_makereq(struct tftp_handle *h)
270230557Sjimharris{
271230557Sjimharris	struct {
272230557Sjimharris		u_char header[HEADER_SIZE];
273230557Sjimharris		struct tftphdr  t;
274230557Sjimharris		u_char space[FNAME_SIZE + 6];
275230557Sjimharris	} __packed __aligned(4) wbuf;
276230557Sjimharris	char           *wtail;
277230557Sjimharris	int             l;
278230557Sjimharris	ssize_t         res;
279230557Sjimharris	struct tftphdr *t;
280230557Sjimharris	char *tftp_blksize = NULL;
281230557Sjimharris	int blksize_l;
282230557Sjimharris	unsigned short rtype = 0;
283230557Sjimharris
284230557Sjimharris	/*
285230557Sjimharris	 * Allow overriding default TFTP block size by setting
286230557Sjimharris	 * a tftp.blksize environment variable.
287230557Sjimharris	 */
288230557Sjimharris	if ((tftp_blksize = getenv("tftp.blksize")) != NULL) {
289230557Sjimharris		tftp_set_blksize(h, tftp_blksize);
290230557Sjimharris	}
291230557Sjimharris
292230557Sjimharris	wbuf.t.th_opcode = htons((u_short) RRQ);
293230557Sjimharris	wtail = wbuf.t.th_stuff;
294230557Sjimharris	l = strlen(h->path);
295230557Sjimharris#ifdef TFTP_PREPEND_PATH
296230557Sjimharris	if (l > FNAME_SIZE - (sizeof(TFTP_PREPEND_PATH) - 1))
297230557Sjimharris		return (ENAMETOOLONG);
298230557Sjimharris	bcopy(TFTP_PREPEND_PATH, wtail, sizeof(TFTP_PREPEND_PATH) - 1);
299230557Sjimharris	wtail += sizeof(TFTP_PREPEND_PATH) - 1;
300230557Sjimharris#else
301230557Sjimharris	if (l > FNAME_SIZE)
302230557Sjimharris		return (ENAMETOOLONG);
303230557Sjimharris#endif
304230557Sjimharris	bcopy(h->path, wtail, l + 1);
305230557Sjimharris	wtail += l + 1;
306230557Sjimharris	bcopy("octet", wtail, 6);
307230557Sjimharris	wtail += 6;
308230557Sjimharris	bcopy("blksize", wtail, 8);
309230557Sjimharris	wtail += 8;
310230557Sjimharris	blksize_l = sprintf(wtail, "%d", h->tftp_blksize);
311230557Sjimharris	wtail += blksize_l + 1;
312230557Sjimharris	bcopy("tsize", wtail, 6);
313230557Sjimharris	wtail += 6;
314230557Sjimharris	bcopy("0", wtail, 2);
315230557Sjimharris	wtail += 2;
316230557Sjimharris
317230557Sjimharris	t = &h->lastdata.t;
318230557Sjimharris
319230557Sjimharris	/* h->iodesc->myport = htons(--tftpport); */
320230557Sjimharris	h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
321230557Sjimharris	h->iodesc->destport = htons(IPPORT_TFTP);
322230557Sjimharris	h->iodesc->xid = 1;	/* expected block */
323230557Sjimharris
324230557Sjimharris	h->currblock = 0;
325230557Sjimharris	h->islastblock = 0;
326230557Sjimharris	h->validsize = 0;
327230557Sjimharris
328230557Sjimharris	res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
329230557Sjimharris		       &recvtftp, t, sizeof(*t) + h->tftp_blksize, &rtype);
330230557Sjimharris
331230557Sjimharris	if (rtype == OACK)
332230557Sjimharris		return (tftp_getnextblock(h));
333230557Sjimharris
334230557Sjimharris	/* Server ignored our blksize request, revert to TFTP default. */
335230557Sjimharris	h->tftp_blksize = SEGSIZE;
336230557Sjimharris
337230557Sjimharris	switch (rtype) {
338230557Sjimharris		case DATA: {
339230557Sjimharris			h->currblock = 1;
340230557Sjimharris			h->validsize = res;
341230557Sjimharris			h->islastblock = 0;
342230557Sjimharris			if (res < h->tftp_blksize) {
343230557Sjimharris				h->islastblock = 1;	/* very short file */
344230557Sjimharris				tftp_sendack(h);
345230557Sjimharris			}
346230557Sjimharris			return (0);
347230557Sjimharris		}
348230557Sjimharris		case ERROR:
349230557Sjimharris		default:
350230557Sjimharris			return (errno);
351230557Sjimharris	}
352230557Sjimharris
353230557Sjimharris}
354230557Sjimharris
355230557Sjimharris/* ack block, expect next */
356230557Sjimharrisstatic int
357230557Sjimharristftp_getnextblock(struct tftp_handle *h)
358230557Sjimharris{
359230557Sjimharris	struct {
360230557Sjimharris		u_char header[HEADER_SIZE];
361230557Sjimharris		struct tftphdr t;
362230557Sjimharris	} __packed __aligned(4) wbuf;
363230557Sjimharris	char           *wtail;
364230557Sjimharris	int             res;
365230557Sjimharris	struct tftphdr *t;
366230557Sjimharris	unsigned short rtype = 0;
367230557Sjimharris	wbuf.t.th_opcode = htons((u_short) ACK);
368230557Sjimharris	wtail = (char *) &wbuf.t.th_block;
369230557Sjimharris	wbuf.t.th_block = htons((u_short) h->currblock);
370230557Sjimharris	wtail += 2;
371230557Sjimharris
372230557Sjimharris	t = &h->lastdata.t;
373230557Sjimharris
374230557Sjimharris	h->iodesc->xid = h->currblock + 1;	/* expected block */
375230557Sjimharris
376230557Sjimharris	res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
377230557Sjimharris		       &recvtftp, t, sizeof(*t) + h->tftp_blksize, &rtype);
378230557Sjimharris
379230557Sjimharris	if (res == -1)		/* 0 is OK! */
380230557Sjimharris		return (errno);
381230557Sjimharris
382230557Sjimharris	h->currblock++;
383230557Sjimharris	h->validsize = res;
384230557Sjimharris	if (res < h->tftp_blksize)
385230557Sjimharris		h->islastblock = 1;	/* EOF */
386230557Sjimharris
387230557Sjimharris	if (h->islastblock == 1) {
388230557Sjimharris		/* Send an ACK for the last block */
389230557Sjimharris		wbuf.t.th_block = htons((u_short) h->currblock);
390230557Sjimharris		sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t);
391230557Sjimharris	}
392230557Sjimharris
393230557Sjimharris	return (0);
394230557Sjimharris}
395230557Sjimharris
396230557Sjimharrisstatic int
397230557Sjimharristftp_open(const char *path, struct open_file *f)
398230557Sjimharris{
399230557Sjimharris	struct tftp_handle *tftpfile;
400230557Sjimharris	struct iodesc  *io;
401230557Sjimharris	int             res;
402230557Sjimharris
403230557Sjimharris	if (strcmp(f->f_dev->dv_name, "net") != 0) {
404230557Sjimharris#ifdef __i386__
405230557Sjimharris		if (strcmp(f->f_dev->dv_name, "pxe") != 0)
406230557Sjimharris			return (EINVAL);
407230557Sjimharris#else
408230557Sjimharris		return (EINVAL);
409230557Sjimharris#endif
410230557Sjimharris	}
411230557Sjimharris
412230557Sjimharris	if (is_open)
413230557Sjimharris		return (EBUSY);
414230557Sjimharris
415230557Sjimharris	tftpfile = (struct tftp_handle *) malloc(sizeof(*tftpfile));
416230557Sjimharris	if (!tftpfile)
417230557Sjimharris		return (ENOMEM);
418230557Sjimharris
419230557Sjimharris	memset(tftpfile, 0, sizeof(*tftpfile));
420230557Sjimharris	tftpfile->tftp_blksize = TFTP_REQUESTED_BLKSIZE;
421230557Sjimharris	tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata));
422230557Sjimharris	if (io == NULL)
423230557Sjimharris		return (EINVAL);
424230557Sjimharris
425230557Sjimharris	io->destip = servip;
426230557Sjimharris	tftpfile->off = 0;
427230557Sjimharris	tftpfile->path = strdup(path);
428230557Sjimharris	if (tftpfile->path == NULL) {
429230557Sjimharris	    free(tftpfile);
430230557Sjimharris	    return(ENOMEM);
431230557Sjimharris	}
432230557Sjimharris
433230557Sjimharris	res = tftp_makereq(tftpfile);
434230557Sjimharris
435230557Sjimharris	if (res) {
436230557Sjimharris		free(tftpfile->path);
437230557Sjimharris		free(tftpfile);
438230557Sjimharris		return (res);
439230557Sjimharris	}
440230557Sjimharris	f->f_fsdata = (void *) tftpfile;
441230557Sjimharris	is_open = 1;
442230557Sjimharris	return (0);
443230557Sjimharris}
444230557Sjimharris
445230557Sjimharrisstatic int
446230557Sjimharristftp_read(struct open_file *f, void *addr, size_t size,
447230557Sjimharris    size_t *resid /* out */)
448230557Sjimharris{
449230557Sjimharris	struct tftp_handle *tftpfile;
450230557Sjimharris	static int      tc = 0;
451230557Sjimharris	tftpfile = (struct tftp_handle *) f->f_fsdata;
452230557Sjimharris
453230557Sjimharris	while (size > 0) {
454230557Sjimharris		int needblock, count;
455230557Sjimharris
456230557Sjimharris		if (!(tc++ % 16))
457230557Sjimharris			twiddle();
458230557Sjimharris
459230557Sjimharris		needblock = tftpfile->off / tftpfile->tftp_blksize + 1;
460230557Sjimharris
461230557Sjimharris		if (tftpfile->currblock > needblock) {	/* seek backwards */
462230557Sjimharris			tftp_senderr(tftpfile, 0, "No error: read aborted");
463			tftp_makereq(tftpfile);	/* no error check, it worked
464						 * for open */
465		}
466
467		while (tftpfile->currblock < needblock) {
468			int res;
469
470			res = tftp_getnextblock(tftpfile);
471			if (res) {	/* no answer */
472#ifdef TFTP_DEBUG
473				printf("tftp: read error\n");
474#endif
475				return (res);
476			}
477			if (tftpfile->islastblock)
478				break;
479		}
480
481		if (tftpfile->currblock == needblock) {
482			int offinblock, inbuffer;
483
484			offinblock = tftpfile->off % tftpfile->tftp_blksize;
485
486			inbuffer = tftpfile->validsize - offinblock;
487			if (inbuffer < 0) {
488#ifdef TFTP_DEBUG
489				printf("tftp: invalid offset %d\n",
490				    tftpfile->off);
491#endif
492				return (EINVAL);
493			}
494			count = (size < inbuffer ? size : inbuffer);
495			bcopy(tftpfile->lastdata.t.th_data + offinblock,
496			    addr, count);
497
498			addr = (char *)addr + count;
499			tftpfile->off += count;
500			size -= count;
501
502			if ((tftpfile->islastblock) && (count == inbuffer))
503				break;	/* EOF */
504		} else {
505#ifdef TFTP_DEBUG
506			printf("tftp: block %d not found\n", needblock);
507#endif
508			return (EINVAL);
509		}
510
511	}
512
513	if (resid)
514		*resid = size;
515	return (0);
516}
517
518static int
519tftp_close(struct open_file *f)
520{
521	struct tftp_handle *tftpfile;
522	tftpfile = (struct tftp_handle *) f->f_fsdata;
523
524	/* let it time out ... */
525
526	if (tftpfile) {
527		free(tftpfile->path);
528		free(tftpfile);
529	}
530	is_open = 0;
531	return (0);
532}
533
534static int
535tftp_write(struct open_file *f __unused, void *start __unused, size_t size __unused,
536    size_t *resid __unused /* out */)
537{
538	return (EROFS);
539}
540
541static int
542tftp_stat(struct open_file *f, struct stat *sb)
543{
544	struct tftp_handle *tftpfile;
545	tftpfile = (struct tftp_handle *) f->f_fsdata;
546
547	sb->st_mode = 0444 | S_IFREG;
548	sb->st_nlink = 1;
549	sb->st_uid = 0;
550	sb->st_gid = 0;
551	sb->st_size = -1;
552	return (0);
553}
554
555static off_t
556tftp_seek(struct open_file *f, off_t offset, int where)
557{
558	struct tftp_handle *tftpfile;
559	tftpfile = (struct tftp_handle *) f->f_fsdata;
560
561	switch (where) {
562	case SEEK_SET:
563		tftpfile->off = offset;
564		break;
565	case SEEK_CUR:
566		tftpfile->off += offset;
567		break;
568	default:
569		errno = EOFFSET;
570		return (-1);
571	}
572	return (tftpfile->off);
573}
574
575static ssize_t
576sendrecv_tftp(struct tftp_handle *h,
577    ssize_t (*sproc)(struct iodesc *, void *, size_t),
578    void *sbuf, size_t ssize,
579    ssize_t (*rproc)(struct tftp_handle *, void *, ssize_t, time_t, unsigned short *),
580    void *rbuf, size_t rsize, unsigned short *rtype)
581{
582	struct iodesc *d = h->iodesc;
583	ssize_t cc;
584	time_t t, t1, tleft;
585
586#ifdef TFTP_DEBUG
587	if (debug)
588		printf("sendrecv: called\n");
589#endif
590
591	tleft = MINTMO;
592	t = t1 = getsecs();
593	for (;;) {
594		if ((getsecs() - t) > MAXTMO) {
595			errno = ETIMEDOUT;
596			return -1;
597		}
598
599		cc = (*sproc)(d, sbuf, ssize);
600		if (cc != -1 && cc < ssize)
601			panic("sendrecv: short write! (%zd < %zu)",
602			    cc, ssize);
603
604		if (cc == -1) {
605			/* Error on transmit; wait before retrying */
606			while ((getsecs() - t1) < tleft);
607			continue;
608		}
609
610recvnext:
611		/* Try to get a packet and process it. */
612		cc = (*rproc)(h, rbuf, rsize, tleft, rtype);
613		/* Return on data, EOF or real error. */
614		if (cc != -1 || errno != 0)
615			return (cc);
616		if ((getsecs() - t1) < tleft) {
617		    goto recvnext;
618		}
619
620		/* Timed out or didn't get the packet we're waiting for */
621		tleft += MINTMO;
622		if (tleft > (2 * MINTMO)) {
623			tleft = (2 * MINTMO);
624		}
625		t1 = getsecs();
626	}
627}
628
629static int
630tftp_set_blksize(struct tftp_handle *h, const char *str)
631{
632        char *endptr;
633	int new_blksize;
634	int ret = 0;
635
636	if (h == NULL || str == NULL)
637		return (ret);
638
639	new_blksize =
640	    (unsigned int)strtol(str, &endptr, 0);
641
642	/*
643	 * Only accept blksize value if it is numeric.
644	 * RFC2348 specifies that acceptable values are 8-65464.
645	 * Let's choose a limit less than MAXRSPACE.
646	 */
647	if (*endptr == '\0' && new_blksize >= 8
648	    && new_blksize <= TFTP_MAX_BLKSIZE) {
649		h->tftp_blksize = new_blksize;
650		ret = 1;
651	}
652
653	return (ret);
654}
655
656/*
657 * In RFC2347, the TFTP Option Acknowledgement package (OACK)
658 * is used to acknowledge a client's option negotiation request.
659 * The format of an OACK packet is:
660 *    +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
661 *    |  opc  |  opt1  | 0 | value1 | 0 |  optN  | 0 | valueN | 0 |
662 *    +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
663 *
664 *    opc
665 *       The opcode field contains a 6, for Option Acknowledgment.
666 *
667 *    opt1
668 *       The first option acknowledgment, copied from the original
669 *       request.
670 *
671 *    value1
672 *       The acknowledged value associated with the first option.  If
673 *       and how this value may differ from the original request is
674 *       detailed in the specification for the option.
675 *
676 *    optN, valueN
677 *       The final option/value acknowledgment pair.
678 */
679static int
680tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len)
681{
682	/*
683	 *  We parse the OACK strings into an array
684	 *  of name-value pairs.
685	 */
686	char *tftp_options[128] = { 0 };
687	char *val = buf;
688	int i = 0;
689	int option_idx = 0;
690	int blksize_is_set = 0;
691	int tsize = 0;
692
693	unsigned int orig_blksize;
694
695	while (option_idx < 128 && i < len) {
696		if (buf[i] == '\0') {
697			if (&buf[i] > val) {
698				tftp_options[option_idx] = val;
699				val = &buf[i] + 1;
700				++option_idx;
701			}
702		}
703		++i;
704	}
705
706	/* Save the block size we requested for sanity check later. */
707	orig_blksize = h->tftp_blksize;
708
709	/*
710	 * Parse individual TFTP options.
711	 *    * "blksize" is specified in RFC2348.
712	 *    * "tsize" is specified in RFC2349.
713	 */
714	for (i = 0; i < option_idx; i += 2) {
715	    if (strcasecmp(tftp_options[i], "blksize") == 0) {
716		if (i + 1 < option_idx)
717			blksize_is_set =
718			    tftp_set_blksize(h, tftp_options[i + 1]);
719	    } else if (strcasecmp(tftp_options[i], "tsize") == 0) {
720		if (i + 1 < option_idx)
721			tsize = strtol(tftp_options[i + 1], (char **)NULL, 10);
722	    } else {
723		/* Do not allow any options we did not expect to be ACKed. */
724		printf("unexpected tftp option '%s'\n", tftp_options[i]);
725		return (-1);
726	    }
727	}
728
729	if (!blksize_is_set) {
730		/*
731		 * If TFTP blksize was not set, try defaulting
732		 * to the legacy TFTP blksize of SEGSIZE(512)
733		 */
734		h->tftp_blksize = SEGSIZE;
735	} else if (h->tftp_blksize > orig_blksize) {
736		/*
737		 * Server should not be proposing block sizes that
738		 * exceed what we said we can handle.
739		 */
740		printf("unexpected blksize %u\n", h->tftp_blksize);
741		return (-1);
742	}
743
744#ifdef TFTP_DEBUG
745	printf("tftp_blksize: %u\n", h->tftp_blksize);
746	printf("tftp_tsize: %lu\n", h->tftp_tsize);
747#endif
748	return 0;
749}
750