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