xref: /freebsd/stand/libsa/tftp.c (revision ca987d4641cdcd7f27e153db17c5bf064934faf5)
1*ca987d46SWarner Losh /*	$NetBSD: tftp.c,v 1.4 1997/09/17 16:57:07 drochner Exp $	 */
2*ca987d46SWarner Losh 
3*ca987d46SWarner Losh /*
4*ca987d46SWarner Losh  * Copyright (c) 1996
5*ca987d46SWarner Losh  *	Matthias Drochner.  All rights reserved.
6*ca987d46SWarner Losh  *
7*ca987d46SWarner Losh  * Redistribution and use in source and binary forms, with or without
8*ca987d46SWarner Losh  * modification, are permitted provided that the following conditions
9*ca987d46SWarner Losh  * are met:
10*ca987d46SWarner Losh  * 1. Redistributions of source code must retain the above copyright
11*ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer.
12*ca987d46SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
13*ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
14*ca987d46SWarner Losh  *    documentation and/or other materials provided with the distribution.
15*ca987d46SWarner Losh  * 3. All advertising materials mentioning features or use of this software
16*ca987d46SWarner Losh  *    must display the following acknowledgement:
17*ca987d46SWarner Losh  *	This product includes software developed for the NetBSD Project
18*ca987d46SWarner Losh  *	by Matthias Drochner.
19*ca987d46SWarner Losh  * 4. The name of the author may not be used to endorse or promote products
20*ca987d46SWarner Losh  *    derived from this software without specific prior written permission.
21*ca987d46SWarner Losh  *
22*ca987d46SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23*ca987d46SWarner Losh  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24*ca987d46SWarner Losh  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25*ca987d46SWarner Losh  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26*ca987d46SWarner Losh  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27*ca987d46SWarner Losh  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28*ca987d46SWarner Losh  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29*ca987d46SWarner Losh  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30*ca987d46SWarner Losh  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31*ca987d46SWarner Losh  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32*ca987d46SWarner Losh  */
33*ca987d46SWarner Losh 
34*ca987d46SWarner Losh #include <sys/cdefs.h>
35*ca987d46SWarner Losh __FBSDID("$FreeBSD$");
36*ca987d46SWarner Losh 
37*ca987d46SWarner Losh /*
38*ca987d46SWarner Losh  * Simple TFTP implementation for libsa.
39*ca987d46SWarner Losh  * Assumes:
40*ca987d46SWarner Losh  *  - socket descriptor (int) at open_file->f_devdata
41*ca987d46SWarner Losh  *  - server host IP in global servip
42*ca987d46SWarner Losh  * Restrictions:
43*ca987d46SWarner Losh  *  - read only
44*ca987d46SWarner Losh  *  - lseek only with SEEK_SET or SEEK_CUR
45*ca987d46SWarner Losh  *  - no big time differences between transfers (<tftp timeout)
46*ca987d46SWarner Losh  */
47*ca987d46SWarner Losh 
48*ca987d46SWarner Losh #include <sys/types.h>
49*ca987d46SWarner Losh #include <sys/stat.h>
50*ca987d46SWarner Losh #include <netinet/in.h>
51*ca987d46SWarner Losh #include <netinet/udp.h>
52*ca987d46SWarner Losh #include <netinet/in_systm.h>
53*ca987d46SWarner Losh #include <arpa/tftp.h>
54*ca987d46SWarner Losh 
55*ca987d46SWarner Losh #include <string.h>
56*ca987d46SWarner Losh 
57*ca987d46SWarner Losh #include "stand.h"
58*ca987d46SWarner Losh #include "net.h"
59*ca987d46SWarner Losh #include "netif.h"
60*ca987d46SWarner Losh 
61*ca987d46SWarner Losh #include "tftp.h"
62*ca987d46SWarner Losh 
63*ca987d46SWarner Losh struct tftp_handle;
64*ca987d46SWarner Losh 
65*ca987d46SWarner Losh static int	tftp_open(const char *path, struct open_file *f);
66*ca987d46SWarner Losh static int	tftp_close(struct open_file *f);
67*ca987d46SWarner Losh static int	tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len);
68*ca987d46SWarner Losh static int	tftp_read(struct open_file *f, void *buf, size_t size, size_t *resid);
69*ca987d46SWarner Losh static int	tftp_write(struct open_file *f, void *buf, size_t size, size_t *resid);
70*ca987d46SWarner Losh static off_t	tftp_seek(struct open_file *f, off_t offset, int where);
71*ca987d46SWarner Losh static int	tftp_set_blksize(struct tftp_handle *h, const char *str);
72*ca987d46SWarner Losh static int	tftp_stat(struct open_file *f, struct stat *sb);
73*ca987d46SWarner Losh static ssize_t sendrecv_tftp(struct tftp_handle *h,
74*ca987d46SWarner Losh     ssize_t (*sproc)(struct iodesc *, void *, size_t),
75*ca987d46SWarner Losh     void *sbuf, size_t ssize,
76*ca987d46SWarner Losh     ssize_t (*rproc)(struct tftp_handle *h, void **, void **, time_t, unsigned short *),
77*ca987d46SWarner Losh     void **, void **, unsigned short *rtype);
78*ca987d46SWarner Losh 
79*ca987d46SWarner Losh struct fs_ops tftp_fsops = {
80*ca987d46SWarner Losh 	"tftp",
81*ca987d46SWarner Losh 	tftp_open,
82*ca987d46SWarner Losh 	tftp_close,
83*ca987d46SWarner Losh 	tftp_read,
84*ca987d46SWarner Losh 	tftp_write,
85*ca987d46SWarner Losh 	tftp_seek,
86*ca987d46SWarner Losh 	tftp_stat,
87*ca987d46SWarner Losh 	null_readdir
88*ca987d46SWarner Losh };
89*ca987d46SWarner Losh 
90*ca987d46SWarner Losh extern struct in_addr servip;
91*ca987d46SWarner Losh 
92*ca987d46SWarner Losh static int      tftpport = 2000;
93*ca987d46SWarner Losh static int	is_open = 0;
94*ca987d46SWarner Losh 
95*ca987d46SWarner Losh /*
96*ca987d46SWarner Losh  * The legacy TFTP_BLKSIZE value was SEGSIZE(512).
97*ca987d46SWarner Losh  * TFTP_REQUESTED_BLKSIZE of 1428 is (Ethernet MTU, less the TFTP, UDP and
98*ca987d46SWarner Losh  * IP header lengths).
99*ca987d46SWarner Losh  */
100*ca987d46SWarner Losh #define TFTP_REQUESTED_BLKSIZE 1428
101*ca987d46SWarner Losh 
102*ca987d46SWarner Losh /*
103*ca987d46SWarner Losh  * Choose a blksize big enough so we can test with Ethernet
104*ca987d46SWarner Losh  * Jumbo frames in the future.
105*ca987d46SWarner Losh  */
106*ca987d46SWarner Losh #define TFTP_MAX_BLKSIZE 9008
107*ca987d46SWarner Losh 
108*ca987d46SWarner Losh struct tftp_handle {
109*ca987d46SWarner Losh 	struct iodesc  *iodesc;
110*ca987d46SWarner Losh 	int             currblock;	/* contents of lastdata */
111*ca987d46SWarner Losh 	int             islastblock;	/* flag */
112*ca987d46SWarner Losh 	int             validsize;
113*ca987d46SWarner Losh 	int             off;
114*ca987d46SWarner Losh 	char           *path;	/* saved for re-requests */
115*ca987d46SWarner Losh 	unsigned int	tftp_blksize;
116*ca987d46SWarner Losh 	unsigned long	tftp_tsize;
117*ca987d46SWarner Losh 	void		*pkt;
118*ca987d46SWarner Losh 	struct tftphdr	*tftp_hdr;
119*ca987d46SWarner Losh };
120*ca987d46SWarner Losh 
121*ca987d46SWarner Losh #define	TFTP_MAX_ERRCODE EOPTNEG
122*ca987d46SWarner Losh static const int tftperrors[TFTP_MAX_ERRCODE + 1] = {
123*ca987d46SWarner Losh 	0,			/* ??? */
124*ca987d46SWarner Losh 	ENOENT,
125*ca987d46SWarner Losh 	EPERM,
126*ca987d46SWarner Losh 	ENOSPC,
127*ca987d46SWarner Losh 	EINVAL,			/* ??? */
128*ca987d46SWarner Losh 	EINVAL,			/* ??? */
129*ca987d46SWarner Losh 	EEXIST,
130*ca987d46SWarner Losh 	EINVAL,			/* ??? */
131*ca987d46SWarner Losh 	EINVAL,			/* Option negotiation failed. */
132*ca987d46SWarner Losh };
133*ca987d46SWarner Losh 
134*ca987d46SWarner Losh static int  tftp_getnextblock(struct tftp_handle *h);
135*ca987d46SWarner Losh 
136*ca987d46SWarner Losh /* send error message back. */
137*ca987d46SWarner Losh static void
138*ca987d46SWarner Losh tftp_senderr(struct tftp_handle *h, u_short errcode, const char *msg)
139*ca987d46SWarner Losh {
140*ca987d46SWarner Losh 	struct {
141*ca987d46SWarner Losh 		u_char header[HEADER_SIZE];
142*ca987d46SWarner Losh 		struct tftphdr  t;
143*ca987d46SWarner Losh 		u_char space[63]; /* +1 from t */
144*ca987d46SWarner Losh 	} __packed __aligned(4) wbuf;
145*ca987d46SWarner Losh 	char           *wtail;
146*ca987d46SWarner Losh 	int             len;
147*ca987d46SWarner Losh 
148*ca987d46SWarner Losh 	len = strlen(msg);
149*ca987d46SWarner Losh 	if (len > sizeof(wbuf.space))
150*ca987d46SWarner Losh 		len = sizeof(wbuf.space);
151*ca987d46SWarner Losh 
152*ca987d46SWarner Losh 	wbuf.t.th_opcode = htons((u_short) ERROR);
153*ca987d46SWarner Losh 	wbuf.t.th_code   = htons(errcode);
154*ca987d46SWarner Losh 
155*ca987d46SWarner Losh 	wtail = wbuf.t.th_msg;
156*ca987d46SWarner Losh 	bcopy(msg, wtail, len);
157*ca987d46SWarner Losh 	wtail[len] = '\0';
158*ca987d46SWarner Losh 	wtail += len + 1;
159*ca987d46SWarner Losh 
160*ca987d46SWarner Losh 	sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
161*ca987d46SWarner Losh }
162*ca987d46SWarner Losh 
163*ca987d46SWarner Losh static void
164*ca987d46SWarner Losh tftp_sendack(struct tftp_handle *h)
165*ca987d46SWarner Losh {
166*ca987d46SWarner Losh 	struct {
167*ca987d46SWarner Losh 		u_char header[HEADER_SIZE];
168*ca987d46SWarner Losh 		struct tftphdr  t;
169*ca987d46SWarner Losh 	} __packed __aligned(4) wbuf;
170*ca987d46SWarner Losh 	char           *wtail;
171*ca987d46SWarner Losh 
172*ca987d46SWarner Losh 	wbuf.t.th_opcode = htons((u_short) ACK);
173*ca987d46SWarner Losh 	wtail = (char *) &wbuf.t.th_block;
174*ca987d46SWarner Losh 	wbuf.t.th_block = htons((u_short) h->currblock);
175*ca987d46SWarner Losh 	wtail += 2;
176*ca987d46SWarner Losh 
177*ca987d46SWarner Losh 	sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
178*ca987d46SWarner Losh }
179*ca987d46SWarner Losh 
180*ca987d46SWarner Losh static ssize_t
181*ca987d46SWarner Losh recvtftp(struct tftp_handle *h, void **pkt, void **payload, time_t tleft,
182*ca987d46SWarner Losh     unsigned short *rtype)
183*ca987d46SWarner Losh {
184*ca987d46SWarner Losh 	struct iodesc *d = h->iodesc;
185*ca987d46SWarner Losh 	struct tftphdr *t;
186*ca987d46SWarner Losh 	void *ptr = NULL;
187*ca987d46SWarner Losh 	ssize_t len;
188*ca987d46SWarner Losh 
189*ca987d46SWarner Losh 	errno = 0;
190*ca987d46SWarner Losh 
191*ca987d46SWarner Losh 	len = readudp(d, &ptr, (void **)&t, tleft);
192*ca987d46SWarner Losh 
193*ca987d46SWarner Losh 	if (len < 4) {
194*ca987d46SWarner Losh 		free(ptr);
195*ca987d46SWarner Losh 		return (-1);
196*ca987d46SWarner Losh 	}
197*ca987d46SWarner Losh 
198*ca987d46SWarner Losh 	*rtype = ntohs(t->th_opcode);
199*ca987d46SWarner Losh 	switch (ntohs(t->th_opcode)) {
200*ca987d46SWarner Losh 	case DATA: {
201*ca987d46SWarner Losh 		int got;
202*ca987d46SWarner Losh 
203*ca987d46SWarner Losh 		if (htons(t->th_block) != (u_short) d->xid) {
204*ca987d46SWarner Losh 			/*
205*ca987d46SWarner Losh 			 * Expected block?
206*ca987d46SWarner Losh 			 */
207*ca987d46SWarner Losh 			free(ptr);
208*ca987d46SWarner Losh 			return (-1);
209*ca987d46SWarner Losh 		}
210*ca987d46SWarner Losh 		if (d->xid == 1) {
211*ca987d46SWarner Losh 			/*
212*ca987d46SWarner Losh 			 * First data packet from new port.
213*ca987d46SWarner Losh 			 */
214*ca987d46SWarner Losh 			struct udphdr *uh;
215*ca987d46SWarner Losh 			uh = (struct udphdr *) t - 1;
216*ca987d46SWarner Losh 			d->destport = uh->uh_sport;
217*ca987d46SWarner Losh 		} /* else check uh_sport has not changed??? */
218*ca987d46SWarner Losh 		got = len - (t->th_data - (char *)t);
219*ca987d46SWarner Losh 		*pkt = ptr;
220*ca987d46SWarner Losh 		*payload = t;
221*ca987d46SWarner Losh 		return (got);
222*ca987d46SWarner Losh 	}
223*ca987d46SWarner Losh 	case ERROR:
224*ca987d46SWarner Losh 		if ((unsigned) ntohs(t->th_code) > TFTP_MAX_ERRCODE) {
225*ca987d46SWarner Losh 			printf("illegal tftp error %d\n", ntohs(t->th_code));
226*ca987d46SWarner Losh 			errno = EIO;
227*ca987d46SWarner Losh 		} else {
228*ca987d46SWarner Losh #ifdef TFTP_DEBUG
229*ca987d46SWarner Losh 			printf("tftp-error %d\n", ntohs(t->th_code));
230*ca987d46SWarner Losh #endif
231*ca987d46SWarner Losh 			errno = tftperrors[ntohs(t->th_code)];
232*ca987d46SWarner Losh 		}
233*ca987d46SWarner Losh 		free(ptr);
234*ca987d46SWarner Losh 		return (-1);
235*ca987d46SWarner Losh 	case OACK: {
236*ca987d46SWarner Losh 		struct udphdr *uh;
237*ca987d46SWarner Losh 		int tftp_oack_len;
238*ca987d46SWarner Losh 
239*ca987d46SWarner Losh 		/*
240*ca987d46SWarner Losh 		 * Unexpected OACK. TFTP transfer already in progress.
241*ca987d46SWarner Losh 		 * Drop the pkt.
242*ca987d46SWarner Losh 		 */
243*ca987d46SWarner Losh 		if (d->xid != 1) {
244*ca987d46SWarner Losh 			free(ptr);
245*ca987d46SWarner Losh 			return (-1);
246*ca987d46SWarner Losh 		}
247*ca987d46SWarner Losh 
248*ca987d46SWarner Losh 		/*
249*ca987d46SWarner Losh 		 * Remember which port this OACK came from, because we need
250*ca987d46SWarner Losh 		 * to send the ACK or errors back to it.
251*ca987d46SWarner Losh 		 */
252*ca987d46SWarner Losh 		uh = (struct udphdr *) t - 1;
253*ca987d46SWarner Losh 		d->destport = uh->uh_sport;
254*ca987d46SWarner Losh 
255*ca987d46SWarner Losh 		/* Parse options ACK-ed by the server. */
256*ca987d46SWarner Losh 		tftp_oack_len = len - sizeof(t->th_opcode);
257*ca987d46SWarner Losh 		if (tftp_parse_oack(h, t->th_u.tu_stuff, tftp_oack_len) != 0) {
258*ca987d46SWarner Losh 			tftp_senderr(h, EOPTNEG, "Malformed OACK");
259*ca987d46SWarner Losh 			errno = EIO;
260*ca987d46SWarner Losh 			free(ptr);
261*ca987d46SWarner Losh 			return (-1);
262*ca987d46SWarner Losh 		}
263*ca987d46SWarner Losh 		*pkt = ptr;
264*ca987d46SWarner Losh 		*payload = t;
265*ca987d46SWarner Losh 		return (0);
266*ca987d46SWarner Losh 	}
267*ca987d46SWarner Losh 	default:
268*ca987d46SWarner Losh #ifdef TFTP_DEBUG
269*ca987d46SWarner Losh 		printf("tftp type %d not handled\n", ntohs(t->th_opcode));
270*ca987d46SWarner Losh #endif
271*ca987d46SWarner Losh 		free(ptr);
272*ca987d46SWarner Losh 		return (-1);
273*ca987d46SWarner Losh 	}
274*ca987d46SWarner Losh }
275*ca987d46SWarner Losh 
276*ca987d46SWarner Losh /* send request, expect first block (or error) */
277*ca987d46SWarner Losh static int
278*ca987d46SWarner Losh tftp_makereq(struct tftp_handle *h)
279*ca987d46SWarner Losh {
280*ca987d46SWarner Losh 	struct {
281*ca987d46SWarner Losh 		u_char header[HEADER_SIZE];
282*ca987d46SWarner Losh 		struct tftphdr  t;
283*ca987d46SWarner Losh 		u_char space[FNAME_SIZE + 6];
284*ca987d46SWarner Losh 	} __packed __aligned(4) wbuf;
285*ca987d46SWarner Losh 	char           *wtail;
286*ca987d46SWarner Losh 	int             l;
287*ca987d46SWarner Losh 	ssize_t         res;
288*ca987d46SWarner Losh 	void *pkt;
289*ca987d46SWarner Losh 	struct tftphdr *t;
290*ca987d46SWarner Losh 	char *tftp_blksize = NULL;
291*ca987d46SWarner Losh 	int blksize_l;
292*ca987d46SWarner Losh 	unsigned short rtype = 0;
293*ca987d46SWarner Losh 
294*ca987d46SWarner Losh 	/*
295*ca987d46SWarner Losh 	 * Allow overriding default TFTP block size by setting
296*ca987d46SWarner Losh 	 * a tftp.blksize environment variable.
297*ca987d46SWarner Losh 	 */
298*ca987d46SWarner Losh 	if ((tftp_blksize = getenv("tftp.blksize")) != NULL) {
299*ca987d46SWarner Losh 		tftp_set_blksize(h, tftp_blksize);
300*ca987d46SWarner Losh 	}
301*ca987d46SWarner Losh 
302*ca987d46SWarner Losh 	wbuf.t.th_opcode = htons((u_short) RRQ);
303*ca987d46SWarner Losh 	wtail = wbuf.t.th_stuff;
304*ca987d46SWarner Losh 	l = strlen(h->path);
305*ca987d46SWarner Losh #ifdef TFTP_PREPEND_PATH
306*ca987d46SWarner Losh 	if (l > FNAME_SIZE - (sizeof(TFTP_PREPEND_PATH) - 1))
307*ca987d46SWarner Losh 		return (ENAMETOOLONG);
308*ca987d46SWarner Losh 	bcopy(TFTP_PREPEND_PATH, wtail, sizeof(TFTP_PREPEND_PATH) - 1);
309*ca987d46SWarner Losh 	wtail += sizeof(TFTP_PREPEND_PATH) - 1;
310*ca987d46SWarner Losh #else
311*ca987d46SWarner Losh 	if (l > FNAME_SIZE)
312*ca987d46SWarner Losh 		return (ENAMETOOLONG);
313*ca987d46SWarner Losh #endif
314*ca987d46SWarner Losh 	bcopy(h->path, wtail, l + 1);
315*ca987d46SWarner Losh 	wtail += l + 1;
316*ca987d46SWarner Losh 	bcopy("octet", wtail, 6);
317*ca987d46SWarner Losh 	wtail += 6;
318*ca987d46SWarner Losh 	bcopy("blksize", wtail, 8);
319*ca987d46SWarner Losh 	wtail += 8;
320*ca987d46SWarner Losh 	blksize_l = sprintf(wtail, "%d", h->tftp_blksize);
321*ca987d46SWarner Losh 	wtail += blksize_l + 1;
322*ca987d46SWarner Losh 	bcopy("tsize", wtail, 6);
323*ca987d46SWarner Losh 	wtail += 6;
324*ca987d46SWarner Losh 	bcopy("0", wtail, 2);
325*ca987d46SWarner Losh 	wtail += 2;
326*ca987d46SWarner Losh 
327*ca987d46SWarner Losh 	/* h->iodesc->myport = htons(--tftpport); */
328*ca987d46SWarner Losh 	h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
329*ca987d46SWarner Losh 	h->iodesc->destport = htons(IPPORT_TFTP);
330*ca987d46SWarner Losh 	h->iodesc->xid = 1;	/* expected block */
331*ca987d46SWarner Losh 
332*ca987d46SWarner Losh 	h->currblock = 0;
333*ca987d46SWarner Losh 	h->islastblock = 0;
334*ca987d46SWarner Losh 	h->validsize = 0;
335*ca987d46SWarner Losh 
336*ca987d46SWarner Losh 	pkt = NULL;
337*ca987d46SWarner Losh 	res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
338*ca987d46SWarner Losh 		       &recvtftp, &pkt, (void **)&t, &rtype);
339*ca987d46SWarner Losh 	if (res == -1) {
340*ca987d46SWarner Losh 		free(pkt);
341*ca987d46SWarner Losh 		return (errno);
342*ca987d46SWarner Losh 	}
343*ca987d46SWarner Losh 
344*ca987d46SWarner Losh 	free(h->pkt);
345*ca987d46SWarner Losh 	h->pkt = pkt;
346*ca987d46SWarner Losh 	h->tftp_hdr = t;
347*ca987d46SWarner Losh 
348*ca987d46SWarner Losh 	if (rtype == OACK)
349*ca987d46SWarner Losh 		return (tftp_getnextblock(h));
350*ca987d46SWarner Losh 
351*ca987d46SWarner Losh 	/* Server ignored our blksize request, revert to TFTP default. */
352*ca987d46SWarner Losh 	h->tftp_blksize = SEGSIZE;
353*ca987d46SWarner Losh 
354*ca987d46SWarner Losh 	switch (rtype) {
355*ca987d46SWarner Losh 		case DATA: {
356*ca987d46SWarner Losh 			h->currblock = 1;
357*ca987d46SWarner Losh 			h->validsize = res;
358*ca987d46SWarner Losh 			h->islastblock = 0;
359*ca987d46SWarner Losh 			if (res < h->tftp_blksize) {
360*ca987d46SWarner Losh 				h->islastblock = 1;	/* very short file */
361*ca987d46SWarner Losh 				tftp_sendack(h);
362*ca987d46SWarner Losh 			}
363*ca987d46SWarner Losh 			return (0);
364*ca987d46SWarner Losh 		}
365*ca987d46SWarner Losh 		case ERROR:
366*ca987d46SWarner Losh 		default:
367*ca987d46SWarner Losh 			return (errno);
368*ca987d46SWarner Losh 	}
369*ca987d46SWarner Losh 
370*ca987d46SWarner Losh }
371*ca987d46SWarner Losh 
372*ca987d46SWarner Losh /* ack block, expect next */
373*ca987d46SWarner Losh static int
374*ca987d46SWarner Losh tftp_getnextblock(struct tftp_handle *h)
375*ca987d46SWarner Losh {
376*ca987d46SWarner Losh 	struct {
377*ca987d46SWarner Losh 		u_char header[HEADER_SIZE];
378*ca987d46SWarner Losh 		struct tftphdr t;
379*ca987d46SWarner Losh 	} __packed __aligned(4) wbuf;
380*ca987d46SWarner Losh 	char           *wtail;
381*ca987d46SWarner Losh 	int             res;
382*ca987d46SWarner Losh 	void *pkt;
383*ca987d46SWarner Losh 	struct tftphdr *t;
384*ca987d46SWarner Losh 	unsigned short rtype = 0;
385*ca987d46SWarner Losh 	wbuf.t.th_opcode = htons((u_short) ACK);
386*ca987d46SWarner Losh 	wtail = (char *) &wbuf.t.th_block;
387*ca987d46SWarner Losh 	wbuf.t.th_block = htons((u_short) h->currblock);
388*ca987d46SWarner Losh 	wtail += 2;
389*ca987d46SWarner Losh 
390*ca987d46SWarner Losh 	h->iodesc->xid = h->currblock + 1;	/* expected block */
391*ca987d46SWarner Losh 
392*ca987d46SWarner Losh 	pkt = NULL;
393*ca987d46SWarner Losh 	res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
394*ca987d46SWarner Losh 		       &recvtftp, &pkt, (void **)&t, &rtype);
395*ca987d46SWarner Losh 
396*ca987d46SWarner Losh 	if (res == -1) {		/* 0 is OK! */
397*ca987d46SWarner Losh 		free(pkt);
398*ca987d46SWarner Losh 		return (errno);
399*ca987d46SWarner Losh 	}
400*ca987d46SWarner Losh 
401*ca987d46SWarner Losh 	free(h->pkt);
402*ca987d46SWarner Losh 	h->pkt = pkt;
403*ca987d46SWarner Losh 	h->tftp_hdr = t;
404*ca987d46SWarner Losh 	h->currblock++;
405*ca987d46SWarner Losh 	h->validsize = res;
406*ca987d46SWarner Losh 	if (res < h->tftp_blksize)
407*ca987d46SWarner Losh 		h->islastblock = 1;	/* EOF */
408*ca987d46SWarner Losh 
409*ca987d46SWarner Losh 	if (h->islastblock == 1) {
410*ca987d46SWarner Losh 		/* Send an ACK for the last block */
411*ca987d46SWarner Losh 		wbuf.t.th_block = htons((u_short) h->currblock);
412*ca987d46SWarner Losh 		sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t);
413*ca987d46SWarner Losh 	}
414*ca987d46SWarner Losh 
415*ca987d46SWarner Losh 	return (0);
416*ca987d46SWarner Losh }
417*ca987d46SWarner Losh 
418*ca987d46SWarner Losh static int
419*ca987d46SWarner Losh tftp_open(const char *path, struct open_file *f)
420*ca987d46SWarner Losh {
421*ca987d46SWarner Losh 	struct tftp_handle *tftpfile;
422*ca987d46SWarner Losh 	struct iodesc  *io;
423*ca987d46SWarner Losh 	int             res;
424*ca987d46SWarner Losh 	size_t          pathsize;
425*ca987d46SWarner Losh 	const char     *extraslash;
426*ca987d46SWarner Losh 
427*ca987d46SWarner Losh 	if (netproto != NET_TFTP)
428*ca987d46SWarner Losh 		return (EINVAL);
429*ca987d46SWarner Losh 
430*ca987d46SWarner Losh 	if (f->f_dev->dv_type != DEVT_NET)
431*ca987d46SWarner Losh 		return (EINVAL);
432*ca987d46SWarner Losh 
433*ca987d46SWarner Losh 	if (is_open)
434*ca987d46SWarner Losh 		return (EBUSY);
435*ca987d46SWarner Losh 
436*ca987d46SWarner Losh 	tftpfile = (struct tftp_handle *) malloc(sizeof(*tftpfile));
437*ca987d46SWarner Losh 	if (!tftpfile)
438*ca987d46SWarner Losh 		return (ENOMEM);
439*ca987d46SWarner Losh 
440*ca987d46SWarner Losh 	memset(tftpfile, 0, sizeof(*tftpfile));
441*ca987d46SWarner Losh 	tftpfile->tftp_blksize = TFTP_REQUESTED_BLKSIZE;
442*ca987d46SWarner Losh 	tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata));
443*ca987d46SWarner Losh 	if (io == NULL)
444*ca987d46SWarner Losh 		return (EINVAL);
445*ca987d46SWarner Losh 
446*ca987d46SWarner Losh 	io->destip = servip;
447*ca987d46SWarner Losh 	tftpfile->off = 0;
448*ca987d46SWarner Losh 	pathsize = (strlen(rootpath) + 1 + strlen(path) + 1) * sizeof(char);
449*ca987d46SWarner Losh 	tftpfile->path = malloc(pathsize);
450*ca987d46SWarner Losh 	if (tftpfile->path == NULL) {
451*ca987d46SWarner Losh 		free(tftpfile);
452*ca987d46SWarner Losh 		return(ENOMEM);
453*ca987d46SWarner Losh 	}
454*ca987d46SWarner Losh 	if (rootpath[strlen(rootpath) - 1] == '/' || path[0] == '/')
455*ca987d46SWarner Losh 		extraslash = "";
456*ca987d46SWarner Losh 	else
457*ca987d46SWarner Losh 		extraslash = "/";
458*ca987d46SWarner Losh 	res = snprintf(tftpfile->path, pathsize, "%s%s%s",
459*ca987d46SWarner Losh 	    rootpath, extraslash, path);
460*ca987d46SWarner Losh 	if (res < 0 || res > pathsize) {
461*ca987d46SWarner Losh 		free(tftpfile->path);
462*ca987d46SWarner Losh 		free(tftpfile);
463*ca987d46SWarner Losh 		return(ENOMEM);
464*ca987d46SWarner Losh 	}
465*ca987d46SWarner Losh 
466*ca987d46SWarner Losh 	res = tftp_makereq(tftpfile);
467*ca987d46SWarner Losh 
468*ca987d46SWarner Losh 	if (res) {
469*ca987d46SWarner Losh 		free(tftpfile->path);
470*ca987d46SWarner Losh 		free(tftpfile->pkt);
471*ca987d46SWarner Losh 		free(tftpfile);
472*ca987d46SWarner Losh 		return (res);
473*ca987d46SWarner Losh 	}
474*ca987d46SWarner Losh 	f->f_fsdata = (void *) tftpfile;
475*ca987d46SWarner Losh 	is_open = 1;
476*ca987d46SWarner Losh 	return (0);
477*ca987d46SWarner Losh }
478*ca987d46SWarner Losh 
479*ca987d46SWarner Losh static int
480*ca987d46SWarner Losh tftp_read(struct open_file *f, void *addr, size_t size,
481*ca987d46SWarner Losh     size_t *resid /* out */)
482*ca987d46SWarner Losh {
483*ca987d46SWarner Losh 	struct tftp_handle *tftpfile;
484*ca987d46SWarner Losh 	tftpfile = (struct tftp_handle *) f->f_fsdata;
485*ca987d46SWarner Losh 
486*ca987d46SWarner Losh 	while (size > 0) {
487*ca987d46SWarner Losh 		int needblock, count;
488*ca987d46SWarner Losh 
489*ca987d46SWarner Losh 		twiddle(32);
490*ca987d46SWarner Losh 
491*ca987d46SWarner Losh 		needblock = tftpfile->off / tftpfile->tftp_blksize + 1;
492*ca987d46SWarner Losh 
493*ca987d46SWarner Losh 		if (tftpfile->currblock > needblock) {	/* seek backwards */
494*ca987d46SWarner Losh 			tftp_senderr(tftpfile, 0, "No error: read aborted");
495*ca987d46SWarner Losh 			tftp_makereq(tftpfile);	/* no error check, it worked
496*ca987d46SWarner Losh 						 * for open */
497*ca987d46SWarner Losh 		}
498*ca987d46SWarner Losh 
499*ca987d46SWarner Losh 		while (tftpfile->currblock < needblock) {
500*ca987d46SWarner Losh 			int res;
501*ca987d46SWarner Losh 
502*ca987d46SWarner Losh 			res = tftp_getnextblock(tftpfile);
503*ca987d46SWarner Losh 			if (res) {	/* no answer */
504*ca987d46SWarner Losh #ifdef TFTP_DEBUG
505*ca987d46SWarner Losh 				printf("tftp: read error\n");
506*ca987d46SWarner Losh #endif
507*ca987d46SWarner Losh 				return (res);
508*ca987d46SWarner Losh 			}
509*ca987d46SWarner Losh 			if (tftpfile->islastblock)
510*ca987d46SWarner Losh 				break;
511*ca987d46SWarner Losh 		}
512*ca987d46SWarner Losh 
513*ca987d46SWarner Losh 		if (tftpfile->currblock == needblock) {
514*ca987d46SWarner Losh 			int offinblock, inbuffer;
515*ca987d46SWarner Losh 
516*ca987d46SWarner Losh 			offinblock = tftpfile->off % tftpfile->tftp_blksize;
517*ca987d46SWarner Losh 
518*ca987d46SWarner Losh 			inbuffer = tftpfile->validsize - offinblock;
519*ca987d46SWarner Losh 			if (inbuffer < 0) {
520*ca987d46SWarner Losh #ifdef TFTP_DEBUG
521*ca987d46SWarner Losh 				printf("tftp: invalid offset %d\n",
522*ca987d46SWarner Losh 				    tftpfile->off);
523*ca987d46SWarner Losh #endif
524*ca987d46SWarner Losh 				return (EINVAL);
525*ca987d46SWarner Losh 			}
526*ca987d46SWarner Losh 			count = (size < inbuffer ? size : inbuffer);
527*ca987d46SWarner Losh 			bcopy(tftpfile->tftp_hdr->th_data + offinblock,
528*ca987d46SWarner Losh 			    addr, count);
529*ca987d46SWarner Losh 
530*ca987d46SWarner Losh 			addr = (char *)addr + count;
531*ca987d46SWarner Losh 			tftpfile->off += count;
532*ca987d46SWarner Losh 			size -= count;
533*ca987d46SWarner Losh 
534*ca987d46SWarner Losh 			if ((tftpfile->islastblock) && (count == inbuffer))
535*ca987d46SWarner Losh 				break;	/* EOF */
536*ca987d46SWarner Losh 		} else {
537*ca987d46SWarner Losh #ifdef TFTP_DEBUG
538*ca987d46SWarner Losh 			printf("tftp: block %d not found\n", needblock);
539*ca987d46SWarner Losh #endif
540*ca987d46SWarner Losh 			return (EINVAL);
541*ca987d46SWarner Losh 		}
542*ca987d46SWarner Losh 
543*ca987d46SWarner Losh 	}
544*ca987d46SWarner Losh 
545*ca987d46SWarner Losh 	if (resid)
546*ca987d46SWarner Losh 		*resid = size;
547*ca987d46SWarner Losh 	return (0);
548*ca987d46SWarner Losh }
549*ca987d46SWarner Losh 
550*ca987d46SWarner Losh static int
551*ca987d46SWarner Losh tftp_close(struct open_file *f)
552*ca987d46SWarner Losh {
553*ca987d46SWarner Losh 	struct tftp_handle *tftpfile;
554*ca987d46SWarner Losh 	tftpfile = (struct tftp_handle *) f->f_fsdata;
555*ca987d46SWarner Losh 
556*ca987d46SWarner Losh 	/* let it time out ... */
557*ca987d46SWarner Losh 
558*ca987d46SWarner Losh 	if (tftpfile) {
559*ca987d46SWarner Losh 		free(tftpfile->path);
560*ca987d46SWarner Losh 		free(tftpfile->pkt);
561*ca987d46SWarner Losh 		free(tftpfile);
562*ca987d46SWarner Losh 	}
563*ca987d46SWarner Losh 	is_open = 0;
564*ca987d46SWarner Losh 	return (0);
565*ca987d46SWarner Losh }
566*ca987d46SWarner Losh 
567*ca987d46SWarner Losh static int
568*ca987d46SWarner Losh tftp_write(struct open_file *f __unused, void *start __unused, size_t size __unused,
569*ca987d46SWarner Losh     size_t *resid __unused /* out */)
570*ca987d46SWarner Losh {
571*ca987d46SWarner Losh 	return (EROFS);
572*ca987d46SWarner Losh }
573*ca987d46SWarner Losh 
574*ca987d46SWarner Losh static int
575*ca987d46SWarner Losh tftp_stat(struct open_file *f, struct stat *sb)
576*ca987d46SWarner Losh {
577*ca987d46SWarner Losh 	struct tftp_handle *tftpfile;
578*ca987d46SWarner Losh 	tftpfile = (struct tftp_handle *) f->f_fsdata;
579*ca987d46SWarner Losh 
580*ca987d46SWarner Losh 	sb->st_mode = 0444 | S_IFREG;
581*ca987d46SWarner Losh 	sb->st_nlink = 1;
582*ca987d46SWarner Losh 	sb->st_uid = 0;
583*ca987d46SWarner Losh 	sb->st_gid = 0;
584*ca987d46SWarner Losh 	sb->st_size = (off_t) tftpfile->tftp_tsize;
585*ca987d46SWarner Losh 	return (0);
586*ca987d46SWarner Losh }
587*ca987d46SWarner Losh 
588*ca987d46SWarner Losh static off_t
589*ca987d46SWarner Losh tftp_seek(struct open_file *f, off_t offset, int where)
590*ca987d46SWarner Losh {
591*ca987d46SWarner Losh 	struct tftp_handle *tftpfile;
592*ca987d46SWarner Losh 	tftpfile = (struct tftp_handle *) f->f_fsdata;
593*ca987d46SWarner Losh 
594*ca987d46SWarner Losh 	switch (where) {
595*ca987d46SWarner Losh 	case SEEK_SET:
596*ca987d46SWarner Losh 		tftpfile->off = offset;
597*ca987d46SWarner Losh 		break;
598*ca987d46SWarner Losh 	case SEEK_CUR:
599*ca987d46SWarner Losh 		tftpfile->off += offset;
600*ca987d46SWarner Losh 		break;
601*ca987d46SWarner Losh 	default:
602*ca987d46SWarner Losh 		errno = EOFFSET;
603*ca987d46SWarner Losh 		return (-1);
604*ca987d46SWarner Losh 	}
605*ca987d46SWarner Losh 	return (tftpfile->off);
606*ca987d46SWarner Losh }
607*ca987d46SWarner Losh 
608*ca987d46SWarner Losh static ssize_t
609*ca987d46SWarner Losh sendrecv_tftp(struct tftp_handle *h,
610*ca987d46SWarner Losh     ssize_t (*sproc)(struct iodesc *, void *, size_t),
611*ca987d46SWarner Losh     void *sbuf, size_t ssize,
612*ca987d46SWarner Losh     ssize_t (*rproc)(struct tftp_handle *, void **, void **, time_t,
613*ca987d46SWarner Losh     unsigned short *),
614*ca987d46SWarner Losh     void **pkt, void **payload, unsigned short *rtype)
615*ca987d46SWarner Losh {
616*ca987d46SWarner Losh 	struct iodesc *d = h->iodesc;
617*ca987d46SWarner Losh 	ssize_t cc;
618*ca987d46SWarner Losh 	time_t t, t1, tleft;
619*ca987d46SWarner Losh 
620*ca987d46SWarner Losh #ifdef TFTP_DEBUG
621*ca987d46SWarner Losh 	if (debug)
622*ca987d46SWarner Losh 		printf("sendrecv: called\n");
623*ca987d46SWarner Losh #endif
624*ca987d46SWarner Losh 
625*ca987d46SWarner Losh 	tleft = MINTMO;
626*ca987d46SWarner Losh 	t = t1 = getsecs();
627*ca987d46SWarner Losh 	for (;;) {
628*ca987d46SWarner Losh 		if ((getsecs() - t) > MAXTMO) {
629*ca987d46SWarner Losh 			errno = ETIMEDOUT;
630*ca987d46SWarner Losh 			return -1;
631*ca987d46SWarner Losh 		}
632*ca987d46SWarner Losh 
633*ca987d46SWarner Losh 		cc = (*sproc)(d, sbuf, ssize);
634*ca987d46SWarner Losh 		if (cc != -1 && cc < ssize)
635*ca987d46SWarner Losh 			panic("sendrecv: short write! (%zd < %zu)",
636*ca987d46SWarner Losh 			    cc, ssize);
637*ca987d46SWarner Losh 
638*ca987d46SWarner Losh 		if (cc == -1) {
639*ca987d46SWarner Losh 			/* Error on transmit; wait before retrying */
640*ca987d46SWarner Losh 			while ((getsecs() - t1) < tleft);
641*ca987d46SWarner Losh 			continue;
642*ca987d46SWarner Losh 		}
643*ca987d46SWarner Losh 
644*ca987d46SWarner Losh recvnext:
645*ca987d46SWarner Losh 		/* Try to get a packet and process it. */
646*ca987d46SWarner Losh 		cc = (*rproc)(h, pkt, payload, tleft, rtype);
647*ca987d46SWarner Losh 		/* Return on data, EOF or real error. */
648*ca987d46SWarner Losh 		if (cc != -1 || errno != 0)
649*ca987d46SWarner Losh 			return (cc);
650*ca987d46SWarner Losh 		if ((getsecs() - t1) < tleft) {
651*ca987d46SWarner Losh 		    goto recvnext;
652*ca987d46SWarner Losh 		}
653*ca987d46SWarner Losh 
654*ca987d46SWarner Losh 		/* Timed out or didn't get the packet we're waiting for */
655*ca987d46SWarner Losh 		tleft += MINTMO;
656*ca987d46SWarner Losh 		if (tleft > (2 * MINTMO)) {
657*ca987d46SWarner Losh 			tleft = (2 * MINTMO);
658*ca987d46SWarner Losh 		}
659*ca987d46SWarner Losh 		t1 = getsecs();
660*ca987d46SWarner Losh 	}
661*ca987d46SWarner Losh }
662*ca987d46SWarner Losh 
663*ca987d46SWarner Losh static int
664*ca987d46SWarner Losh tftp_set_blksize(struct tftp_handle *h, const char *str)
665*ca987d46SWarner Losh {
666*ca987d46SWarner Losh         char *endptr;
667*ca987d46SWarner Losh 	int new_blksize;
668*ca987d46SWarner Losh 	int ret = 0;
669*ca987d46SWarner Losh 
670*ca987d46SWarner Losh 	if (h == NULL || str == NULL)
671*ca987d46SWarner Losh 		return (ret);
672*ca987d46SWarner Losh 
673*ca987d46SWarner Losh 	new_blksize =
674*ca987d46SWarner Losh 	    (unsigned int)strtol(str, &endptr, 0);
675*ca987d46SWarner Losh 
676*ca987d46SWarner Losh 	/*
677*ca987d46SWarner Losh 	 * Only accept blksize value if it is numeric.
678*ca987d46SWarner Losh 	 * RFC2348 specifies that acceptable values are 8-65464.
679*ca987d46SWarner Losh 	 * Let's choose a limit less than MAXRSPACE.
680*ca987d46SWarner Losh 	 */
681*ca987d46SWarner Losh 	if (*endptr == '\0' && new_blksize >= 8
682*ca987d46SWarner Losh 	    && new_blksize <= TFTP_MAX_BLKSIZE) {
683*ca987d46SWarner Losh 		h->tftp_blksize = new_blksize;
684*ca987d46SWarner Losh 		ret = 1;
685*ca987d46SWarner Losh 	}
686*ca987d46SWarner Losh 
687*ca987d46SWarner Losh 	return (ret);
688*ca987d46SWarner Losh }
689*ca987d46SWarner Losh 
690*ca987d46SWarner Losh /*
691*ca987d46SWarner Losh  * In RFC2347, the TFTP Option Acknowledgement package (OACK)
692*ca987d46SWarner Losh  * is used to acknowledge a client's option negotiation request.
693*ca987d46SWarner Losh  * The format of an OACK packet is:
694*ca987d46SWarner Losh  *    +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
695*ca987d46SWarner Losh  *    |  opc  |  opt1  | 0 | value1 | 0 |  optN  | 0 | valueN | 0 |
696*ca987d46SWarner Losh  *    +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
697*ca987d46SWarner Losh  *
698*ca987d46SWarner Losh  *    opc
699*ca987d46SWarner Losh  *       The opcode field contains a 6, for Option Acknowledgment.
700*ca987d46SWarner Losh  *
701*ca987d46SWarner Losh  *    opt1
702*ca987d46SWarner Losh  *       The first option acknowledgment, copied from the original
703*ca987d46SWarner Losh  *       request.
704*ca987d46SWarner Losh  *
705*ca987d46SWarner Losh  *    value1
706*ca987d46SWarner Losh  *       The acknowledged value associated with the first option.  If
707*ca987d46SWarner Losh  *       and how this value may differ from the original request is
708*ca987d46SWarner Losh  *       detailed in the specification for the option.
709*ca987d46SWarner Losh  *
710*ca987d46SWarner Losh  *    optN, valueN
711*ca987d46SWarner Losh  *       The final option/value acknowledgment pair.
712*ca987d46SWarner Losh  */
713*ca987d46SWarner Losh static int
714*ca987d46SWarner Losh tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len)
715*ca987d46SWarner Losh {
716*ca987d46SWarner Losh 	/*
717*ca987d46SWarner Losh 	 *  We parse the OACK strings into an array
718*ca987d46SWarner Losh 	 *  of name-value pairs.
719*ca987d46SWarner Losh 	 */
720*ca987d46SWarner Losh 	char *tftp_options[128] = { 0 };
721*ca987d46SWarner Losh 	char *val = buf;
722*ca987d46SWarner Losh 	int i = 0;
723*ca987d46SWarner Losh 	int option_idx = 0;
724*ca987d46SWarner Losh 	int blksize_is_set = 0;
725*ca987d46SWarner Losh 	int tsize = 0;
726*ca987d46SWarner Losh 
727*ca987d46SWarner Losh 	unsigned int orig_blksize;
728*ca987d46SWarner Losh 
729*ca987d46SWarner Losh 	while (option_idx < 128 && i < len) {
730*ca987d46SWarner Losh 		if (buf[i] == '\0') {
731*ca987d46SWarner Losh 			if (&buf[i] > val) {
732*ca987d46SWarner Losh 				tftp_options[option_idx] = val;
733*ca987d46SWarner Losh 				val = &buf[i] + 1;
734*ca987d46SWarner Losh 				++option_idx;
735*ca987d46SWarner Losh 			}
736*ca987d46SWarner Losh 		}
737*ca987d46SWarner Losh 		++i;
738*ca987d46SWarner Losh 	}
739*ca987d46SWarner Losh 
740*ca987d46SWarner Losh 	/* Save the block size we requested for sanity check later. */
741*ca987d46SWarner Losh 	orig_blksize = h->tftp_blksize;
742*ca987d46SWarner Losh 
743*ca987d46SWarner Losh 	/*
744*ca987d46SWarner Losh 	 * Parse individual TFTP options.
745*ca987d46SWarner Losh 	 *    * "blksize" is specified in RFC2348.
746*ca987d46SWarner Losh 	 *    * "tsize" is specified in RFC2349.
747*ca987d46SWarner Losh 	 */
748*ca987d46SWarner Losh 	for (i = 0; i < option_idx; i += 2) {
749*ca987d46SWarner Losh 	    if (strcasecmp(tftp_options[i], "blksize") == 0) {
750*ca987d46SWarner Losh 		if (i + 1 < option_idx)
751*ca987d46SWarner Losh 			blksize_is_set =
752*ca987d46SWarner Losh 			    tftp_set_blksize(h, tftp_options[i + 1]);
753*ca987d46SWarner Losh 	    } else if (strcasecmp(tftp_options[i], "tsize") == 0) {
754*ca987d46SWarner Losh 		if (i + 1 < option_idx)
755*ca987d46SWarner Losh 			tsize = strtol(tftp_options[i + 1], (char **)NULL, 10);
756*ca987d46SWarner Losh 		if (tsize != 0)
757*ca987d46SWarner Losh 			h->tftp_tsize = tsize;
758*ca987d46SWarner Losh 	    } else {
759*ca987d46SWarner Losh 		/* Do not allow any options we did not expect to be ACKed. */
760*ca987d46SWarner Losh 		printf("unexpected tftp option '%s'\n", tftp_options[i]);
761*ca987d46SWarner Losh 		return (-1);
762*ca987d46SWarner Losh 	    }
763*ca987d46SWarner Losh 	}
764*ca987d46SWarner Losh 
765*ca987d46SWarner Losh 	if (!blksize_is_set) {
766*ca987d46SWarner Losh 		/*
767*ca987d46SWarner Losh 		 * If TFTP blksize was not set, try defaulting
768*ca987d46SWarner Losh 		 * to the legacy TFTP blksize of SEGSIZE(512)
769*ca987d46SWarner Losh 		 */
770*ca987d46SWarner Losh 		h->tftp_blksize = SEGSIZE;
771*ca987d46SWarner Losh 	} else if (h->tftp_blksize > orig_blksize) {
772*ca987d46SWarner Losh 		/*
773*ca987d46SWarner Losh 		 * Server should not be proposing block sizes that
774*ca987d46SWarner Losh 		 * exceed what we said we can handle.
775*ca987d46SWarner Losh 		 */
776*ca987d46SWarner Losh 		printf("unexpected blksize %u\n", h->tftp_blksize);
777*ca987d46SWarner Losh 		return (-1);
778*ca987d46SWarner Losh 	}
779*ca987d46SWarner Losh 
780*ca987d46SWarner Losh #ifdef TFTP_DEBUG
781*ca987d46SWarner Losh 	printf("tftp_blksize: %u\n", h->tftp_blksize);
782*ca987d46SWarner Losh 	printf("tftp_tsize: %lu\n", h->tftp_tsize);
783*ca987d46SWarner Losh #endif
784*ca987d46SWarner Losh 	return 0;
785*ca987d46SWarner Losh }
786