Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 21 Sep 2001 19:12:47 +0200 (CEST)
From:      Espen Skoglund <esk@ira.uka.de>
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   bin/30710: Patch: tftpd extension --- RFC2349
Message-ID:  <200109211712.f8LHClv65384@i30nb2.ira.uka.de>

next in thread | raw e-mail | index | archive | help

>Number:         30710
>Category:       bin
>Synopsis:       Patch: tftpd extension --- RFC2349
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Fri Sep 21 10:20:00 PDT 2001
>Closed-Date:
>Last-Modified:
>Originator:     Espen Skoglund
>Release:        FreeBSD 4.4-PRERELEASE i386
>Organization:
Karlsruhe University
>Environment:


>Description:

RFC2349 <URL:http://www.hypermail.org/rfcs/rfc2349.html>; adds support
for negotiation of timeout and file size to the tftp protocol.  This
is *required* by some firmware like EFI boot managers (at least on
HP i2000 Itanium servers) in order to boot an image using tftp.  The
attached patch implements the RFC, and in doing so also implements
RFC2347; a generic tftp option extension.

The patch is only tested with -STABLE, but given that tftpd.c is not
changed in -CURRENT, I see no reason why it shouldn't work there.

>How-To-Repeat:


>Fix:

--- libexec/tftpd/tftpd.c.orig	Fri Sep 21 18:32:14 2001
+++ libexec/tftpd/tftpd.c	Fri Sep 21 19:07:03 2001
@@ -79,10 +79,11 @@
 #include "tftpsubs.h"
 
 #define	TIMEOUT		5
+#define	MAX_TIMEOUTS	5
 
 int	peer;
 int	rexmtval = TIMEOUT;
-int	maxtimeout = 5*TIMEOUT;
+int	max_rexmtval = 2*TIMEOUT;
 
 #define	PKTSIZE	SEGSIZE+4
 char	buf[PKTSIZE];
@@ -110,6 +111,7 @@
 
 static char *errtomsg __P((int));
 static void  nak __P((int));
+static void  oack __P(());
 
 int
 main(argc, argv)
@@ -311,6 +313,21 @@
 	{ 0 }
 };
 
+struct options {
+	char	*o_type;
+	char	*o_request;
+	int	o_reply;	/* turn into union if need be */
+} options[] = {
+	{ "tsize" },		/* OPT_TSIZE */
+	{ "timeout" },		/* OPT_TIMEOUT */
+	{ NULL }
+};
+
+enum opt_enum {
+	OPT_TSIZE = 0,
+	OPT_TIMEOUT,
+};
+
 /*
  * Handle initial connection protocol.
  */
@@ -320,9 +337,9 @@
 	int size;
 {
 	register char *cp;
-	int first = 1, ecode;
+	int i, first = 1, has_options = 0, ecode;
 	register struct formats *pf;
-	char *filename, *mode;
+	char *filename, *mode, *option, *ccp;
 
 	filename = cp = tp->th_stuff;
 again:
@@ -350,7 +367,40 @@
 		nak(EBADOP);
 		exit(1);
 	}
+	while (++cp < buf + size) {
+		for (i = 2, ccp = cp; i > 0; ccp++) {
+			if (ccp >= buf + size) {
+				nak(EBADOP);
+				exit(1);
+			} else if (*ccp == '\0')
+				i--;
+		}
+		for (option = cp; *cp; cp++)
+			if (isupper(*cp))
+				*cp = tolower(*cp);
+		for (i = 0; options[i].o_type != NULL; i++)
+			if (strcmp(option, options[i].o_type) == 0) {
+				options[i].o_request = ++cp;
+				has_options = 1;
+			}
+		cp = ccp-1;
+	}
+
+	if (options[OPT_TIMEOUT].o_request) {
+		int to = atoi(options[OPT_TIMEOUT].o_request);
+		if (to < 1 || to > 255) {
+			nak(EBADOP);
+			exit(1);
+		}
+		else if (to <= max_rexmtval)
+			options[OPT_TIMEOUT].o_reply = rexmtval = to;
+		else
+			options[OPT_TIMEOUT].o_request = NULL;
+	}
+
 	ecode = (*pf->f_validate)(&filename, tp->th_opcode);
+	if (has_options)
+		oack();
 	if (logging) {
 		char host[MAXHOSTNAMELEN];
 
@@ -468,6 +518,14 @@
 			return (err);
 		*filep = filename = pathname;
 	}
