xref: /titanic_51/usr/src/boot/lib/libstand/tftp.c (revision a01cd540d88e418b5c01fad14002e18c745c08be)
14a5d661aSToomas Soome /*	$NetBSD: tftp.c,v 1.4 1997/09/17 16:57:07 drochner Exp $	 */
24a5d661aSToomas Soome 
34a5d661aSToomas Soome /*
44a5d661aSToomas Soome  * Copyright (c) 1996
54a5d661aSToomas Soome  *	Matthias Drochner.  All rights reserved.
64a5d661aSToomas Soome  *
74a5d661aSToomas Soome  * Redistribution and use in source and binary forms, with or without
84a5d661aSToomas Soome  * modification, are permitted provided that the following conditions
94a5d661aSToomas Soome  * are met:
104a5d661aSToomas Soome  * 1. Redistributions of source code must retain the above copyright
114a5d661aSToomas Soome  *    notice, this list of conditions and the following disclaimer.
124a5d661aSToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
134a5d661aSToomas Soome  *    notice, this list of conditions and the following disclaimer in the
144a5d661aSToomas Soome  *    documentation and/or other materials provided with the distribution.
154a5d661aSToomas Soome  * 3. All advertising materials mentioning features or use of this software
164a5d661aSToomas Soome  *    must display the following acknowledgement:
174a5d661aSToomas Soome  *	This product includes software developed for the NetBSD Project
184a5d661aSToomas Soome  *	by Matthias Drochner.
194a5d661aSToomas Soome  * 4. The name of the author may not be used to endorse or promote products
204a5d661aSToomas Soome  *    derived from this software without specific prior written permission.
214a5d661aSToomas Soome  *
224a5d661aSToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
234a5d661aSToomas Soome  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
244a5d661aSToomas Soome  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
254a5d661aSToomas Soome  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
264a5d661aSToomas Soome  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
274a5d661aSToomas Soome  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
284a5d661aSToomas Soome  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
294a5d661aSToomas Soome  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
304a5d661aSToomas Soome  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
314a5d661aSToomas Soome  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
324a5d661aSToomas Soome  */
334a5d661aSToomas Soome 
344a5d661aSToomas Soome #include <sys/cdefs.h>
354a5d661aSToomas Soome 
364a5d661aSToomas Soome /*
374a5d661aSToomas Soome  * Simple TFTP implementation for libsa.
384a5d661aSToomas Soome  * Assumes:
394a5d661aSToomas Soome  *  - socket descriptor (int) at open_file->f_devdata
404a5d661aSToomas Soome  *  - server host IP in global servip
414a5d661aSToomas Soome  * Restrictions:
424a5d661aSToomas Soome  *  - read only
434a5d661aSToomas Soome  *  - lseek only with SEEK_SET or SEEK_CUR
444a5d661aSToomas Soome  *  - no big time differences between transfers (<tftp timeout)
454a5d661aSToomas Soome  */
464a5d661aSToomas Soome 
474a5d661aSToomas Soome #include <sys/types.h>
484a5d661aSToomas Soome #include <sys/stat.h>
494a5d661aSToomas Soome #include <netinet/in.h>
504a5d661aSToomas Soome #include <netinet/udp.h>
514a5d661aSToomas Soome #include <netinet/in_systm.h>
524a5d661aSToomas Soome #include <arpa/tftp.h>
534a5d661aSToomas Soome 
544a5d661aSToomas Soome #include <string.h>
554a5d661aSToomas Soome 
564a5d661aSToomas Soome #include "stand.h"
574a5d661aSToomas Soome #include "net.h"
584a5d661aSToomas Soome #include "netif.h"
594a5d661aSToomas Soome 
604a5d661aSToomas Soome #include "tftp.h"
614a5d661aSToomas Soome 
624a5d661aSToomas Soome struct tftp_handle;
634a5d661aSToomas Soome 
644a5d661aSToomas Soome static int	tftp_open(const char *path, struct open_file *f);
654a5d661aSToomas Soome static int	tftp_close(struct open_file *f);
664a5d661aSToomas Soome static int	tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len);
674a5d661aSToomas Soome static int	tftp_read(struct open_file *f, void *buf, size_t size, size_t *resid);
684a5d661aSToomas Soome static int	tftp_write(struct open_file *f, void *buf, size_t size, size_t *resid);
694a5d661aSToomas Soome static off_t	tftp_seek(struct open_file *f, off_t offset, int where);
704a5d661aSToomas Soome static int	tftp_set_blksize(struct tftp_handle *h, const char *str);
714a5d661aSToomas Soome static int	tftp_stat(struct open_file *f, struct stat *sb);
724a5d661aSToomas Soome static ssize_t sendrecv_tftp(struct tftp_handle *h,
734a5d661aSToomas Soome     ssize_t (*sproc)(struct iodesc *, void *, size_t),
744a5d661aSToomas Soome     void *sbuf, size_t ssize,
757b2a1233SToomas Soome     ssize_t (*rproc)(struct tftp_handle *h, void **, void **, time_t, unsigned short *),
767b2a1233SToomas Soome     void **, void **, unsigned short *rtype);
774a5d661aSToomas Soome 
784a5d661aSToomas Soome struct fs_ops tftp_fsops = {
794a5d661aSToomas Soome 	"tftp",
804a5d661aSToomas Soome 	tftp_open,
814a5d661aSToomas Soome 	tftp_close,
824a5d661aSToomas Soome 	tftp_read,
834a5d661aSToomas Soome 	tftp_write,
844a5d661aSToomas Soome 	tftp_seek,
854a5d661aSToomas Soome 	tftp_stat,
864a5d661aSToomas Soome 	null_readdir
874a5d661aSToomas Soome };
884a5d661aSToomas Soome 
894a5d661aSToomas Soome extern struct in_addr servip;
904a5d661aSToomas Soome 
914a5d661aSToomas Soome static int      tftpport = 2000;
924a5d661aSToomas Soome static int	is_open = 0;
934a5d661aSToomas Soome 
944a5d661aSToomas Soome /*
954a5d661aSToomas Soome  * The legacy TFTP_BLKSIZE value was SEGSIZE(512).
964a5d661aSToomas Soome  * TFTP_REQUESTED_BLKSIZE of 1428 is (Ethernet MTU, less the TFTP, UDP and
974a5d661aSToomas Soome  * IP header lengths).
984a5d661aSToomas Soome  */
994a5d661aSToomas Soome #define TFTP_REQUESTED_BLKSIZE 1428
1004a5d661aSToomas Soome 
1014a5d661aSToomas Soome /*
1024a5d661aSToomas Soome  * Choose a blksize big enough so we can test with Ethernet
1034a5d661aSToomas Soome  * Jumbo frames in the future.
1044a5d661aSToomas Soome  */
1054a5d661aSToomas Soome #define TFTP_MAX_BLKSIZE 9008
1064a5d661aSToomas Soome 
1074a5d661aSToomas Soome struct tftp_handle {
1084a5d661aSToomas Soome 	struct iodesc  *iodesc;
1094a5d661aSToomas Soome 	int             currblock;	/* contents of lastdata */
1104a5d661aSToomas Soome 	int             islastblock;	/* flag */
1114a5d661aSToomas Soome 	int             validsize;
1124a5d661aSToomas Soome 	int             off;
1134a5d661aSToomas Soome 	char           *path;	/* saved for re-requests */
1144a5d661aSToomas Soome 	unsigned int	tftp_blksize;
1154a5d661aSToomas Soome 	unsigned long	tftp_tsize;
1167b2a1233SToomas Soome 	void		*pkt;
1177b2a1233SToomas Soome 	struct tftphdr	*tftp_hdr;
1184a5d661aSToomas Soome };
1194a5d661aSToomas Soome 
1204a5d661aSToomas Soome #define	TFTP_MAX_ERRCODE EOPTNEG
1214a5d661aSToomas Soome static const int tftperrors[TFTP_MAX_ERRCODE + 1] = {
1224a5d661aSToomas Soome 	0,			/* ??? */
1234a5d661aSToomas Soome 	ENOENT,
1244a5d661aSToomas Soome 	EPERM,
1254a5d661aSToomas Soome 	ENOSPC,
1264a5d661aSToomas Soome 	EINVAL,			/* ??? */
1274a5d661aSToomas Soome 	EINVAL,			/* ??? */
1284a5d661aSToomas Soome 	EEXIST,
1294a5d661aSToomas Soome 	EINVAL,			/* ??? */
1304a5d661aSToomas Soome 	EINVAL,			/* Option negotiation failed. */
1314a5d661aSToomas Soome };
1324a5d661aSToomas Soome 
1334a5d661aSToomas Soome static int  tftp_getnextblock(struct tftp_handle *h);
1344a5d661aSToomas Soome 
1354a5d661aSToomas Soome /* send error message back. */
1364a5d661aSToomas Soome static void
1374a5d661aSToomas Soome tftp_senderr(struct tftp_handle *h, u_short errcode, const char *msg)
1384a5d661aSToomas Soome {
1394a5d661aSToomas Soome 	struct {
1404a5d661aSToomas Soome 		u_char header[HEADER_SIZE];
1414a5d661aSToomas Soome 		struct tftphdr  t;
1424a5d661aSToomas Soome 		u_char space[63]; /* +1 from t */
1434a5d661aSToomas Soome 	} __packed __aligned(4) wbuf;
1444a5d661aSToomas Soome 	char           *wtail;
1454a5d661aSToomas Soome 	int             len;
1464a5d661aSToomas Soome 
1474a5d661aSToomas Soome 	len = strlen(msg);
1484a5d661aSToomas Soome 	if (len > sizeof(wbuf.space))
1494a5d661aSToomas Soome 		len = sizeof(wbuf.space);
1504a5d661aSToomas Soome 
1514a5d661aSToomas Soome 	wbuf.t.th_opcode = htons((u_short) ERROR);
1524a5d661aSToomas Soome 	wbuf.t.th_code   = htons(errcode);
1534a5d661aSToomas Soome 
1544a5d661aSToomas Soome 	wtail = wbuf.t.th_msg;
1554a5d661aSToomas Soome 	bcopy(msg, wtail, len);
1564a5d661aSToomas Soome 	wtail[len] = '\0';
1574a5d661aSToomas Soome 	wtail += len + 1;
1584a5d661aSToomas Soome 
1594a5d661aSToomas Soome 	sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
1604a5d661aSToomas Soome }
1614a5d661aSToomas Soome 
1624a5d661aSToomas Soome static void
1634a5d661aSToomas Soome tftp_sendack(struct tftp_handle *h)
1644a5d661aSToomas Soome {
1654a5d661aSToomas Soome 	struct {
1664a5d661aSToomas Soome 		u_char header[HEADER_SIZE];
1674a5d661aSToomas Soome 		struct tftphdr  t;
1684a5d661aSToomas Soome 	} __packed __aligned(4) wbuf;
1694a5d661aSToomas Soome 	char           *wtail;
1704a5d661aSToomas Soome 
1714a5d661aSToomas Soome 	wbuf.t.th_opcode = htons((u_short) ACK);
1724a5d661aSToomas Soome 	wtail = (char *) &wbuf.t.th_block;
1734a5d661aSToomas Soome 	wbuf.t.th_block = htons((u_short) h->currblock);
1744a5d661aSToomas Soome 	wtail += 2;
1754a5d661aSToomas Soome 
1764a5d661aSToomas Soome 	sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
1774a5d661aSToomas Soome }
1784a5d661aSToomas Soome 
1794a5d661aSToomas Soome static ssize_t
1807b2a1233SToomas Soome recvtftp(struct tftp_handle *h, void **pkt, void **payload, time_t tleft,
1814a5d661aSToomas Soome     unsigned short *rtype)
1824a5d661aSToomas Soome {
1834a5d661aSToomas Soome 	struct iodesc *d = h->iodesc;
1844a5d661aSToomas Soome 	struct tftphdr *t;
1857b2a1233SToomas Soome 	void *ptr = NULL;
1867b2a1233SToomas Soome 	ssize_t len;
1874a5d661aSToomas Soome 
1884a5d661aSToomas Soome 	errno = 0;
1894a5d661aSToomas Soome 
1907b2a1233SToomas Soome 	len = readudp(d, &ptr, (void **)&t, tleft);
1914a5d661aSToomas Soome 
1927b2a1233SToomas Soome 	if (len < 4) {
1937b2a1233SToomas Soome 		free(ptr);
1944a5d661aSToomas Soome 		return (-1);
1957b2a1233SToomas Soome 	}
1964a5d661aSToomas Soome 
1974a5d661aSToomas Soome 	*rtype = ntohs(t->th_opcode);
1984a5d661aSToomas Soome 	switch (ntohs(t->th_opcode)) {
1994a5d661aSToomas Soome 	case DATA: {
2004a5d661aSToomas Soome 		int got;
2014a5d661aSToomas Soome 
2024a5d661aSToomas Soome 		if (htons(t->th_block) != (u_short) d->xid) {
2034a5d661aSToomas Soome 			/*
2044a5d661aSToomas Soome 			 * Expected block?
2054a5d661aSToomas Soome 			 */
2067b2a1233SToomas Soome 			free(ptr);
2074a5d661aSToomas Soome 			return (-1);
2084a5d661aSToomas Soome 		}
2094a5d661aSToomas Soome 		if (d->xid == 1) {
2104a5d661aSToomas Soome 			/*
2114a5d661aSToomas Soome 			 * First data packet from new port.
2124a5d661aSToomas Soome 			 */
2134a5d661aSToomas Soome 			struct udphdr *uh;
2147b2a1233SToomas Soome 			uh = (struct udphdr *) t - 1;
2154a5d661aSToomas Soome 			d->destport = uh->uh_sport;
2164a5d661aSToomas Soome 		} /* else check uh_sport has not changed??? */
2174a5d661aSToomas Soome 		got = len - (t->th_data - (char *)t);
2187b2a1233SToomas Soome 		*pkt = ptr;
2197b2a1233SToomas Soome 		*payload = t;
2207b2a1233SToomas Soome 		return (got);
2214a5d661aSToomas Soome 	}
2224a5d661aSToomas Soome 	case ERROR:
2234a5d661aSToomas Soome 		if ((unsigned) ntohs(t->th_code) > TFTP_MAX_ERRCODE) {
2244a5d661aSToomas Soome 			printf("illegal tftp error %d\n", ntohs(t->th_code));
2254a5d661aSToomas Soome 			errno = EIO;
2264a5d661aSToomas Soome 		} else {
2274a5d661aSToomas Soome #ifdef TFTP_DEBUG
2284a5d661aSToomas Soome 			printf("tftp-error %d\n", ntohs(t->th_code));
2294a5d661aSToomas Soome #endif
2304a5d661aSToomas Soome 			errno = tftperrors[ntohs(t->th_code)];
2314a5d661aSToomas Soome 		}
2327b2a1233SToomas Soome 		free(ptr);
2334a5d661aSToomas Soome 		return (-1);
2344a5d661aSToomas Soome 	case OACK: {
2354a5d661aSToomas Soome 		struct udphdr *uh;
2364a5d661aSToomas Soome 		int tftp_oack_len;
2374a5d661aSToomas Soome 
2384a5d661aSToomas Soome 		/*
2394a5d661aSToomas Soome 		 * Unexpected OACK. TFTP transfer already in progress.
2404a5d661aSToomas Soome 		 * Drop the pkt.
2414a5d661aSToomas Soome 		 */
2424a5d661aSToomas Soome 		if (d->xid != 1) {
2437b2a1233SToomas Soome 			free(ptr);
2444a5d661aSToomas Soome 			return (-1);
2454a5d661aSToomas Soome 		}
2464a5d661aSToomas Soome 
2474a5d661aSToomas Soome 		/*
2484a5d661aSToomas Soome 		 * Remember which port this OACK came from, because we need
2494a5d661aSToomas Soome 		 * to send the ACK or errors back to it.
2504a5d661aSToomas Soome 		 */
2517b2a1233SToomas Soome 		uh = (struct udphdr *) t - 1;
2524a5d661aSToomas Soome 		d->destport = uh->uh_sport;
2534a5d661aSToomas Soome 
2544a5d661aSToomas Soome 		/* Parse options ACK-ed by the server. */
2554a5d661aSToomas Soome 		tftp_oack_len = len - sizeof(t->th_opcode);
2564a5d661aSToomas Soome 		if (tftp_parse_oack(h, t->th_u.tu_stuff, tftp_oack_len) != 0) {
2574a5d661aSToomas Soome 			tftp_senderr(h, EOPTNEG, "Malformed OACK");
2584a5d661aSToomas Soome 			errno = EIO;
2597b2a1233SToomas Soome 			free(ptr);
2604a5d661aSToomas Soome 			return (-1);
2614a5d661aSToomas Soome 		}
2627b2a1233SToomas Soome 		*pkt = ptr;
2637b2a1233SToomas Soome 		*payload = t;
2644a5d661aSToomas Soome 		return (0);
2654a5d661aSToomas Soome 	}
2664a5d661aSToomas Soome 	default:
2674a5d661aSToomas Soome #ifdef TFTP_DEBUG
2684a5d661aSToomas Soome 		printf("tftp type %d not handled\n", ntohs(t->th_opcode));
2694a5d661aSToomas Soome #endif
2707b2a1233SToomas Soome 		free(ptr);
2714a5d661aSToomas Soome 		return (-1);
2724a5d661aSToomas Soome 	}
2734a5d661aSToomas Soome }
2744a5d661aSToomas Soome 
2754a5d661aSToomas Soome /* send request, expect first block (or error) */
2764a5d661aSToomas Soome static int
2774a5d661aSToomas Soome tftp_makereq(struct tftp_handle *h)
2784a5d661aSToomas Soome {
2794a5d661aSToomas Soome 	struct {
2804a5d661aSToomas Soome 		u_char header[HEADER_SIZE];
2814a5d661aSToomas Soome 		struct tftphdr  t;
2824a5d661aSToomas Soome 		u_char space[FNAME_SIZE + 6];
2834a5d661aSToomas Soome 	} __packed __aligned(4) wbuf;
2844a5d661aSToomas Soome 	char           *wtail;
2854a5d661aSToomas Soome 	int             l;
2864a5d661aSToomas Soome 	ssize_t         res;
2877b2a1233SToomas Soome 	void *pkt;
2884a5d661aSToomas Soome 	struct tftphdr *t;
2894a5d661aSToomas Soome 	char *tftp_blksize = NULL;
2904a5d661aSToomas Soome 	int blksize_l;
2914a5d661aSToomas Soome 	unsigned short rtype = 0;
2924a5d661aSToomas Soome 
2934a5d661aSToomas Soome 	/*
2944a5d661aSToomas Soome 	 * Allow overriding default TFTP block size by setting
2954a5d661aSToomas Soome 	 * a tftp.blksize environment variable.
2964a5d661aSToomas Soome 	 */
2974a5d661aSToomas Soome 	if ((tftp_blksize = getenv("tftp.blksize")) != NULL) {
2984a5d661aSToomas Soome 		tftp_set_blksize(h, tftp_blksize);
2994a5d661aSToomas Soome 	}
3004a5d661aSToomas Soome 
3014a5d661aSToomas Soome 	wbuf.t.th_opcode = htons((u_short) RRQ);
3024a5d661aSToomas Soome 	wtail = wbuf.t.th_stuff;
3034a5d661aSToomas Soome 	l = strlen(h->path);
3044a5d661aSToomas Soome #ifdef TFTP_PREPEND_PATH
3054a5d661aSToomas Soome 	if (l > FNAME_SIZE - (sizeof(TFTP_PREPEND_PATH) - 1))
3064a5d661aSToomas Soome 		return (ENAMETOOLONG);
3074a5d661aSToomas Soome 	bcopy(TFTP_PREPEND_PATH, wtail, sizeof(TFTP_PREPEND_PATH) - 1);
3084a5d661aSToomas Soome 	wtail += sizeof(TFTP_PREPEND_PATH) - 1;
3094a5d661aSToomas Soome #else
3104a5d661aSToomas Soome 	if (l > FNAME_SIZE)
3114a5d661aSToomas Soome 		return (ENAMETOOLONG);
3124a5d661aSToomas Soome #endif
3134a5d661aSToomas Soome 	bcopy(h->path, wtail, l + 1);
3144a5d661aSToomas Soome 	wtail += l + 1;
3154a5d661aSToomas Soome 	bcopy("octet", wtail, 6);
3164a5d661aSToomas Soome 	wtail += 6;
3174a5d661aSToomas Soome 	bcopy("blksize", wtail, 8);
3184a5d661aSToomas Soome 	wtail += 8;
3194a5d661aSToomas Soome 	blksize_l = sprintf(wtail, "%d", h->tftp_blksize);
3204a5d661aSToomas Soome 	wtail += blksize_l + 1;
3214a5d661aSToomas Soome 	bcopy("tsize", wtail, 6);
3224a5d661aSToomas Soome 	wtail += 6;
3234a5d661aSToomas Soome 	bcopy("0", wtail, 2);
3244a5d661aSToomas Soome 	wtail += 2;
3254a5d661aSToomas Soome 
3264a5d661aSToomas Soome 	/* h->iodesc->myport = htons(--tftpport); */
3274a5d661aSToomas Soome 	h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
3284a5d661aSToomas Soome 	h->iodesc->destport = htons(IPPORT_TFTP);
3294a5d661aSToomas Soome 	h->iodesc->xid = 1;	/* expected block */
3304a5d661aSToomas Soome 
3314a5d661aSToomas Soome 	h->currblock = 0;
3324a5d661aSToomas Soome 	h->islastblock = 0;
3334a5d661aSToomas Soome 	h->validsize = 0;
3344a5d661aSToomas Soome 
3357b2a1233SToomas Soome 	pkt = NULL;
3364a5d661aSToomas Soome 	res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
3377b2a1233SToomas Soome 		       &recvtftp, &pkt, (void **)&t, &rtype);
3387b2a1233SToomas Soome 	if (res == -1) {
3397b2a1233SToomas Soome 		free(pkt);
3407b2a1233SToomas Soome 		return (errno);
3417b2a1233SToomas Soome 	}
3427b2a1233SToomas Soome 
3437b2a1233SToomas Soome 	free(h->pkt);
3447b2a1233SToomas Soome 	h->pkt = pkt;
3457b2a1233SToomas Soome 	h->tftp_hdr = t;
3464a5d661aSToomas Soome 
3474a5d661aSToomas Soome 	if (rtype == OACK)
3484a5d661aSToomas Soome 		return (tftp_getnextblock(h));
3494a5d661aSToomas Soome 
3504a5d661aSToomas Soome 	/* Server ignored our blksize request, revert to TFTP default. */
3514a5d661aSToomas Soome 	h->tftp_blksize = SEGSIZE;
3524a5d661aSToomas Soome 
3534a5d661aSToomas Soome 	switch (rtype) {
3544a5d661aSToomas Soome 		case DATA: {
3554a5d661aSToomas Soome 			h->currblock = 1;
3564a5d661aSToomas Soome 			h->validsize = res;
3574a5d661aSToomas Soome 			h->islastblock = 0;
3584a5d661aSToomas Soome 			if (res < h->tftp_blksize) {
3594a5d661aSToomas Soome 				h->islastblock = 1;	/* very short file */
3604a5d661aSToomas Soome 				tftp_sendack(h);
3614a5d661aSToomas Soome 			}
3624a5d661aSToomas Soome 			return (0);
3634a5d661aSToomas Soome 		}
3644a5d661aSToomas Soome 		case ERROR:
3654a5d661aSToomas Soome 		default:
3664a5d661aSToomas Soome 			return (errno);
3674a5d661aSToomas Soome 	}
3684a5d661aSToomas Soome 
3694a5d661aSToomas Soome }
3704a5d661aSToomas Soome 
3714a5d661aSToomas Soome /* ack block, expect next */
3724a5d661aSToomas Soome static int
3734a5d661aSToomas Soome tftp_getnextblock(struct tftp_handle *h)
3744a5d661aSToomas Soome {
3754a5d661aSToomas Soome 	struct {
3764a5d661aSToomas Soome 		u_char header[HEADER_SIZE];
3774a5d661aSToomas Soome 		struct tftphdr t;
3784a5d661aSToomas Soome 	} __packed __aligned(4) wbuf;
3794a5d661aSToomas Soome 	char           *wtail;
3804a5d661aSToomas Soome 	int             res;
3817b2a1233SToomas Soome 	void *pkt;
3824a5d661aSToomas Soome 	struct tftphdr *t;
3834a5d661aSToomas Soome 	unsigned short rtype = 0;
3844a5d661aSToomas Soome 	wbuf.t.th_opcode = htons((u_short) ACK);
3854a5d661aSToomas Soome 	wtail = (char *) &wbuf.t.th_block;
3864a5d661aSToomas Soome 	wbuf.t.th_block = htons((u_short) h->currblock);
3874a5d661aSToomas Soome 	wtail += 2;
3884a5d661aSToomas Soome 
3894a5d661aSToomas Soome 	h->iodesc->xid = h->currblock + 1;	/* expected block */
3904a5d661aSToomas Soome 
3917b2a1233SToomas Soome 	pkt = NULL;
3924a5d661aSToomas Soome 	res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
3937b2a1233SToomas Soome 		       &recvtftp, &pkt, (void **)&t, &rtype);
3944a5d661aSToomas Soome 
3957b2a1233SToomas Soome 	if (res == -1) {		/* 0 is OK! */
3967b2a1233SToomas Soome 		free(pkt);
3974a5d661aSToomas Soome 		return (errno);
3987b2a1233SToomas Soome 	}
3994a5d661aSToomas Soome 
4007b2a1233SToomas Soome 	free(h->pkt);
4017b2a1233SToomas Soome 	h->pkt = pkt;
4027b2a1233SToomas Soome 	h->tftp_hdr = t;
4034a5d661aSToomas Soome 	h->currblock++;
4044a5d661aSToomas Soome 	h->validsize = res;
4054a5d661aSToomas Soome 	if (res < h->tftp_blksize)
4064a5d661aSToomas Soome 		h->islastblock = 1;	/* EOF */
4074a5d661aSToomas Soome 
4084a5d661aSToomas Soome 	if (h->islastblock == 1) {
4094a5d661aSToomas Soome 		/* Send an ACK for the last block */
4104a5d661aSToomas Soome 		wbuf.t.th_block = htons((u_short) h->currblock);
4114a5d661aSToomas Soome 		sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t);
4124a5d661aSToomas Soome 	}
4134a5d661aSToomas Soome 
4144a5d661aSToomas Soome 	return (0);
4154a5d661aSToomas Soome }
4164a5d661aSToomas Soome 
4174a5d661aSToomas Soome static int
4184a5d661aSToomas Soome tftp_open(const char *path, struct open_file *f)
4194a5d661aSToomas Soome {
4204a5d661aSToomas Soome 	struct tftp_handle *tftpfile;
4214a5d661aSToomas Soome 	struct iodesc  *io;
4224a5d661aSToomas Soome 	int             res;
4234a5d661aSToomas Soome 	size_t          pathsize;
4244a5d661aSToomas Soome 	const char     *extraslash;
4254a5d661aSToomas Soome 
42618609d04SToomas Soome 	if (netproto != NET_TFTP)
42718609d04SToomas Soome 		return (EINVAL);
42818609d04SToomas Soome 
4297b2a1233SToomas Soome 	if (f->f_dev->dv_type != DEVT_NET)
4304a5d661aSToomas Soome 		return (EINVAL);
4314a5d661aSToomas Soome 
4324a5d661aSToomas Soome 	if (is_open)
4334a5d661aSToomas Soome 		return (EBUSY);
4344a5d661aSToomas Soome 
4354a5d661aSToomas Soome 	tftpfile = (struct tftp_handle *) malloc(sizeof(*tftpfile));
4364a5d661aSToomas Soome 	if (!tftpfile)
4374a5d661aSToomas Soome 		return (ENOMEM);
4384a5d661aSToomas Soome 
4394a5d661aSToomas Soome 	memset(tftpfile, 0, sizeof(*tftpfile));
4404a5d661aSToomas Soome 	tftpfile->tftp_blksize = TFTP_REQUESTED_BLKSIZE;
4414a5d661aSToomas Soome 	tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata));
4424a5d661aSToomas Soome 	if (io == NULL)
4434a5d661aSToomas Soome 		return (EINVAL);
4444a5d661aSToomas Soome 
4454a5d661aSToomas Soome 	io->destip = servip;
4464a5d661aSToomas Soome 	tftpfile->off = 0;
4474a5d661aSToomas Soome 	pathsize = (strlen(rootpath) + 1 + strlen(path) + 1) * sizeof(char);
4484a5d661aSToomas Soome 	tftpfile->path = malloc(pathsize);
4494a5d661aSToomas Soome 	if (tftpfile->path == NULL) {
4504a5d661aSToomas Soome 		free(tftpfile);
4514a5d661aSToomas Soome 		return(ENOMEM);
4524a5d661aSToomas Soome 	}
4534a5d661aSToomas Soome 	if (rootpath[strlen(rootpath) - 1] == '/' || path[0] == '/')
4544a5d661aSToomas Soome 		extraslash = "";
4554a5d661aSToomas Soome 	else
4564a5d661aSToomas Soome 		extraslash = "/";
4574a5d661aSToomas Soome 	res = snprintf(tftpfile->path, pathsize, "%s%s%s",
4584a5d661aSToomas Soome 	    rootpath, extraslash, path);
4594a5d661aSToomas Soome 	if (res < 0 || res > pathsize) {
4604a5d661aSToomas Soome 		free(tftpfile->path);
4614a5d661aSToomas Soome 		free(tftpfile);
4624a5d661aSToomas Soome 		return(ENOMEM);
4634a5d661aSToomas Soome 	}
4644a5d661aSToomas Soome 
4654a5d661aSToomas Soome 	res = tftp_makereq(tftpfile);
4664a5d661aSToomas Soome 
4674a5d661aSToomas Soome 	if (res) {
4684a5d661aSToomas Soome 		free(tftpfile->path);
4697b2a1233SToomas Soome 		free(tftpfile->pkt);
4704a5d661aSToomas Soome 		free(tftpfile);
4714a5d661aSToomas Soome 		return (res);
4724a5d661aSToomas Soome 	}
4734a5d661aSToomas Soome 	f->f_fsdata = (void *) tftpfile;
4744a5d661aSToomas Soome 	is_open = 1;
4754a5d661aSToomas Soome 	return (0);
4764a5d661aSToomas Soome }
4774a5d661aSToomas Soome 
4784a5d661aSToomas Soome static int
4794a5d661aSToomas Soome tftp_read(struct open_file *f, void *addr, size_t size,
4804a5d661aSToomas Soome     size_t *resid /* out */)
4814a5d661aSToomas Soome {
4824a5d661aSToomas Soome 	struct tftp_handle *tftpfile;
4834a5d661aSToomas Soome 	tftpfile = (struct tftp_handle *) f->f_fsdata;
4844a5d661aSToomas Soome 
4854a5d661aSToomas Soome 	while (size > 0) {
4864a5d661aSToomas Soome 		int needblock, count;
4874a5d661aSToomas Soome 
4884a5d661aSToomas Soome 		twiddle(32);
4894a5d661aSToomas Soome 
4904a5d661aSToomas Soome 		needblock = tftpfile->off / tftpfile->tftp_blksize + 1;
4914a5d661aSToomas Soome 
4924a5d661aSToomas Soome 		if (tftpfile->currblock > needblock) {	/* seek backwards */
4934a5d661aSToomas Soome 			tftp_senderr(tftpfile, 0, "No error: read aborted");
4944a5d661aSToomas Soome 			tftp_makereq(tftpfile);	/* no error check, it worked
4954a5d661aSToomas Soome 						 * for open */
4964a5d661aSToomas Soome 		}
4974a5d661aSToomas Soome 
4984a5d661aSToomas Soome 		while (tftpfile->currblock < needblock) {
4994a5d661aSToomas Soome 			int res;
5004a5d661aSToomas Soome 
5014a5d661aSToomas Soome 			res = tftp_getnextblock(tftpfile);
5024a5d661aSToomas Soome 			if (res) {	/* no answer */
5034a5d661aSToomas Soome #ifdef TFTP_DEBUG
5044a5d661aSToomas Soome 				printf("tftp: read error\n");
5054a5d661aSToomas Soome #endif
5064a5d661aSToomas Soome 				return (res);
5074a5d661aSToomas Soome 			}
5084a5d661aSToomas Soome 			if (tftpfile->islastblock)
5094a5d661aSToomas Soome 				break;
5104a5d661aSToomas Soome 		}
5114a5d661aSToomas Soome 
5124a5d661aSToomas Soome 		if (tftpfile->currblock == needblock) {
5134a5d661aSToomas Soome 			int offinblock, inbuffer;
5144a5d661aSToomas Soome 
5154a5d661aSToomas Soome 			offinblock = tftpfile->off % tftpfile->tftp_blksize;
5164a5d661aSToomas Soome 
5174a5d661aSToomas Soome 			inbuffer = tftpfile->validsize - offinblock;
5184a5d661aSToomas Soome 			if (inbuffer < 0) {
5194a5d661aSToomas Soome #ifdef TFTP_DEBUG
5204a5d661aSToomas Soome 				printf("tftp: invalid offset %d\n",
5214a5d661aSToomas Soome 				    tftpfile->off);
5224a5d661aSToomas Soome #endif
5234a5d661aSToomas Soome 				return (EINVAL);
5244a5d661aSToomas Soome 			}
5254a5d661aSToomas Soome 			count = (size < inbuffer ? size : inbuffer);
5267b2a1233SToomas Soome 			bcopy(tftpfile->tftp_hdr->th_data + offinblock,
5274a5d661aSToomas Soome 			    addr, count);
5284a5d661aSToomas Soome 
5294a5d661aSToomas Soome 			addr = (char *)addr + count;
5304a5d661aSToomas Soome 			tftpfile->off += count;
5314a5d661aSToomas Soome 			size -= count;
5324a5d661aSToomas Soome 
5334a5d661aSToomas Soome 			if ((tftpfile->islastblock) && (count == inbuffer))
5344a5d661aSToomas Soome 				break;	/* EOF */
5354a5d661aSToomas Soome 		} else {
5364a5d661aSToomas Soome #ifdef TFTP_DEBUG
5374a5d661aSToomas Soome 			printf("tftp: block %d not found\n", needblock);
5384a5d661aSToomas Soome #endif
5394a5d661aSToomas Soome 			return (EINVAL);
5404a5d661aSToomas Soome 		}
5414a5d661aSToomas Soome 
5424a5d661aSToomas Soome 	}
5434a5d661aSToomas Soome 
5444a5d661aSToomas Soome 	if (resid)
5454a5d661aSToomas Soome 		*resid = size;
5464a5d661aSToomas Soome 	return (0);
5474a5d661aSToomas Soome }
5484a5d661aSToomas Soome 
5494a5d661aSToomas Soome static int
5504a5d661aSToomas Soome tftp_close(struct open_file *f)
5514a5d661aSToomas Soome {
5524a5d661aSToomas Soome 	struct tftp_handle *tftpfile;
5534a5d661aSToomas Soome 	tftpfile = (struct tftp_handle *) f->f_fsdata;
5544a5d661aSToomas Soome 
5554a5d661aSToomas Soome 	/* let it time out ... */
5564a5d661aSToomas Soome 
5574a5d661aSToomas Soome 	if (tftpfile) {
5584a5d661aSToomas Soome 		free(tftpfile->path);
5597b2a1233SToomas Soome 		free(tftpfile->pkt);
5604a5d661aSToomas Soome 		free(tftpfile);
5614a5d661aSToomas Soome 	}
5624a5d661aSToomas Soome 	is_open = 0;
5634a5d661aSToomas Soome 	return (0);
5644a5d661aSToomas Soome }
5654a5d661aSToomas Soome 
5664a5d661aSToomas Soome static int
5674a5d661aSToomas Soome tftp_write(struct open_file *f __unused, void *start __unused, size_t size __unused,
5684a5d661aSToomas Soome     size_t *resid __unused /* out */)
5694a5d661aSToomas Soome {
5704a5d661aSToomas Soome 	return (EROFS);
5714a5d661aSToomas Soome }
5724a5d661aSToomas Soome 
5734a5d661aSToomas Soome static int
5744a5d661aSToomas Soome tftp_stat(struct open_file *f, struct stat *sb)
5754a5d661aSToomas Soome {
5764a5d661aSToomas Soome 	struct tftp_handle *tftpfile;
5774a5d661aSToomas Soome 	tftpfile = (struct tftp_handle *) f->f_fsdata;
5784a5d661aSToomas Soome 
5794a5d661aSToomas Soome 	sb->st_mode = 0444 | S_IFREG;
5804a5d661aSToomas Soome 	sb->st_nlink = 1;
5814a5d661aSToomas Soome 	sb->st_uid = 0;
5824a5d661aSToomas Soome 	sb->st_gid = 0;
5834a5d661aSToomas Soome 	sb->st_size = (off_t) tftpfile->tftp_tsize;
5844a5d661aSToomas Soome 	return (0);
5854a5d661aSToomas Soome }
5864a5d661aSToomas Soome 
5874a5d661aSToomas Soome static off_t
5884a5d661aSToomas Soome tftp_seek(struct open_file *f, off_t offset, int where)
5894a5d661aSToomas Soome {
5904a5d661aSToomas Soome 	struct tftp_handle *tftpfile;
5914a5d661aSToomas Soome 	tftpfile = (struct tftp_handle *) f->f_fsdata;
5924a5d661aSToomas Soome 
5934a5d661aSToomas Soome 	switch (where) {
5944a5d661aSToomas Soome 	case SEEK_SET:
5954a5d661aSToomas Soome 		tftpfile->off = offset;
5964a5d661aSToomas Soome 		break;
5974a5d661aSToomas Soome 	case SEEK_CUR:
5984a5d661aSToomas Soome 		tftpfile->off += offset;
5994a5d661aSToomas Soome 		break;
6004a5d661aSToomas Soome 	default:
6014a5d661aSToomas Soome 		errno = EOFFSET;
6024a5d661aSToomas Soome 		return (-1);
6034a5d661aSToomas Soome 	}
6044a5d661aSToomas Soome 	return (tftpfile->off);
6054a5d661aSToomas Soome }
6064a5d661aSToomas Soome 
6074a5d661aSToomas Soome static ssize_t
6084a5d661aSToomas Soome sendrecv_tftp(struct tftp_handle *h,
6094a5d661aSToomas Soome     ssize_t (*sproc)(struct iodesc *, void *, size_t),
6104a5d661aSToomas Soome     void *sbuf, size_t ssize,
6117b2a1233SToomas Soome     ssize_t (*rproc)(struct tftp_handle *, void **, void **, time_t,
6127b2a1233SToomas Soome     unsigned short *),
6137b2a1233SToomas Soome     void **pkt, void **payload, unsigned short *rtype)
6144a5d661aSToomas Soome {
6154a5d661aSToomas Soome 	struct iodesc *d = h->iodesc;
6164a5d661aSToomas Soome 	ssize_t cc;
6174a5d661aSToomas Soome 	time_t t, t1, tleft;
6184a5d661aSToomas Soome 
6194a5d661aSToomas Soome #ifdef TFTP_DEBUG
6204a5d661aSToomas Soome 	if (debug)
6214a5d661aSToomas Soome 		printf("sendrecv: called\n");
6224a5d661aSToomas Soome #endif
6234a5d661aSToomas Soome 
6244a5d661aSToomas Soome 	tleft = MINTMO;
6254a5d661aSToomas Soome 	t = t1 = getsecs();
6264a5d661aSToomas Soome 	for (;;) {
6274a5d661aSToomas Soome 		if ((getsecs() - t) > MAXTMO) {
6284a5d661aSToomas Soome 			errno = ETIMEDOUT;
6294a5d661aSToomas Soome 			return -1;
6304a5d661aSToomas Soome 		}
6314a5d661aSToomas Soome 
6324a5d661aSToomas Soome 		cc = (*sproc)(d, sbuf, ssize);
6334a5d661aSToomas Soome 		if (cc != -1 && cc < ssize)
6344a5d661aSToomas Soome 			panic("sendrecv: short write! (%zd < %zu)",
6354a5d661aSToomas Soome 			    cc, ssize);
6364a5d661aSToomas Soome 
6374a5d661aSToomas Soome 		if (cc == -1) {
6384a5d661aSToomas Soome 			/* Error on transmit; wait before retrying */
6394a5d661aSToomas Soome 			while ((getsecs() - t1) < tleft);
640*a01cd540SToomas Soome 			t1 = getsecs();
6414a5d661aSToomas Soome 			continue;
6424a5d661aSToomas Soome 		}
6434a5d661aSToomas Soome 
644*a01cd540SToomas Soome 		t = t1 = getsecs();
6454a5d661aSToomas Soome recvnext:
646*a01cd540SToomas Soome 		if ((getsecs() - t) > MAXTMO) {
647*a01cd540SToomas Soome 			errno = ETIMEDOUT;
648*a01cd540SToomas Soome 			return (-1);
649*a01cd540SToomas Soome 		}
6504a5d661aSToomas Soome 		/* Try to get a packet and process it. */
6517b2a1233SToomas Soome 		cc = (*rproc)(h, pkt, payload, tleft, rtype);
6524a5d661aSToomas Soome 		/* Return on data, EOF or real error. */
653*a01cd540SToomas Soome 		if (cc != -1 || (errno != 0 && errno != ETIMEDOUT))
6544a5d661aSToomas Soome 			return (cc);
6554a5d661aSToomas Soome 		if ((getsecs() - t1) < tleft) {
6564a5d661aSToomas Soome 		    goto recvnext;
6574a5d661aSToomas Soome 		}
6584a5d661aSToomas Soome 
6594a5d661aSToomas Soome 		/* Timed out or didn't get the packet we're waiting for */
6604a5d661aSToomas Soome 		tleft += MINTMO;
6614a5d661aSToomas Soome 		if (tleft > (2 * MINTMO)) {
6624a5d661aSToomas Soome 			tleft = (2 * MINTMO);
6634a5d661aSToomas Soome 		}
6644a5d661aSToomas Soome 		t1 = getsecs();
6654a5d661aSToomas Soome 	}
6664a5d661aSToomas Soome }
6674a5d661aSToomas Soome 
6684a5d661aSToomas Soome static int
6694a5d661aSToomas Soome tftp_set_blksize(struct tftp_handle *h, const char *str)
6704a5d661aSToomas Soome {
6714a5d661aSToomas Soome         char *endptr;
6724a5d661aSToomas Soome 	int new_blksize;
6734a5d661aSToomas Soome 	int ret = 0;
6744a5d661aSToomas Soome 
6754a5d661aSToomas Soome 	if (h == NULL || str == NULL)
6764a5d661aSToomas Soome 		return (ret);
6774a5d661aSToomas Soome 
6784a5d661aSToomas Soome 	new_blksize =
6794a5d661aSToomas Soome 	    (unsigned int)strtol(str, &endptr, 0);
6804a5d661aSToomas Soome 
6814a5d661aSToomas Soome 	/*
6824a5d661aSToomas Soome 	 * Only accept blksize value if it is numeric.
6834a5d661aSToomas Soome 	 * RFC2348 specifies that acceptable values are 8-65464.
6844a5d661aSToomas Soome 	 * Let's choose a limit less than MAXRSPACE.
6854a5d661aSToomas Soome 	 */
6864a5d661aSToomas Soome 	if (*endptr == '\0' && new_blksize >= 8
6874a5d661aSToomas Soome 	    && new_blksize <= TFTP_MAX_BLKSIZE) {
6884a5d661aSToomas Soome 		h->tftp_blksize = new_blksize;
6894a5d661aSToomas Soome 		ret = 1;
6904a5d661aSToomas Soome 	}
6914a5d661aSToomas Soome 
6924a5d661aSToomas Soome 	return (ret);
6934a5d661aSToomas Soome }
6944a5d661aSToomas Soome 
6954a5d661aSToomas Soome /*
6964a5d661aSToomas Soome  * In RFC2347, the TFTP Option Acknowledgement package (OACK)
6974a5d661aSToomas Soome  * is used to acknowledge a client's option negotiation request.
6984a5d661aSToomas Soome  * The format of an OACK packet is:
6994a5d661aSToomas Soome  *    +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
7004a5d661aSToomas Soome  *    |  opc  |  opt1  | 0 | value1 | 0 |  optN  | 0 | valueN | 0 |
7014a5d661aSToomas Soome  *    +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
7024a5d661aSToomas Soome  *
7034a5d661aSToomas Soome  *    opc
7044a5d661aSToomas Soome  *       The opcode field contains a 6, for Option Acknowledgment.
7054a5d661aSToomas Soome  *
7064a5d661aSToomas Soome  *    opt1
7074a5d661aSToomas Soome  *       The first option acknowledgment, copied from the original
7084a5d661aSToomas Soome  *       request.
7094a5d661aSToomas Soome  *
7104a5d661aSToomas Soome  *    value1
7114a5d661aSToomas Soome  *       The acknowledged value associated with the first option.  If
7124a5d661aSToomas Soome  *       and how this value may differ from the original request is
7134a5d661aSToomas Soome  *       detailed in the specification for the option.
7144a5d661aSToomas Soome  *
7154a5d661aSToomas Soome  *    optN, valueN
7164a5d661aSToomas Soome  *       The final option/value acknowledgment pair.
7174a5d661aSToomas Soome  */
7184a5d661aSToomas Soome static int
7194a5d661aSToomas Soome tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len)
7204a5d661aSToomas Soome {
7214a5d661aSToomas Soome 	/*
7224a5d661aSToomas Soome 	 *  We parse the OACK strings into an array
7234a5d661aSToomas Soome 	 *  of name-value pairs.
7244a5d661aSToomas Soome 	 */
7254a5d661aSToomas Soome 	char *tftp_options[128] = { 0 };
7264a5d661aSToomas Soome 	char *val = buf;
7274a5d661aSToomas Soome 	int i = 0;
7284a5d661aSToomas Soome 	int option_idx = 0;
7294a5d661aSToomas Soome 	int blksize_is_set = 0;
7304a5d661aSToomas Soome 	int tsize = 0;
7314a5d661aSToomas Soome 
7324a5d661aSToomas Soome 	unsigned int orig_blksize;
7334a5d661aSToomas Soome 
7344a5d661aSToomas Soome 	while (option_idx < 128 && i < len) {
7354a5d661aSToomas Soome 		if (buf[i] == '\0') {
7364a5d661aSToomas Soome 			if (&buf[i] > val) {
7374a5d661aSToomas Soome 				tftp_options[option_idx] = val;
7384a5d661aSToomas Soome 				val = &buf[i] + 1;
7394a5d661aSToomas Soome 				++option_idx;
7404a5d661aSToomas Soome 			}
7414a5d661aSToomas Soome 		}
7424a5d661aSToomas Soome 		++i;
7434a5d661aSToomas Soome 	}
7444a5d661aSToomas Soome 
7454a5d661aSToomas Soome 	/* Save the block size we requested for sanity check later. */
7464a5d661aSToomas Soome 	orig_blksize = h->tftp_blksize;
7474a5d661aSToomas Soome 
7484a5d661aSToomas Soome 	/*
7494a5d661aSToomas Soome 	 * Parse individual TFTP options.
7504a5d661aSToomas Soome 	 *    * "blksize" is specified in RFC2348.
7514a5d661aSToomas Soome 	 *    * "tsize" is specified in RFC2349.
7524a5d661aSToomas Soome 	 */
7534a5d661aSToomas Soome 	for (i = 0; i < option_idx; i += 2) {
7544a5d661aSToomas Soome 	    if (strcasecmp(tftp_options[i], "blksize") == 0) {
7554a5d661aSToomas Soome 		if (i + 1 < option_idx)
7564a5d661aSToomas Soome 			blksize_is_set =
7574a5d661aSToomas Soome 			    tftp_set_blksize(h, tftp_options[i + 1]);
7584a5d661aSToomas Soome 	    } else if (strcasecmp(tftp_options[i], "tsize") == 0) {
7594a5d661aSToomas Soome 		if (i + 1 < option_idx)
7604a5d661aSToomas Soome 			tsize = strtol(tftp_options[i + 1], (char **)NULL, 10);
7614a5d661aSToomas Soome 		if (tsize != 0)
7624a5d661aSToomas Soome 			h->tftp_tsize = tsize;
7634a5d661aSToomas Soome 	    } else {
7644a5d661aSToomas Soome 		/* Do not allow any options we did not expect to be ACKed. */
7654a5d661aSToomas Soome 		printf("unexpected tftp option '%s'\n", tftp_options[i]);
7664a5d661aSToomas Soome 		return (-1);
7674a5d661aSToomas Soome 	    }
7684a5d661aSToomas Soome 	}
7694a5d661aSToomas Soome 
7704a5d661aSToomas Soome 	if (!blksize_is_set) {
7714a5d661aSToomas Soome 		/*
7724a5d661aSToomas Soome 		 * If TFTP blksize was not set, try defaulting
7734a5d661aSToomas Soome 		 * to the legacy TFTP blksize of SEGSIZE(512)
7744a5d661aSToomas Soome 		 */
7754a5d661aSToomas Soome 		h->tftp_blksize = SEGSIZE;
7764a5d661aSToomas Soome 	} else if (h->tftp_blksize > orig_blksize) {
7774a5d661aSToomas Soome 		/*
7784a5d661aSToomas Soome 		 * Server should not be proposing block sizes that
7794a5d661aSToomas Soome 		 * exceed what we said we can handle.
7804a5d661aSToomas Soome 		 */
7814a5d661aSToomas Soome 		printf("unexpected blksize %u\n", h->tftp_blksize);
7824a5d661aSToomas Soome 		return (-1);
7834a5d661aSToomas Soome 	}
7844a5d661aSToomas Soome 
7854a5d661aSToomas Soome #ifdef TFTP_DEBUG
7864a5d661aSToomas Soome 	printf("tftp_blksize: %u\n", h->tftp_blksize);
7874a5d661aSToomas Soome 	printf("tftp_tsize: %lu\n", h->tftp_tsize);
7884a5d661aSToomas Soome #endif
7894a5d661aSToomas Soome 	return 0;
7904a5d661aSToomas Soome }
791