From owner-freebsd-bugs Fri Sep 21 10:20:22 2001 Delivered-To: freebsd-bugs@hub.freebsd.org Received: from freefall.freebsd.org (freefall.FreeBSD.org [216.136.204.21]) by hub.freebsd.org (Postfix) with ESMTP id 1CE4D37B40E for ; Fri, 21 Sep 2001 10:20:01 -0700 (PDT) Received: (from gnats@localhost) by freefall.freebsd.org (8.11.4/8.11.4) id f8LHK1912372; Fri, 21 Sep 2001 10:20:01 -0700 (PDT) (envelope-from gnats) Received: from iraun2.uka.de (iraun2.uka.de [129.13.10.91]) by hub.freebsd.org (Postfix) with ESMTP id 1507E37B420 for ; Fri, 21 Sep 2001 10:13:13 -0700 (PDT) Received: from i30nb2.ira.uka.de ([129.13.30.52]) by iraun2.uka.de with esmtp (Exim 3.30 #7 (Debian)) id 15kTrU-0006AD-00 for ; Fri, 21 Sep 2001 19:13:12 +0200 Received: (from esk@localhost) by i30nb2.ira.uka.de (8.11.5/8.11.3) id f8LHClv65384; Fri, 21 Sep 2001 19:12:47 +0200 (CEST) (envelope-from esk) Message-Id: <200109211712.f8LHClv65384@i30nb2.ira.uka.de> Date: Fri, 21 Sep 2001 19:12:47 +0200 (CEST) From: Espen Skoglund Reply-To: esk@ira.uka.de To: FreeBSD-gnats-submit@freebsd.org X-Send-Pr-Version: 3.2 Subject: bin/30710: Patch: tftpd extension --- RFC2349 Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.org >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 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