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