xref: /titanic_51/usr/src/boot/lib/libstand/tftp.c (revision 18609d046724d400daf6919e6616533da142e95f)
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 __FBSDID("$FreeBSD$");
364a5d661aSToomas Soome 
374a5d661aSToomas Soome /*
384a5d661aSToomas Soome  * Simple TFTP implementation for libsa.
394a5d661aSToomas Soome  * Assumes:
404a5d661aSToomas Soome  *  - socket descriptor (int) at open_file->f_devdata
414a5d661aSToomas Soome  *  - server host IP in global servip
424a5d661aSToomas Soome  * Restrictions:
434a5d661aSToomas Soome  *  - read only
444a5d661aSToomas Soome  *  - lseek only with SEEK_SET or SEEK_CUR
454a5d661aSToomas Soome  *  - no big time differences between transfers (<tftp timeout)
464a5d661aSToomas Soome  */
474a5d661aSToomas Soome 
484a5d661aSToomas Soome #include <sys/types.h>
494a5d661aSToomas Soome #include <sys/stat.h>
504a5d661aSToomas Soome #include <netinet/in.h>
514a5d661aSToomas Soome #include <netinet/udp.h>
524a5d661aSToomas Soome #include <netinet/in_systm.h>
534a5d661aSToomas Soome #include <arpa/tftp.h>
544a5d661aSToomas Soome 
554a5d661aSToomas Soome #include <string.h>
564a5d661aSToomas Soome 
574a5d661aSToomas Soome #include "stand.h"
584a5d661aSToomas Soome #include "net.h"
594a5d661aSToomas Soome #include "netif.h"
604a5d661aSToomas Soome 
614a5d661aSToomas Soome #include "tftp.h"
624a5d661aSToomas Soome 
634a5d661aSToomas Soome struct tftp_handle;
644a5d661aSToomas Soome 
654a5d661aSToomas Soome static int	tftp_open(const char *path, struct open_file *f);
664a5d661aSToomas Soome static int	tftp_close(struct open_file *f);
674a5d661aSToomas Soome static int	tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len);
684a5d661aSToomas Soome static int	tftp_read(struct open_file *f, void *buf, size_t size, size_t *resid);
694a5d661aSToomas Soome static int	tftp_write(struct open_file *f, void *buf, size_t size, size_t *resid);
704a5d661aSToomas Soome static off_t	tftp_seek(struct open_file *f, off_t offset, int where);
714a5d661aSToomas Soome static int	tftp_set_blksize(struct tftp_handle *h, const char *str);
724a5d661aSToomas Soome static int	tftp_stat(struct open_file *f, struct stat *sb);
734a5d661aSToomas Soome static ssize_t sendrecv_tftp(struct tftp_handle *h,
744a5d661aSToomas Soome     ssize_t (*sproc)(struct iodesc *, void *, size_t),
754a5d661aSToomas Soome     void *sbuf, size_t ssize,
764a5d661aSToomas Soome     ssize_t (*rproc)(struct tftp_handle *h, void *, ssize_t, time_t, unsigned short *),
774a5d661aSToomas Soome     void *rbuf, size_t rsize, unsigned short *rtype);
784a5d661aSToomas Soome 
794a5d661aSToomas Soome struct fs_ops tftp_fsops = {
804a5d661aSToomas Soome 	"tftp",
814a5d661aSToomas Soome 	tftp_open,
824a5d661aSToomas Soome 	tftp_close,
834a5d661aSToomas Soome 	tftp_read,
844a5d661aSToomas Soome 	tftp_write,
854a5d661aSToomas Soome 	tftp_seek,
864a5d661aSToomas Soome 	tftp_stat,
874a5d661aSToomas Soome 	null_readdir
884a5d661aSToomas Soome };
894a5d661aSToomas Soome 
904a5d661aSToomas Soome extern struct in_addr servip;
914a5d661aSToomas Soome 
924a5d661aSToomas Soome static int      tftpport = 2000;
934a5d661aSToomas Soome static int	is_open = 0;
944a5d661aSToomas Soome 
954a5d661aSToomas Soome /*
964a5d661aSToomas Soome  * The legacy TFTP_BLKSIZE value was SEGSIZE(512).
974a5d661aSToomas Soome  * TFTP_REQUESTED_BLKSIZE of 1428 is (Ethernet MTU, less the TFTP, UDP and
984a5d661aSToomas Soome  * IP header lengths).
994a5d661aSToomas Soome  */
1004a5d661aSToomas Soome #define TFTP_REQUESTED_BLKSIZE 1428
1014a5d661aSToomas Soome 
1024a5d661aSToomas Soome /*
1034a5d661aSToomas Soome  * Choose a blksize big enough so we can test with Ethernet
1044a5d661aSToomas Soome  * Jumbo frames in the future.
1054a5d661aSToomas Soome  */
1064a5d661aSToomas Soome #define TFTP_MAX_BLKSIZE 9008
1074a5d661aSToomas Soome 
1084a5d661aSToomas Soome struct tftp_handle {
1094a5d661aSToomas Soome 	struct iodesc  *iodesc;
1104a5d661aSToomas Soome 	int             currblock;	/* contents of lastdata */
1114a5d661aSToomas Soome 	int             islastblock;	/* flag */
1124a5d661aSToomas Soome 	int             validsize;
1134a5d661aSToomas Soome 	int             off;
1144a5d661aSToomas Soome 	char           *path;	/* saved for re-requests */
1154a5d661aSToomas Soome 	unsigned int	tftp_blksize;
1164a5d661aSToomas Soome 	unsigned long	tftp_tsize;
1174a5d661aSToomas Soome 	struct {
1184a5d661aSToomas Soome 		u_char header[HEADER_SIZE];
1194a5d661aSToomas Soome 		struct tftphdr t;
1204a5d661aSToomas Soome 		u_char space[TFTP_MAX_BLKSIZE];
1214a5d661aSToomas Soome 	} __packed __aligned(4) lastdata;
1224a5d661aSToomas Soome };
1234a5d661aSToomas Soome 
1244a5d661aSToomas Soome #define	TFTP_MAX_ERRCODE EOPTNEG
1254a5d661aSToomas Soome static const int tftperrors[TFTP_MAX_ERRCODE + 1] = {
1264a5d661aSToomas Soome 	0,			/* ??? */
1274a5d661aSToomas Soome 	ENOENT,
1284a5d661aSToomas Soome 	EPERM,
1294a5d661aSToomas Soome 	ENOSPC,
1304a5d661aSToomas Soome 	EINVAL,			/* ??? */
1314a5d661aSToomas Soome 	EINVAL,			/* ??? */
1324a5d661aSToomas Soome 	EEXIST,
1334a5d661aSToomas Soome 	EINVAL,			/* ??? */
1344a5d661aSToomas Soome 	EINVAL,			/* Option negotiation failed. */
1354a5d661aSToomas Soome };
1364a5d661aSToomas Soome 
1374a5d661aSToomas Soome static int  tftp_getnextblock(struct tftp_handle *h);
1384a5d661aSToomas Soome 
1394a5d661aSToomas Soome /* send error message back. */
1404a5d661aSToomas Soome static void
1414a5d661aSToomas Soome tftp_senderr(struct tftp_handle *h, u_short errcode, const char *msg)
1424a5d661aSToomas Soome {
1434a5d661aSToomas Soome 	struct {
1444a5d661aSToomas Soome 		u_char header[HEADER_SIZE];
1454a5d661aSToomas Soome 		struct tftphdr  t;
1464a5d661aSToomas Soome 		u_char space[63]; /* +1 from t */
1474a5d661aSToomas Soome 	} __packed __aligned(4) wbuf;
1484a5d661aSToomas Soome 	char           *wtail;
1494a5d661aSToomas Soome 	int             len;
1504a5d661aSToomas Soome 
1514a5d661aSToomas Soome 	len = strlen(msg);
1524a5d661aSToomas Soome 	if (len > sizeof(wbuf.space))
1534a5d661aSToomas Soome 		len = sizeof(wbuf.space);
1544a5d661aSToomas Soome 
1554a5d661aSToomas Soome 	wbuf.t.th_opcode = htons((u_short) ERROR);
1564a5d661aSToomas Soome 	wbuf.t.th_code   = htons(errcode);
1574a5d661aSToomas Soome 
1584a5d661aSToomas Soome 	wtail = wbuf.t.th_msg;
1594a5d661aSToomas Soome 	bcopy(msg, wtail, len);
1604a5d661aSToomas Soome 	wtail[len] = '\0';
1614a5d661aSToomas Soome 	wtail += len + 1;
1624a5d661aSToomas Soome 
1634a5d661aSToomas Soome 	sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
1644a5d661aSToomas Soome }
1654a5d661aSToomas Soome 
1664a5d661aSToomas Soome static void
1674a5d661aSToomas Soome tftp_sendack(struct tftp_handle *h)
1684a5d661aSToomas Soome {
1694a5d661aSToomas Soome 	struct {
1704a5d661aSToomas Soome 		u_char header[HEADER_SIZE];
1714a5d661aSToomas Soome 		struct tftphdr  t;
1724a5d661aSToomas Soome 	} __packed __aligned(4) wbuf;
1734a5d661aSToomas Soome 	char           *wtail;
1744a5d661aSToomas Soome 
1754a5d661aSToomas Soome 	wbuf.t.th_opcode = htons((u_short) ACK);
1764a5d661aSToomas Soome 	wtail = (char *) &wbuf.t.th_block;
1774a5d661aSToomas Soome 	wbuf.t.th_block = htons((u_short) h->currblock);
1784a5d661aSToomas Soome 	wtail += 2;
1794a5d661aSToomas Soome 
1804a5d661aSToomas Soome 	sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
1814a5d661aSToomas Soome }
1824a5d661aSToomas Soome 
1834a5d661aSToomas Soome static ssize_t
1844a5d661aSToomas Soome recvtftp(struct tftp_handle *h, void *pkt, ssize_t len, time_t tleft,
1854a5d661aSToomas Soome     unsigned short *rtype)
1864a5d661aSToomas Soome {
1874a5d661aSToomas Soome 	struct iodesc *d = h->iodesc;
1884a5d661aSToomas Soome 	struct tftphdr *t;
1894a5d661aSToomas Soome 
1904a5d661aSToomas Soome 	errno = 0;
1914a5d661aSToomas Soome 
1924a5d661aSToomas Soome 	len = readudp(d, pkt, len, tleft);
1934a5d661aSToomas Soome 
1944a5d661aSToomas Soome 	if (len < 4)
1954a5d661aSToomas Soome 		return (-1);
1964a5d661aSToomas Soome 
1974a5d661aSToomas Soome 	t = (struct tftphdr *) pkt;
1984a5d661aSToomas Soome 	*rtype = ntohs(t->th_opcode);
1994a5d661aSToomas Soome 	switch (ntohs(t->th_opcode)) {
2004a5d661aSToomas Soome 	case DATA: {
2014a5d661aSToomas Soome 		int got;
2024a5d661aSToomas Soome 
2034a5d661aSToomas Soome 		if (htons(t->th_block) != (u_short) d->xid) {
2044a5d661aSToomas Soome 			/*
2054a5d661aSToomas Soome 			 * Expected block?
2064a5d661aSToomas Soome 			 */
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;
2144a5d661aSToomas Soome 			uh = (struct udphdr *) pkt - 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);
2184a5d661aSToomas Soome 		return got;
2194a5d661aSToomas Soome 	}
2204a5d661aSToomas Soome 	case ERROR:
2214a5d661aSToomas Soome 		if ((unsigned) ntohs(t->th_code) > TFTP_MAX_ERRCODE) {
2224a5d661aSToomas Soome 			printf("illegal tftp error %d\n", ntohs(t->th_code));
2234a5d661aSToomas Soome 			errno = EIO;
2244a5d661aSToomas Soome 		} else {
2254a5d661aSToomas Soome #ifdef TFTP_DEBUG
2264a5d661aSToomas Soome 			printf("tftp-error %d\n", ntohs(t->th_code));
2274a5d661aSToomas Soome #endif
2284a5d661aSToomas Soome 			errno = tftperrors[ntohs(t->th_code)];
2294a5d661aSToomas Soome 		}
2304a5d661aSToomas Soome 		return (-1);
2314a5d661aSToomas Soome 	case OACK: {
2324a5d661aSToomas Soome 		struct udphdr *uh;
2334a5d661aSToomas Soome 		int tftp_oack_len;
2344a5d661aSToomas Soome 
2354a5d661aSToomas Soome 		/*
2364a5d661aSToomas Soome 		 * Unexpected OACK. TFTP transfer already in progress.
2374a5d661aSToomas Soome 		 * Drop the pkt.
2384a5d661aSToomas Soome 		 */
2394a5d661aSToomas Soome 		if (d->xid != 1) {
2404a5d661aSToomas Soome 			return (-1);
2414a5d661aSToomas Soome 		}
2424a5d661aSToomas Soome 
2434a5d661aSToomas Soome 		/*
2444a5d661aSToomas Soome 		 * Remember which port this OACK came from, because we need
2454a5d661aSToomas Soome 		 * to send the ACK or errors back to it.
2464a5d661aSToomas Soome 		 */
2474a5d661aSToomas Soome 		uh = (struct udphdr *) pkt - 1;
2484a5d661aSToomas Soome 		d->destport = uh->uh_sport;
2494a5d661aSToomas Soome 
2504a5d661aSToomas Soome 		/* Parse options ACK-ed by the server. */
2514a5d661aSToomas Soome 		tftp_oack_len = len - sizeof(t->th_opcode);
2524a5d661aSToomas Soome 		if (tftp_parse_oack(h, t->th_u.tu_stuff, tftp_oack_len) != 0) {
2534a5d661aSToomas Soome 			tftp_senderr(h, EOPTNEG, "Malformed OACK");
2544a5d661aSToomas Soome 			errno = EIO;
2554a5d661aSToomas Soome 			return (-1);
2564a5d661aSToomas Soome 		}
2574a5d661aSToomas Soome 		return (0);
2584a5d661aSToomas Soome 	}
2594a5d661aSToomas Soome 	default:
2604a5d661aSToomas Soome #ifdef TFTP_DEBUG
2614a5d661aSToomas Soome 		printf("tftp type %d not handled\n", ntohs(t->th_opcode));
2624a5d661aSToomas Soome #endif
2634a5d661aSToomas Soome 		return (-1);
2644a5d661aSToomas Soome 	}
2654a5d661aSToomas Soome }
2664a5d661aSToomas Soome 
2674a5d661aSToomas Soome /* send request, expect first block (or error) */
2684a5d661aSToomas Soome static int
2694a5d661aSToomas Soome tftp_makereq(struct tftp_handle *h)
2704a5d661aSToomas Soome {
2714a5d661aSToomas Soome 	struct {
2724a5d661aSToomas Soome 		u_char header[HEADER_SIZE];
2734a5d661aSToomas Soome 		struct tftphdr  t;
2744a5d661aSToomas Soome 		u_char space[FNAME_SIZE + 6];
2754a5d661aSToomas Soome 	} __packed __aligned(4) wbuf;
2764a5d661aSToomas Soome 	char           *wtail;
2774a5d661aSToomas Soome 	int             l;
2784a5d661aSToomas Soome 	ssize_t         res;
2794a5d661aSToomas Soome 	struct tftphdr *t;
2804a5d661aSToomas Soome 	char *tftp_blksize = NULL;
2814a5d661aSToomas Soome 	int blksize_l;
2824a5d661aSToomas Soome 	unsigned short rtype = 0;
2834a5d661aSToomas Soome 
2844a5d661aSToomas Soome 	/*
2854a5d661aSToomas Soome 	 * Allow overriding default TFTP block size by setting
2864a5d661aSToomas Soome 	 * a tftp.blksize environment variable.
2874a5d661aSToomas Soome 	 */
2884a5d661aSToomas Soome 	if ((tftp_blksize = getenv("tftp.blksize")) != NULL) {
2894a5d661aSToomas Soome 		tftp_set_blksize(h, tftp_blksize);
2904a5d661aSToomas Soome 	}
2914a5d661aSToomas Soome 
2924a5d661aSToomas Soome 	wbuf.t.th_opcode = htons((u_short) RRQ);
2934a5d661aSToomas Soome 	wtail = wbuf.t.th_stuff;
2944a5d661aSToomas Soome 	l = strlen(h->path);
2954a5d661aSToomas Soome #ifdef TFTP_PREPEND_PATH
2964a5d661aSToomas Soome 	if (l > FNAME_SIZE - (sizeof(TFTP_PREPEND_PATH) - 1))
2974a5d661aSToomas Soome 		return (ENAMETOOLONG);
2984a5d661aSToomas Soome 	bcopy(TFTP_PREPEND_PATH, wtail, sizeof(TFTP_PREPEND_PATH) - 1);
2994a5d661aSToomas Soome 	wtail += sizeof(TFTP_PREPEND_PATH) - 1;
3004a5d661aSToomas Soome #else
3014a5d661aSToomas Soome 	if (l > FNAME_SIZE)
3024a5d661aSToomas Soome 		return (ENAMETOOLONG);
3034a5d661aSToomas Soome #endif
3044a5d661aSToomas Soome 	bcopy(h->path, wtail, l + 1);
3054a5d661aSToomas Soome 	wtail += l + 1;
3064a5d661aSToomas Soome 	bcopy("octet", wtail, 6);
3074a5d661aSToomas Soome 	wtail += 6;
3084a5d661aSToomas Soome 	bcopy("blksize", wtail, 8);
3094a5d661aSToomas Soome 	wtail += 8;
3104a5d661aSToomas Soome 	blksize_l = sprintf(wtail, "%d", h->tftp_blksize);
3114a5d661aSToomas Soome 	wtail += blksize_l + 1;
3124a5d661aSToomas Soome 	bcopy("tsize", wtail, 6);
3134a5d661aSToomas Soome 	wtail += 6;
3144a5d661aSToomas Soome 	bcopy("0", wtail, 2);
3154a5d661aSToomas Soome 	wtail += 2;
3164a5d661aSToomas Soome 
3174a5d661aSToomas Soome 	t = &h->lastdata.t;
3184a5d661aSToomas Soome 
3194a5d661aSToomas Soome 	/* h->iodesc->myport = htons(--tftpport); */
3204a5d661aSToomas Soome 	h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
3214a5d661aSToomas Soome 	h->iodesc->destport = htons(IPPORT_TFTP);
3224a5d661aSToomas Soome 	h->iodesc->xid = 1;	/* expected block */
3234a5d661aSToomas Soome 
3244a5d661aSToomas Soome 	h->currblock = 0;
3254a5d661aSToomas Soome 	h->islastblock = 0;
3264a5d661aSToomas Soome 	h->validsize = 0;
3274a5d661aSToomas Soome 
3284a5d661aSToomas Soome 	res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
3294a5d661aSToomas Soome 		       &recvtftp, t, sizeof(*t) + h->tftp_blksize, &rtype);
3304a5d661aSToomas Soome 
3314a5d661aSToomas Soome 	if (rtype == OACK)
3324a5d661aSToomas Soome 		return (tftp_getnextblock(h));
3334a5d661aSToomas Soome 
3344a5d661aSToomas Soome 	/* Server ignored our blksize request, revert to TFTP default. */
3354a5d661aSToomas Soome 	h->tftp_blksize = SEGSIZE;
3364a5d661aSToomas Soome 
3374a5d661aSToomas Soome 	switch (rtype) {
3384a5d661aSToomas Soome 		case DATA: {
3394a5d661aSToomas Soome 			h->currblock = 1;
3404a5d661aSToomas Soome 			h->validsize = res;
3414a5d661aSToomas Soome 			h->islastblock = 0;
3424a5d661aSToomas Soome 			if (res < h->tftp_blksize) {
3434a5d661aSToomas Soome 				h->islastblock = 1;	/* very short file */
3444a5d661aSToomas Soome 				tftp_sendack(h);
3454a5d661aSToomas Soome 			}
3464a5d661aSToomas Soome 			return (0);
3474a5d661aSToomas Soome 		}
3484a5d661aSToomas Soome 		case ERROR:
3494a5d661aSToomas Soome 		default:
3504a5d661aSToomas Soome 			return (errno);
3514a5d661aSToomas Soome 	}
3524a5d661aSToomas Soome 
3534a5d661aSToomas Soome }
3544a5d661aSToomas Soome 
3554a5d661aSToomas Soome /* ack block, expect next */
3564a5d661aSToomas Soome static int
3574a5d661aSToomas Soome tftp_getnextblock(struct tftp_handle *h)
3584a5d661aSToomas Soome {
3594a5d661aSToomas Soome 	struct {
3604a5d661aSToomas Soome 		u_char header[HEADER_SIZE];
3614a5d661aSToomas Soome 		struct tftphdr t;
3624a5d661aSToomas Soome 	} __packed __aligned(4) wbuf;
3634a5d661aSToomas Soome 	char           *wtail;
3644a5d661aSToomas Soome 	int             res;
3654a5d661aSToomas Soome 	struct tftphdr *t;
3664a5d661aSToomas Soome 	unsigned short rtype = 0;
3674a5d661aSToomas Soome 	wbuf.t.th_opcode = htons((u_short) ACK);
3684a5d661aSToomas Soome 	wtail = (char *) &wbuf.t.th_block;
3694a5d661aSToomas Soome 	wbuf.t.th_block = htons((u_short) h->currblock);
3704a5d661aSToomas Soome 	wtail += 2;
3714a5d661aSToomas Soome 
3724a5d661aSToomas Soome 	t = &h->lastdata.t;
3734a5d661aSToomas Soome 
3744a5d661aSToomas Soome 	h->iodesc->xid = h->currblock + 1;	/* expected block */
3754a5d661aSToomas Soome 
3764a5d661aSToomas Soome 	res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
3774a5d661aSToomas Soome 		       &recvtftp, t, sizeof(*t) + h->tftp_blksize, &rtype);
3784a5d661aSToomas Soome 
3794a5d661aSToomas Soome 	if (res == -1)		/* 0 is OK! */
3804a5d661aSToomas Soome 		return (errno);
3814a5d661aSToomas Soome 
3824a5d661aSToomas Soome 	h->currblock++;
3834a5d661aSToomas Soome 	h->validsize = res;
3844a5d661aSToomas Soome 	if (res < h->tftp_blksize)
3854a5d661aSToomas Soome 		h->islastblock = 1;	/* EOF */
3864a5d661aSToomas Soome 
3874a5d661aSToomas Soome 	if (h->islastblock == 1) {
3884a5d661aSToomas Soome 		/* Send an ACK for the last block */
3894a5d661aSToomas Soome 		wbuf.t.th_block = htons((u_short) h->currblock);
3904a5d661aSToomas Soome 		sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t);
3914a5d661aSToomas Soome 	}
3924a5d661aSToomas Soome 
3934a5d661aSToomas Soome 	return (0);
3944a5d661aSToomas Soome }
3954a5d661aSToomas Soome 
3964a5d661aSToomas Soome static int
3974a5d661aSToomas Soome tftp_open(const char *path, struct open_file *f)
3984a5d661aSToomas Soome {
3994a5d661aSToomas Soome 	struct tftp_handle *tftpfile;
4004a5d661aSToomas Soome 	struct iodesc  *io;
4014a5d661aSToomas Soome 	int             res;
4024a5d661aSToomas Soome 	size_t          pathsize;
4034a5d661aSToomas Soome 	const char     *extraslash;
4044a5d661aSToomas Soome 
405*18609d04SToomas Soome 	if (netproto != NET_TFTP)
406*18609d04SToomas Soome 		return (EINVAL);
407*18609d04SToomas Soome 
4084a5d661aSToomas Soome 	if (strcmp(f->f_dev->dv_name, "net") != 0) {
4094a5d661aSToomas Soome #ifdef __i386__
4104a5d661aSToomas Soome 		if (strcmp(f->f_dev->dv_name, "pxe") != 0)
4114a5d661aSToomas Soome 			return (EINVAL);
4124a5d661aSToomas Soome #else
4134a5d661aSToomas Soome 		return (EINVAL);
4144a5d661aSToomas Soome #endif
4154a5d661aSToomas Soome 	}
4164a5d661aSToomas Soome 
4174a5d661aSToomas Soome 	if (is_open)
4184a5d661aSToomas Soome 		return (EBUSY);
4194a5d661aSToomas Soome 
4204a5d661aSToomas Soome 	tftpfile = (struct tftp_handle *) malloc(sizeof(*tftpfile));
4214a5d661aSToomas Soome 	if (!tftpfile)
4224a5d661aSToomas Soome 		return (ENOMEM);
4234a5d661aSToomas Soome 
4244a5d661aSToomas Soome 	memset(tftpfile, 0, sizeof(*tftpfile));
4254a5d661aSToomas Soome 	tftpfile->tftp_blksize = TFTP_REQUESTED_BLKSIZE;
4264a5d661aSToomas Soome 	tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata));
4274a5d661aSToomas Soome 	if (io == NULL)
4284a5d661aSToomas Soome 		return (EINVAL);
4294a5d661aSToomas Soome 
4304a5d661aSToomas Soome 	io->destip = servip;
4314a5d661aSToomas Soome 	tftpfile->off = 0;
4324a5d661aSToomas Soome 	pathsize = (strlen(rootpath) + 1 + strlen(path) + 1) * sizeof(char);
4334a5d661aSToomas Soome 	tftpfile->path = malloc(pathsize);
4344a5d661aSToomas Soome 	if (tftpfile->path == NULL) {
4354a5d661aSToomas Soome 		free(tftpfile);
4364a5d661aSToomas Soome 		return(ENOMEM);
4374a5d661aSToomas Soome 	}
4384a5d661aSToomas Soome 	if (rootpath[strlen(rootpath) - 1] == '/' || path[0] == '/')
4394a5d661aSToomas Soome 		extraslash = "";
4404a5d661aSToomas Soome 	else
4414a5d661aSToomas Soome 		extraslash = "/";
4424a5d661aSToomas Soome 	res = snprintf(tftpfile->path, pathsize, "%s%s%s",
4434a5d661aSToomas Soome 	    rootpath, extraslash, path);
4444a5d661aSToomas Soome 	if (res < 0 || res > pathsize) {
4454a5d661aSToomas Soome 		free(tftpfile->path);
4464a5d661aSToomas Soome 		free(tftpfile);
4474a5d661aSToomas Soome 		return(ENOMEM);
4484a5d661aSToomas Soome 	}
4494a5d661aSToomas Soome 
4504a5d661aSToomas Soome 	res = tftp_makereq(tftpfile);
4514a5d661aSToomas Soome 
4524a5d661aSToomas Soome 	if (res) {
4534a5d661aSToomas Soome 		free(tftpfile->path);
4544a5d661aSToomas Soome 		free(tftpfile);
4554a5d661aSToomas Soome 		return (res);
4564a5d661aSToomas Soome 	}
4574a5d661aSToomas Soome 	f->f_fsdata = (void *) tftpfile;
4584a5d661aSToomas Soome 	is_open = 1;
4594a5d661aSToomas Soome 	return (0);
4604a5d661aSToomas Soome }
4614a5d661aSToomas Soome 
4624a5d661aSToomas Soome static int
4634a5d661aSToomas Soome tftp_read(struct open_file *f, void *addr, size_t size,
4644a5d661aSToomas Soome     size_t *resid /* out */)
4654a5d661aSToomas Soome {
4664a5d661aSToomas Soome 	struct tftp_handle *tftpfile;
4674a5d661aSToomas Soome 	tftpfile = (struct tftp_handle *) f->f_fsdata;
4684a5d661aSToomas Soome 
4694a5d661aSToomas Soome 	while (size > 0) {
4704a5d661aSToomas Soome 		int needblock, count;
4714a5d661aSToomas Soome 
4724a5d661aSToomas Soome 		twiddle(32);
4734a5d661aSToomas Soome 
4744a5d661aSToomas Soome 		needblock = tftpfile->off / tftpfile->tftp_blksize + 1;
4754a5d661aSToomas Soome 
4764a5d661aSToomas Soome 		if (tftpfile->currblock > needblock) {	/* seek backwards */
4774a5d661aSToomas Soome 			tftp_senderr(tftpfile, 0, "No error: read aborted");
4784a5d661aSToomas Soome 			tftp_makereq(tftpfile);	/* no error check, it worked
4794a5d661aSToomas Soome 						 * for open */
4804a5d661aSToomas Soome 		}
4814a5d661aSToomas Soome 
4824a5d661aSToomas Soome 		while (tftpfile->currblock < needblock) {
4834a5d661aSToomas Soome 			int res;
4844a5d661aSToomas Soome 
4854a5d661aSToomas Soome 			res = tftp_getnextblock(tftpfile);
4864a5d661aSToomas Soome 			if (res) {	/* no answer */
4874a5d661aSToomas Soome #ifdef TFTP_DEBUG
4884a5d661aSToomas Soome 				printf("tftp: read error\n");
4894a5d661aSToomas Soome #endif
4904a5d661aSToomas Soome 				return (res);
4914a5d661aSToomas Soome 			}
4924a5d661aSToomas Soome 			if (tftpfile->islastblock)
4934a5d661aSToomas Soome 				break;
4944a5d661aSToomas Soome 		}
4954a5d661aSToomas Soome 
4964a5d661aSToomas Soome 		if (tftpfile->currblock == needblock) {
4974a5d661aSToomas Soome 			int offinblock, inbuffer;
4984a5d661aSToomas Soome 
4994a5d661aSToomas Soome 			offinblock = tftpfile->off % tftpfile->tftp_blksize;
5004a5d661aSToomas Soome 
5014a5d661aSToomas Soome 			inbuffer = tftpfile->validsize - offinblock;
5024a5d661aSToomas Soome 			if (inbuffer < 0) {
5034a5d661aSToomas Soome #ifdef TFTP_DEBUG
5044a5d661aSToomas Soome 				printf("tftp: invalid offset %d\n",
5054a5d661aSToomas Soome 				    tftpfile->off);
5064a5d661aSToomas Soome #endif
5074a5d661aSToomas Soome 				return (EINVAL);
5084a5d661aSToomas Soome 			}
5094a5d661aSToomas Soome 			count = (size < inbuffer ? size : inbuffer);
5104a5d661aSToomas Soome 			bcopy(tftpfile->lastdata.t.th_data + offinblock,
5114a5d661aSToomas Soome 			    addr, count);
5124a5d661aSToomas Soome 
5134a5d661aSToomas Soome 			addr = (char *)addr + count;
5144a5d661aSToomas Soome 			tftpfile->off += count;
5154a5d661aSToomas Soome 			size -= count;
5164a5d661aSToomas Soome 
5174a5d661aSToomas Soome 			if ((tftpfile->islastblock) && (count == inbuffer))
5184a5d661aSToomas Soome 				break;	/* EOF */
5194a5d661aSToomas Soome 		} else {
5204a5d661aSToomas Soome #ifdef TFTP_DEBUG
5214a5d661aSToomas Soome 			printf("tftp: block %d not found\n", needblock);
5224a5d661aSToomas Soome #endif
5234a5d661aSToomas Soome 			return (EINVAL);
5244a5d661aSToomas Soome 		}
5254a5d661aSToomas Soome 
5264a5d661aSToomas Soome 	}
5274a5d661aSToomas Soome 
5284a5d661aSToomas Soome 	if (resid)
5294a5d661aSToomas Soome 		*resid = size;
5304a5d661aSToomas Soome 	return (0);
5314a5d661aSToomas Soome }
5324a5d661aSToomas Soome 
5334a5d661aSToomas Soome static int
5344a5d661aSToomas Soome tftp_close(struct open_file *f)
5354a5d661aSToomas Soome {
5364a5d661aSToomas Soome 	struct tftp_handle *tftpfile;
5374a5d661aSToomas Soome 	tftpfile = (struct tftp_handle *) f->f_fsdata;
5384a5d661aSToomas Soome 
5394a5d661aSToomas Soome 	/* let it time out ... */
5404a5d661aSToomas Soome 
5414a5d661aSToomas Soome 	if (tftpfile) {
5424a5d661aSToomas Soome 		free(tftpfile->path);
5434a5d661aSToomas Soome 		free(tftpfile);
5444a5d661aSToomas Soome 	}
5454a5d661aSToomas Soome 	is_open = 0;
5464a5d661aSToomas Soome 	return (0);
5474a5d661aSToomas Soome }
5484a5d661aSToomas Soome 
5494a5d661aSToomas Soome static int
5504a5d661aSToomas Soome tftp_write(struct open_file *f __unused, void *start __unused, size_t size __unused,
5514a5d661aSToomas Soome     size_t *resid __unused /* out */)
5524a5d661aSToomas Soome {
5534a5d661aSToomas Soome 	return (EROFS);
5544a5d661aSToomas Soome }
5554a5d661aSToomas Soome 
5564a5d661aSToomas Soome static int
5574a5d661aSToomas Soome tftp_stat(struct open_file *f, struct stat *sb)
5584a5d661aSToomas Soome {
5594a5d661aSToomas Soome 	struct tftp_handle *tftpfile;
5604a5d661aSToomas Soome 	tftpfile = (struct tftp_handle *) f->f_fsdata;
5614a5d661aSToomas Soome 
5624a5d661aSToomas Soome 	sb->st_mode = 0444 | S_IFREG;
5634a5d661aSToomas Soome 	sb->st_nlink = 1;
5644a5d661aSToomas Soome 	sb->st_uid = 0;
5654a5d661aSToomas Soome 	sb->st_gid = 0;
5664a5d661aSToomas Soome 	sb->st_size = (off_t) tftpfile->tftp_tsize;
5674a5d661aSToomas Soome 	return (0);
5684a5d661aSToomas Soome }
5694a5d661aSToomas Soome 
5704a5d661aSToomas Soome static off_t
5714a5d661aSToomas Soome tftp_seek(struct open_file *f, off_t offset, int where)
5724a5d661aSToomas Soome {
5734a5d661aSToomas Soome 	struct tftp_handle *tftpfile;
5744a5d661aSToomas Soome 	tftpfile = (struct tftp_handle *) f->f_fsdata;
5754a5d661aSToomas Soome 
5764a5d661aSToomas Soome 	switch (where) {
5774a5d661aSToomas Soome 	case SEEK_SET:
5784a5d661aSToomas Soome 		tftpfile->off = offset;
5794a5d661aSToomas Soome 		break;
5804a5d661aSToomas Soome 	case SEEK_CUR:
5814a5d661aSToomas Soome 		tftpfile->off += offset;
5824a5d661aSToomas Soome 		break;
5834a5d661aSToomas Soome 	default:
5844a5d661aSToomas Soome 		errno = EOFFSET;
5854a5d661aSToomas Soome 		return (-1);
5864a5d661aSToomas Soome 	}
5874a5d661aSToomas Soome 	return (tftpfile->off);
5884a5d661aSToomas Soome }
5894a5d661aSToomas Soome 
5904a5d661aSToomas Soome static ssize_t
5914a5d661aSToomas Soome sendrecv_tftp(struct tftp_handle *h,
5924a5d661aSToomas Soome     ssize_t (*sproc)(struct iodesc *, void *, size_t),
5934a5d661aSToomas Soome     void *sbuf, size_t ssize,
5944a5d661aSToomas Soome     ssize_t (*rproc)(struct tftp_handle *, void *, ssize_t, time_t, unsigned short *),
5954a5d661aSToomas Soome     void *rbuf, size_t rsize, unsigned short *rtype)
5964a5d661aSToomas Soome {
5974a5d661aSToomas Soome 	struct iodesc *d = h->iodesc;
5984a5d661aSToomas Soome 	ssize_t cc;
5994a5d661aSToomas Soome 	time_t t, t1, tleft;
6004a5d661aSToomas Soome 
6014a5d661aSToomas Soome #ifdef TFTP_DEBUG
6024a5d661aSToomas Soome 	if (debug)
6034a5d661aSToomas Soome 		printf("sendrecv: called\n");
6044a5d661aSToomas Soome #endif
6054a5d661aSToomas Soome 
6064a5d661aSToomas Soome 	tleft = MINTMO;
6074a5d661aSToomas Soome 	t = t1 = getsecs();
6084a5d661aSToomas Soome 	for (;;) {
6094a5d661aSToomas Soome 		if ((getsecs() - t) > MAXTMO) {
6104a5d661aSToomas Soome 			errno = ETIMEDOUT;
6114a5d661aSToomas Soome 			return -1;
6124a5d661aSToomas Soome 		}
6134a5d661aSToomas Soome 
6144a5d661aSToomas Soome 		cc = (*sproc)(d, sbuf, ssize);
6154a5d661aSToomas Soome 		if (cc != -1 && cc < ssize)
6164a5d661aSToomas Soome 			panic("sendrecv: short write! (%zd < %zu)",
6174a5d661aSToomas Soome 			    cc, ssize);
6184a5d661aSToomas Soome 
6194a5d661aSToomas Soome 		if (cc == -1) {
6204a5d661aSToomas Soome 			/* Error on transmit; wait before retrying */
6214a5d661aSToomas Soome 			while ((getsecs() - t1) < tleft);
6224a5d661aSToomas Soome 			continue;
6234a5d661aSToomas Soome 		}
6244a5d661aSToomas Soome 
6254a5d661aSToomas Soome recvnext:
6264a5d661aSToomas Soome 		/* Try to get a packet and process it. */
6274a5d661aSToomas Soome 		cc = (*rproc)(h, rbuf, rsize, tleft, rtype);
6284a5d661aSToomas Soome 		/* Return on data, EOF or real error. */
6294a5d661aSToomas Soome 		if (cc != -1 || errno != 0)
6304a5d661aSToomas Soome 			return (cc);
6314a5d661aSToomas Soome 		if ((getsecs() - t1) < tleft) {
6324a5d661aSToomas Soome 		    goto recvnext;
6334a5d661aSToomas Soome 		}
6344a5d661aSToomas Soome 
6354a5d661aSToomas Soome 		/* Timed out or didn't get the packet we're waiting for */
6364a5d661aSToomas Soome 		tleft += MINTMO;
6374a5d661aSToomas Soome 		if (tleft > (2 * MINTMO)) {
6384a5d661aSToomas Soome 			tleft = (2 * MINTMO);
6394a5d661aSToomas Soome 		}
6404a5d661aSToomas Soome 		t1 = getsecs();
6414a5d661aSToomas Soome 	}
6424a5d661aSToomas Soome }
6434a5d661aSToomas Soome 
6444a5d661aSToomas Soome static int
6454a5d661aSToomas Soome tftp_set_blksize(struct tftp_handle *h, const char *str)
6464a5d661aSToomas Soome {
6474a5d661aSToomas Soome         char *endptr;
6484a5d661aSToomas Soome 	int new_blksize;
6494a5d661aSToomas Soome 	int ret = 0;
6504a5d661aSToomas Soome 
6514a5d661aSToomas Soome 	if (h == NULL || str == NULL)
6524a5d661aSToomas Soome 		return (ret);
6534a5d661aSToomas Soome 
6544a5d661aSToomas Soome 	new_blksize =
6554a5d661aSToomas Soome 	    (unsigned int)strtol(str, &endptr, 0);
6564a5d661aSToomas Soome 
6574a5d661aSToomas Soome 	/*
6584a5d661aSToomas Soome 	 * Only accept blksize value if it is numeric.
6594a5d661aSToomas Soome 	 * RFC2348 specifies that acceptable values are 8-65464.
6604a5d661aSToomas Soome 	 * Let's choose a limit less than MAXRSPACE.
6614a5d661aSToomas Soome 	 */
6624a5d661aSToomas Soome 	if (*endptr == '\0' && new_blksize >= 8
6634a5d661aSToomas Soome 	    && new_blksize <= TFTP_MAX_BLKSIZE) {
6644a5d661aSToomas Soome 		h->tftp_blksize = new_blksize;
6654a5d661aSToomas Soome 		ret = 1;
6664a5d661aSToomas Soome 	}
6674a5d661aSToomas Soome 
6684a5d661aSToomas Soome 	return (ret);
6694a5d661aSToomas Soome }
6704a5d661aSToomas Soome 
6714a5d661aSToomas Soome /*
6724a5d661aSToomas Soome  * In RFC2347, the TFTP Option Acknowledgement package (OACK)
6734a5d661aSToomas Soome  * is used to acknowledge a client's option negotiation request.
6744a5d661aSToomas Soome  * The format of an OACK packet is:
6754a5d661aSToomas Soome  *    +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
6764a5d661aSToomas Soome  *    |  opc  |  opt1  | 0 | value1 | 0 |  optN  | 0 | valueN | 0 |
6774a5d661aSToomas Soome  *    +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
6784a5d661aSToomas Soome  *
6794a5d661aSToomas Soome  *    opc
6804a5d661aSToomas Soome  *       The opcode field contains a 6, for Option Acknowledgment.
6814a5d661aSToomas Soome  *
6824a5d661aSToomas Soome  *    opt1
6834a5d661aSToomas Soome  *       The first option acknowledgment, copied from the original
6844a5d661aSToomas Soome  *       request.
6854a5d661aSToomas Soome  *
6864a5d661aSToomas Soome  *    value1
6874a5d661aSToomas Soome  *       The acknowledged value associated with the first option.  If
6884a5d661aSToomas Soome  *       and how this value may differ from the original request is
6894a5d661aSToomas Soome  *       detailed in the specification for the option.
6904a5d661aSToomas Soome  *
6914a5d661aSToomas Soome  *    optN, valueN
6924a5d661aSToomas Soome  *       The final option/value acknowledgment pair.
6934a5d661aSToomas Soome  */
6944a5d661aSToomas Soome static int
6954a5d661aSToomas Soome tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len)
6964a5d661aSToomas Soome {
6974a5d661aSToomas Soome 	/*
6984a5d661aSToomas Soome 	 *  We parse the OACK strings into an array
6994a5d661aSToomas Soome 	 *  of name-value pairs.
7004a5d661aSToomas Soome 	 */
7014a5d661aSToomas Soome 	char *tftp_options[128] = { 0 };
7024a5d661aSToomas Soome 	char *val = buf;
7034a5d661aSToomas Soome 	int i = 0;
7044a5d661aSToomas Soome 	int option_idx = 0;
7054a5d661aSToomas Soome 	int blksize_is_set = 0;
7064a5d661aSToomas Soome 	int tsize = 0;
7074a5d661aSToomas Soome 
7084a5d661aSToomas Soome 	unsigned int orig_blksize;
7094a5d661aSToomas Soome 
7104a5d661aSToomas Soome 	while (option_idx < 128 && i < len) {
7114a5d661aSToomas Soome 		if (buf[i] == '\0') {
7124a5d661aSToomas Soome 			if (&buf[i] > val) {
7134a5d661aSToomas Soome 				tftp_options[option_idx] = val;
7144a5d661aSToomas Soome 				val = &buf[i] + 1;
7154a5d661aSToomas Soome 				++option_idx;
7164a5d661aSToomas Soome 			}
7174a5d661aSToomas Soome 		}
7184a5d661aSToomas Soome 		++i;
7194a5d661aSToomas Soome 	}
7204a5d661aSToomas Soome 
7214a5d661aSToomas Soome 	/* Save the block size we requested for sanity check later. */
7224a5d661aSToomas Soome 	orig_blksize = h->tftp_blksize;
7234a5d661aSToomas Soome 
7244a5d661aSToomas Soome 	/*
7254a5d661aSToomas Soome 	 * Parse individual TFTP options.
7264a5d661aSToomas Soome 	 *    * "blksize" is specified in RFC2348.
7274a5d661aSToomas Soome 	 *    * "tsize" is specified in RFC2349.
7284a5d661aSToomas Soome 	 */
7294a5d661aSToomas Soome 	for (i = 0; i < option_idx; i += 2) {
7304a5d661aSToomas Soome 	    if (strcasecmp(tftp_options[i], "blksize") == 0) {
7314a5d661aSToomas Soome 		if (i + 1 < option_idx)
7324a5d661aSToomas Soome 			blksize_is_set =
7334a5d661aSToomas Soome 			    tftp_set_blksize(h, tftp_options[i + 1]);
7344a5d661aSToomas Soome 	    } else if (strcasecmp(tftp_options[i], "tsize") == 0) {
7354a5d661aSToomas Soome 		if (i + 1 < option_idx)
7364a5d661aSToomas Soome 			tsize = strtol(tftp_options[i + 1], (char **)NULL, 10);
7374a5d661aSToomas Soome 		if (tsize != 0)
7384a5d661aSToomas Soome 			h->tftp_tsize = tsize;
7394a5d661aSToomas Soome 	    } else {
7404a5d661aSToomas Soome 		/* Do not allow any options we did not expect to be ACKed. */
7414a5d661aSToomas Soome 		printf("unexpected tftp option '%s'\n", tftp_options[i]);
7424a5d661aSToomas Soome 		return (-1);
7434a5d661aSToomas Soome 	    }
7444a5d661aSToomas Soome 	}
7454a5d661aSToomas Soome 
7464a5d661aSToomas Soome 	if (!blksize_is_set) {
7474a5d661aSToomas Soome 		/*
7484a5d661aSToomas Soome 		 * If TFTP blksize was not set, try defaulting
7494a5d661aSToomas Soome 		 * to the legacy TFTP blksize of SEGSIZE(512)
7504a5d661aSToomas Soome 		 */
7514a5d661aSToomas Soome 		h->tftp_blksize = SEGSIZE;
7524a5d661aSToomas Soome 	} else if (h->tftp_blksize > orig_blksize) {
7534a5d661aSToomas Soome 		/*
7544a5d661aSToomas Soome 		 * Server should not be proposing block sizes that
7554a5d661aSToomas Soome 		 * exceed what we said we can handle.
7564a5d661aSToomas Soome 		 */
7574a5d661aSToomas Soome 		printf("unexpected blksize %u\n", h->tftp_blksize);
7584a5d661aSToomas Soome 		return (-1);
7594a5d661aSToomas Soome 	}
7604a5d661aSToomas Soome 
7614a5d661aSToomas Soome #ifdef TFTP_DEBUG
7624a5d661aSToomas Soome 	printf("tftp_blksize: %u\n", h->tftp_blksize);
7634a5d661aSToomas Soome 	printf("tftp_tsize: %lu\n", h->tftp_tsize);
7644a5d661aSToomas Soome #endif
7654a5d661aSToomas Soome 	return 0;
7664a5d661aSToomas Soome }
767