+	if (options[OPT_TSIZE].o_request) {
+		if (mode == RRQ) 
+			options[OPT_TSIZE].o_reply = stbuf.st_size;
+		else
+			/* XXX Allows writes of all sizes. */
+			options[OPT_TSIZE].o_reply =
+				atoi(options[OPT_TSIZE].o_request);
+	}
 	fd = open(filename, mode == RRQ ? O_RDONLY : O_WRONLY|O_TRUNC);
 	if (fd < 0)
 		return (errno + 100);
@@ -478,15 +536,13 @@
 	return (0);
 }
 
-int	timeout;
+int	timeouts;
 jmp_buf	timeoutbuf;
 
 void
 timer()
 {
-
-	timeout += rexmtval;
-	if (timeout >= maxtimeout)
+	if (++timeouts > MAX_TIMEOUTS)
 		exit(1);
 	longjmp(timeoutbuf, 1);
 }
@@ -515,7 +571,7 @@
 		}
 		dp->th_opcode = htons((u_short)DATA);
 		dp->th_block = htons((u_short)block);
-		timeout = 0;
+		timeouts = 0;
 		(void)setjmp(timeoutbuf);
 
 send_data:
@@ -578,7 +634,7 @@
 	ap = (struct tftphdr *)ackbuf;
 	block = 0;
 	do {
-		timeout = 0;
+		timeouts = 0;
 		ap->th_opcode = htons((u_short)ACK);
 		ap->th_block = htons((u_short)block);
 		block++;
@@ -651,6 +707,7 @@
 	{ EBADID,	"Unknown transfer ID" },
 	{ EEXISTS,	"File already exists" },
 	{ ENOUSER,	"No such user" },
+	{ EOPTNEG,	"Option negotiation" },
 	{ -1,		0 }
 };
 
@@ -699,4 +756,58 @@
 	length += 5;
 	if (send(peer, buf, length, 0) != length)
 		syslog(LOG_ERR, "nak: %m");
+}
+
+/*
+ * Send an oack packet (option acknowledgement).
+ */
+static void
+oack()
+{
+	struct tftphdr *tp, *ap;
+	int size, i, n;
+	char *bp;
+
+	tp = (struct tftphdr *)buf;
+	bp = buf + 2;
+	size = sizeof(buf) - 2;
+	tp->th_opcode = htons((u_short)OACK);
+	for (i = 0; options[i].o_type != NULL; i++) {
+		if (options[i].o_request) {
+			n = snprintf(bp, size, "%s%c%d", options[i].o_type,
+				     0, options[i].o_reply);
+			bp += n+1;
+			size -= n+1;
+			if (size < 0) {
+				syslog(LOG_ERR, "oack: buffer overflow");
+				exit(1);
+			}
+		}
+	}
+	size = bp - buf;
+	ap = (struct tftphdr *)ackbuf;
+	signal(SIGALRM, timer);
+	timeouts = 0;
+
+	(void)setjmp(timeoutbuf);
+	if (send(peer, buf, size, 0) != size) {
+		syslog(LOG_INFO, "oack: %m");
+		exit(1);
+	}
+
+	for (;;) {
+		alarm(rexmtval);
+		n = recv(peer, ackbuf, sizeof (ackbuf), 0);
+		alarm(0);
+		if (n < 0) {
+			syslog(LOG_ERR, "recv: %m");
+			exit(1);
+		}
+		ap->th_opcode = ntohs((u_short)ap->th_opcode);
+		ap->th_block = ntohs((u_short)ap->th_block);
+		if (ap->th_opcode == ERROR)
+			exit(1);
+		if (ap->th_opcode == ACK && ap->th_block == 0)
+			break;
+	}
 }
--- include/arpa/tftp.h.orig	Fri Sep 21 18:42:39 2001
+++ include/arpa/tftp.h	Fri Sep 21 18:43:47 2001
@@ -49,6 +49,7 @@
 #define	DATA	03			/* data packet */
 #define	ACK	04			/* acknowledgement */
 #define	ERROR	05			/* error code */
+#define	OACK	06			/* option acknowledgement */
 
 struct	tftphdr {
 	unsigned short	th_opcode;		/* packet type */
@@ -76,5 +77,6 @@
 #define	EBADID		5		/* unknown transfer ID */
 #define	EEXISTS		6		/* file already exists */
 #define	ENOUSER		7		/* no such user */
+#define	EOPTNEG		8		/* option negotiation failed */
 
 #endif /* !_TFTP_H_ */
>Release-Note:
>Audit-Trail:
>Unformatted:

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200109211712.f8LHClv65384>