svn commit: r213038 - stable/8/libexec/tftpd

Marius Strobl marius at FreeBSD.org
Wed Sep 22 21:54:30 UTC 2010


Author: marius
Date: Wed Sep 22 21:54:30 2010
New Revision: 213038
URL: http://svn.freebsd.org/changeset/base/213038

Log:
  MFC: r207608, r207614, r212651, r212665
  
  Go ahead and merge the work edwin@ on tftpd into the tree.  It is a
  lot better than what's in the tree now.  Edwin tested it at a prior
  employer, but can't test it today.  I've found that it works a lot
  better with the various uboot versions that I've used in my embedded
  work.  Here's the pkg-descr from the port that describes the changes:
  
  It all started when we got some new routers, which told me the
  following when trying to upload configuration or download images
  from it: The TFTP server doesn't support the blocksize option.
  
  My curiousity was triggered, it took me some reading of RFCs and
  other documentation to find out what was possible and what could
  be done. Was plain TFTP very simple in its handshake, TFTP with
  options was kind of messy because of its backwards capability: The
  first packet returned could either be an acknowledgement of options,
  or the first data packet.
  
  Going through the source code of src/libexec/tftpd and going through
  the code of src/usr.bin/tftp showed that there was a lot of duplicate
  code, and the addition of options would only increase the amount
  of duplicate code. After all, both the client and the server can
  act as a sender and receiver.
  
  At the end, it ended up with a nearly complete rewrite of the tftp
  client and server. It has been tested against the following TFTP
  clients and servers:
  
  - Itself (yay!)
  - The standard FreeBSD tftp client and server
  - The Fedora Core 6 tftp client and server
  - Cisco router tftp client
  - Extreme Networks tftp client
  
  It supports the following RFCs:
  
  RFC1350 - THE TFTP PROTOCOL (REVISION 2)
  RFC2347 - TFTP Option Extension
  RFC2348 - TFTP Blocksize Option
  RFC2349 - TFTP Timeout Interval and Transfer Size Options
  RFC3617 - Uniform Resource Identifier (URI) Scheme and Applicability
            Statement for the Trivial File Transfer Protocol (TFTP)
  
  It supports the following unofficial TFTP Options as described at
  http://www.compuphase.com/tftp.htm:
  
  blksize2 - Block size restricted to powers of 2, excluding protocol headers
  rollover - Block counter roll-over (roll back to zero or to one)
  
  From the tftp program point of view the following things are changed:
  
  - New commands: "blocksize", "blocksize2", "rollover" and "options"
  - Development features: "debug" and "packetdrop"
  
  If you try this tftp/tftpd implementation, please let me know if
  it works (or doesn't work) and against which implementaion so I can
  get a list of confirmed working systems.
  
  Author: Edwin Groothuis <edwin at FreeBSD.org>

Added:
  stable/8/libexec/tftpd/tftp-file.c
     - copied unchanged from r207614, head/libexec/tftpd/tftp-file.c
  stable/8/libexec/tftpd/tftp-file.h
     - copied unchanged from r207614, head/libexec/tftpd/tftp-file.h
  stable/8/libexec/tftpd/tftp-io.c
     - copied, changed from r207614, head/libexec/tftpd/tftp-io.c
  stable/8/libexec/tftpd/tftp-io.h
     - copied unchanged from r207614, head/libexec/tftpd/tftp-io.h
  stable/8/libexec/tftpd/tftp-options.c
     - copied unchanged from r207614, head/libexec/tftpd/tftp-options.c
  stable/8/libexec/tftpd/tftp-options.h
     - copied unchanged from r207614, head/libexec/tftpd/tftp-options.h
  stable/8/libexec/tftpd/tftp-transfer.c
     - copied unchanged from r207614, head/libexec/tftpd/tftp-transfer.c
  stable/8/libexec/tftpd/tftp-transfer.h
     - copied unchanged from r207614, head/libexec/tftpd/tftp-transfer.h
  stable/8/libexec/tftpd/tftp-utils.c
     - copied unchanged from r207614, head/libexec/tftpd/tftp-utils.c
  stable/8/libexec/tftpd/tftp-utils.h
     - copied unchanged from r207614, head/libexec/tftpd/tftp-utils.h
Modified:
  stable/8/libexec/tftpd/Makefile
  stable/8/libexec/tftpd/tftpd.8
  stable/8/libexec/tftpd/tftpd.c
Directory Properties:
  stable/8/libexec/tftpd/   (props changed)

Modified: stable/8/libexec/tftpd/Makefile
==============================================================================
--- stable/8/libexec/tftpd/Makefile	Wed Sep 22 21:54:16 2010	(r213037)
+++ stable/8/libexec/tftpd/Makefile	Wed Sep 22 21:54:30 2010	(r213038)
@@ -2,12 +2,13 @@
 # $FreeBSD$
 
 PROG=	tftpd
-SRCS=	tftpd.c tftpsubs.c
-DPADD=	${LIBUTIL}
-LDADD=	-lutil
+SRCS=	tftpd.c tftp-io.c tftp-utils.c tftp-file.c tftp-transfer.c tftp-options.c
+WARNS=	3
 WFORMAT=0
 MAN=	tftpd.8
-CFLAGS+=-I${.CURDIR}/../../usr.bin/tftp
+CFLAGS+=-I${.CURDIR}/../../usr.bin/tftp -I${.CURDIR}/../../libexec/tftpd
 .PATH:	${.CURDIR}/../../usr.bin/tftp
+COPTFLAGS = -O
+LDFLAGS= -lwrap
 
 .include <bsd.prog.mk>

Copied: stable/8/libexec/tftpd/tftp-file.c (from r207614, head/libexec/tftpd/tftp-file.c)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ stable/8/libexec/tftpd/tftp-file.c	Wed Sep 22 21:54:30 2010	(r213038, copy of r207614, head/libexec/tftpd/tftp-file.c)
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "tftp-file.h"
+#include "tftp-utils.h"
+
+static FILE	*file;
+static int	convert;
+
+static char	convbuffer[66000];
+static int	gotcr = 0;
+
+static size_t
+convert_from_net(char *buffer, size_t count)
+{
+	size_t i, n;
+
+	/*
+	 * Convert all CR/LF to LF and all CR,NUL to CR
+	 */
+
+	n = 0;
+	for (i = 0; i < count; i++) {
+
+		if (gotcr == 0) {
+			convbuffer[n++] = buffer[i];
+			gotcr = (buffer[i] == '\r');
+			continue;
+		}
+
+		/* CR, NULL -> CR */
+		if (buffer[i] == '\0') {
+			gotcr = 0;
+			continue;
+		}
+
+		/* CR, LF -> LF */
+		if (buffer[i] == '\n') {
+			if (n == 0) {
+				if (ftell(file) != 0) {
+					fseek(file, -1, SEEK_END);
+					convbuffer[n++] = '\n';
+				} else {
+					/* This shouldn't happen */
+					tftp_log(LOG_ERR,
+					    "Received LF as first character");
+					abort();
+				}
+			} else
+				convbuffer[n-1] = '\n';
+			gotcr = 0;
+			continue;
+		}
+
+		/* Everything else just accept as is */
+		convbuffer[n++] = buffer[i];
+		gotcr = (buffer[i] == '\r');
+		continue;
+	}
+
+	return fwrite(convbuffer, 1, n, file);
+}
+
+static size_t
+convert_to_net(char *buffer, size_t count, int init)
+{
+	size_t i;
+	static size_t n = 0, read = 0;
+	static int newline = 0;
+
+	if (init) {
+		newline = 0;
+		n = 0;
+		read = 0;
+		return 0 ;
+	}
+
+	/*
+	 * Convert all LF to CR,LF and all CR to CR,NUL
+	 */
+	i = 0;
+
+	if (newline) {
+		buffer[i++] = newline;
+		newline = 0;
+	}
+
+	while (i < count) {
+		if (n == read) {
+			/* When done we're done */
+			if (feof(file)) break;
+
+			/* Otherwise read another bunch */
+			read = fread(convbuffer, 1, count, file);
+			if (read == 0) break;
+			n = 0;
+		}
+
+		/* CR -> CR,NULL */
+		if (convbuffer[n] == '\r') {
+			buffer[i++] = '\r';
+			buffer[i++] = '\0';
+			n++;
+			continue;
+		}
+
+		/* LF -> CR,LF */
+		if (convbuffer[n] == '\n') {
+			buffer[i++] = '\r';
+			buffer[i++] = '\n';
+			n++;
+			continue;
+		}
+
+		buffer[i++] = convbuffer[n++];
+	}
+
+	if (i > count) {
+		/*
+		 * Whoops... that isn't alllowed (but it will happen
+		 * when there is a CR or LF at the end of the buffer)
+		 */
+		newline = buffer[i-1];
+	}
+
+	if (i < count) {
+		/* We are done! */
+		return i;
+	} else
+		return count;
+
+}
+
+int
+write_init(int fd, FILE *f, const char *mode)
+{
+
+	if (f == NULL) {
+		file = fdopen(fd, "w");
+		if (file == NULL) {
+			int en = errno;
+			tftp_log(LOG_ERR, "fdopen() failed: %s",
+			    strerror(errno));
+			return en;
+		}
+	} else
+		file = f;
+	convert = !strcmp(mode, "netascii");
+	return 0;
+}
+
+size_t
+write_file(char *buffer, int count)
+{
+
+	if (convert == 0)
+		return fwrite(buffer, 1, count, file);
+
+	return convert_from_net(buffer, count);
+}
+
+int
+write_close(void)
+{
+
+	if (fclose(file) != 0) {
+		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
+		return 1;
+	}
+	return 0;
+}
+
+int
+read_init(int fd, FILE *f, const char *mode)
+{
+
+	convert_to_net(NULL, 0, 1);
+	if (f == NULL) {
+		file = fdopen(fd, "r");
+		if (file == NULL) {
+			int en = errno;
+			tftp_log(LOG_ERR, "fdopen() failed: %s",
+			    strerror(errno));
+			return en;
+		}
+	} else
+		file = f;
+	convert = !strcmp(mode, "netascii");
+	return 0;
+}
+
+size_t
+read_file(char *buffer, int count)
+{
+
+	if (convert == 0)
+		return fread(buffer, 1, count, file);
+
+	return convert_to_net(buffer, count, 0);
+}
+
+int
+read_close(void)
+{
+
+	if (fclose(file) != 0) {
+		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
+		return 1;
+	}
+	return 0;
+}
+
+
+int
+synchnet(int peer)
+{
+
+	return 0;
+}

Copied: stable/8/libexec/tftpd/tftp-file.h (from r207614, head/libexec/tftpd/tftp-file.h)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ stable/8/libexec/tftpd/tftp-file.h	Wed Sep 22 21:54:30 2010	(r213038, copy of r207614, head/libexec/tftpd/tftp-file.h)
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+int	write_init(int fd, FILE *f, const char *mode);
+size_t	write_file(char *buffer, int count);
+int	write_close(void);
+
+int	read_init(int fd, FILE *f, const char *mode);
+size_t	read_file(char *buffer, int count);
+int	read_close(void);
+
+int	synchnet(int peer);

Copied and modified: stable/8/libexec/tftpd/tftp-io.c (from r207614, head/libexec/tftpd/tftp-io.c)
==============================================================================
--- head/libexec/tftpd/tftp-io.c	Tue May  4 13:07:40 2010	(r207614, copy source)
+++ stable/8/libexec/tftpd/tftp-io.c	Wed Sep 22 21:54:30 2010	(r213038)
@@ -398,8 +398,6 @@ receive_packet(int peer, char *data, int
 	int n;
 	static int waiting;
 
-	pfrom = (from == NULL) ? &from_local : from;
-
 	if (debug&DEBUG_PACKETS)
 		tftp_log(LOG_DEBUG,
 		    "Waiting %d seconds for packet", timeoutpacket);
@@ -423,6 +421,7 @@ receive_packet(int peer, char *data, int
 	}
 
 	waiting++;
+	pfrom = (from == NULL) ? &from_local : from;
 	fromlen = sizeof(*pfrom);
 	n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);
 

Copied: stable/8/libexec/tftpd/tftp-io.h (from r207614, head/libexec/tftpd/tftp-io.h)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ stable/8/libexec/tftpd/tftp-io.h	Wed Sep 22 21:54:30 2010	(r213038, copy of r207614, head/libexec/tftpd/tftp-io.h)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define RP_NONE		0
+#define	RP_RECVFROM	-1
+#define	RP_TOOSMALL	-2
+#define RP_ERROR	-3
+#define RP_WRONGSOURCE	-4
+#define	RP_TIMEOUT	-5
+#define	RP_TOOBIG	-6
+
+const char *errtomsg(int);
+void	send_error(int peer, int);
+int	send_wrq(int peer, char *, char *);
+int	send_rrq(int peer, char *, char *);
+int	send_oack(int peer);
+int	send_ack(int peer, unsigned short);
+int	send_data(int peer, uint16_t, char *, int);
+int	receive_packet(int peer, char *, int, struct sockaddr_storage *, int);
+
+extern struct sockaddr_storage peer_sock;
+extern struct sockaddr_storage me_sock;

Copied: stable/8/libexec/tftpd/tftp-options.c (from r207614, head/libexec/tftpd/tftp-options.c)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ stable/8/libexec/tftpd/tftp-options.c	Wed Sep 22 21:54:30 2010	(r213038, copy of r207614, head/libexec/tftpd/tftp-options.c)
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "tftp-utils.h"
+#include "tftp-io.h"
+#include "tftp-options.h"
+
+/*
+ * Option handlers
+ */
+
+struct options options[] = {
+	{ "tsize",	NULL, NULL, NULL /* option_tsize */, 1 },
+	{ "timeout",	NULL, NULL, option_timeout, 1 },
+	{ "blksize",	NULL, NULL, option_blksize, 1 },
+	{ "blksize2",	NULL, NULL, option_blksize2, 0 },
+	{ "rollover",	NULL, NULL, option_rollover, 0 },
+	{ NULL,		NULL, NULL, NULL, 0 }
+};
+
+/* By default allow them */
+int options_rfc_enabled = 1;
+int options_extra_enabled = 1;
+
+/*
+ * Rules for the option handlers:
+ * - If there is no o_request, there will be no processing.
+ *
+ * For servers
+ * - Logging is done as warnings.
+ * - The handler exit()s if there is a serious problem with the
+ *   values submitted in the option.
+ *
+ * For clients
+ * - Logging is done as errors. After all, the server shouldn't
+ *   return rubbish.
+ * - The handler returns if there is a serious problem with the
+ *   values submitted in the option.
+ * - Sending the EBADOP packets is done by the handler.
+ */
+
+int
+option_tsize(int peer, struct tftphdr *tp, int mode, struct stat *stbuf)
+{
+
+	if (options[OPT_TSIZE].o_request == NULL)
+		return (0);
+
+	if (mode == RRQ) 
+		asprintf(&options[OPT_TSIZE].o_reply,
+			"%ju", stbuf->st_size);
+	else
+		/* XXX Allows writes of all sizes. */
+		options[OPT_TSIZE].o_reply =
+			strdup(options[OPT_TSIZE].o_request);
+	return (0);
+}
+
+int
+option_timeout(int peer)
+{
+
+	if (options[OPT_TIMEOUT].o_request == NULL)
+		return (0);
+
+	int to = atoi(options[OPT_TIMEOUT].o_request);
+	if (to < TIMEOUT_MIN || to > TIMEOUT_MAX) {
+		tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
+		    "Received bad value for timeout. "
+		    "Should be between %d and %d, received %s",
+		    TIMEOUT_MIN, TIMEOUT_MAX);
+		send_error(peer, EBADOP);
+		if (acting_as_client)
+			return (1);
+		exit(1);
+	} else {
+		timeoutpacket = to;
+		options[OPT_TIMEOUT].o_reply =
+			strdup(options[OPT_TIMEOUT].o_request);
+	}
+	settimeouts(timeoutpacket, timeoutnetwork, maxtimeouts);
+
+	if (debug&DEBUG_OPTIONS)
+		tftp_log(LOG_DEBUG, "Setting timeout to '%s'",
+			options[OPT_TIMEOUT].o_reply);
+
+	return (0);
+}
+
+int
+option_rollover(int peer)
+{
+
+	if (options[OPT_ROLLOVER].o_request == NULL)
+		return (0);
+
+	if (strcmp(options[OPT_ROLLOVER].o_request, "0") != 0
+	 && strcmp(options[OPT_ROLLOVER].o_request, "1") != 0) {
+		tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
+		    "Bad value for rollover, "
+		    "should be either 0 or 1, received '%s', "
+		    "ignoring request",
+		    options[OPT_ROLLOVER].o_request);
+		if (acting_as_client) {
+			send_error(peer, EBADOP);
+			return (1);
+		}
+		return (0);
+	}
+	options[OPT_ROLLOVER].o_reply =
+		strdup(options[OPT_ROLLOVER].o_request);
+
+	if (debug&DEBUG_OPTIONS)
+		tftp_log(LOG_DEBUG, "Setting rollover to '%s'",
+			options[OPT_ROLLOVER].o_reply);
+
+	return (0);
+}
+
+int
+option_blksize(int peer)
+{
+	int *maxdgram;
+	char maxbuffer[100];
+	size_t len;
+
+	if (options[OPT_BLKSIZE].o_request == NULL)
+		return (0);
+
+	/* maximum size of an UDP packet according to the system */
+	len = sizeof(maxbuffer);
+	if (sysctlbyname("net.inet.udp.maxdgram",
+	    maxbuffer, &len, NULL, 0) < 0) {
+		tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
+		return (acting_as_client ? 1 : 0);
+	}
+	maxdgram = (int *)maxbuffer;
+
+	int size = atoi(options[OPT_BLKSIZE].o_request);
+	if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
+		if (acting_as_client) {
+			tftp_log(LOG_ERR,
+			    "Invalid blocksize (%d bytes), aborting",
+			    size);
+			send_error(peer, EBADOP);
+			return (1);
+		} else {
+			tftp_log(LOG_WARNING,
+			    "Invalid blocksize (%d bytes), ignoring request",
+			    size);
+			return (0);
+		}
+	}
+
+	if (size > *maxdgram) {
+		if (acting_as_client) {
+			tftp_log(LOG_ERR,
+			    "Invalid blocksize (%d bytes), "
+			    "net.inet.udp.maxdgram sysctl limits it to "
+			    "%d bytes.\n", size, *maxdgram);
+			send_error(peer, EBADOP);
+			return (1);
+		} else {
+			tftp_log(LOG_WARNING,
+			    "Invalid blocksize (%d bytes), "
+			    "net.inet.udp.maxdgram sysctl limits it to "
+			    "%d bytes.\n", size, *maxdgram);
+			size = *maxdgram;
+			/* No reason to return */
+		}
+	}
+
+	asprintf(&options[OPT_BLKSIZE].o_reply, "%d", size);
+	segsize = size;
+	pktsize = size + 4;
+	if (debug&DEBUG_OPTIONS)
+		tftp_log(LOG_DEBUG, "Setting blksize to '%s'",
+		    options[OPT_BLKSIZE].o_reply);
+
+	return (0);
+}
+
+int
+option_blksize2(int peer)
+{
+	int	*maxdgram;
+	char	maxbuffer[100];
+	int	size, i;
+	size_t	len;
+
+	int sizes[] = {
+		8, 16, 32, 64, 128, 256, 512, 1024,
+		2048, 4096, 8192, 16384, 32768, 0
+	};
+
+	if (options[OPT_BLKSIZE2].o_request == NULL)
+		return (0);
+
+	/* maximum size of an UDP packet according to the system */
+	len = sizeof(maxbuffer);
+	if (sysctlbyname("net.inet.udp.maxdgram",
+	    maxbuffer, &len, NULL, 0) < 0) {
+		tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
+		return (acting_as_client ? 1 : 0);
+	}
+	maxdgram = (int *)maxbuffer;
+
+	size = atoi(options[OPT_BLKSIZE2].o_request);
+	for (i = 0; sizes[i] != 0; i++) {
+		if (size == sizes[i]) break;
+	}
+	if (sizes[i] == 0) {
+		tftp_log(LOG_INFO,
+		    "Invalid blocksize2 (%d bytes), ignoring request", size);
+		return (acting_as_client ? 1 : 0);
+	}
+
+	if (size > *maxdgram) {
+		for (i = 0; sizes[i+1] != 0; i++) {
+			if (*maxdgram < sizes[i+1]) break;
+		}
+		tftp_log(LOG_INFO,
+		    "Invalid blocksize2 (%d bytes), net.inet.udp.maxdgram "
+		    "sysctl limits it to %d bytes.\n", size, *maxdgram);
+		size = sizes[i];
+		/* No need to return */
+	}
+
+	asprintf(&options[OPT_BLKSIZE2].o_reply, "%d", size);
+	segsize = size;
+	pktsize = size + 4;
+	if (debug&DEBUG_OPTIONS)
+		tftp_log(LOG_DEBUG, "Setting blksize2 to '%s'",
+		    options[OPT_BLKSIZE2].o_reply);
+
+	return (0);
+}
+
+/*
+ * Append the available options to the header
+ */
+uint16_t
+make_options(int peer, char *buffer, uint16_t size) {
+	int	i;
+	char	*value;
+	const char *option;
+	uint16_t length;
+	uint16_t returnsize = 0;
+
+	if (!options_rfc_enabled) return (0);
+
+	for (i = 0; options[i].o_type != NULL; i++) {
+		if (options[i].rfc == 0 && !options_extra_enabled)
+			continue;
+
+		option = options[i].o_type;
+		if (acting_as_client)
+			value = options[i].o_request;
+		else
+			value = options[i].o_reply;
+		if (value == NULL)
+			continue;
+
+		length = strlen(value) + strlen(option) + 2;
+		if (size <= length) {
+			tftp_log(LOG_ERR,
+			    "Running out of option space for "
+			    "option '%s' with value '%s': "
+			    "needed %d bytes, got %d bytes",
+			    option, value, size, length);
+			continue;
+		}
+
+		sprintf(buffer, "%s%c%s%c", option, '\000', value, '\000');
+		size -= length;
+		buffer += length;
+		returnsize += length;
+	}
+
+	return (returnsize);
+}
+
+/*
+ * Parse the received options in the header
+ */
+int
+parse_options(int peer, char *buffer, uint16_t size)
+{
+	int	i, options_failed;
+	char	*c, *cp, *option, *value;
+
+	if (!options_rfc_enabled) return (0);
+
+	/* Parse the options */
+	cp = buffer;
+	options_failed = 0;	
+	while (size > 0) {
+		option = cp;
+		i = get_field(peer, cp, size);
+		cp += i;
+
+		value = cp;
+		i = get_field(peer, cp, size);
+		cp += i;
+
+		/* We are at the end */
+		if (*option == '\0') break;
+
+		if (debug&DEBUG_OPTIONS)
+			tftp_log(LOG_DEBUG,
+			    "option: '%s' value: '%s'", option, value);
+
+		for (c = option; *c; c++)
+			if (isupper(*c))
+				*c = tolower(*c);
+		for (i = 0; options[i].o_type != NULL; i++) {
+			if (strcmp(option, options[i].o_type) == 0) {
+				if (!acting_as_client)
+					options[i].o_request = value;
+				if (!options_extra_enabled && !options[i].rfc) {
+					tftp_log(LOG_INFO,
+					    "Option '%s' with value '%s' found "
+					    "but it is not an RFC option",
+					    option, value);
+					continue;
+				}
+				if (options[i].o_handler)
+					options_failed +=
+					    (options[i].o_handler)(peer);
+				break;
+			}
+		}
+		if (options[i].o_type == NULL)
+			tftp_log(LOG_WARNING,
+			    "Unknown option: '%s'", option);
+
+		size -= strlen(option) + strlen(value) + 2;
+	}
+
+	return (options_failed);
+}
+
+/*
+ * Set some default values in the options
+ */
+void
+init_options(void)
+{
+
+	options[OPT_ROLLOVER].o_request = strdup("0");
+}

Copied: stable/8/libexec/tftpd/tftp-options.h (from r207614, head/libexec/tftpd/tftp-options.h)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ stable/8/libexec/tftpd/tftp-options.h	Wed Sep 22 21:54:30 2010	(r213038, copy of r207614, head/libexec/tftpd/tftp-options.h)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Options
+ */
+
+void		init_options(void);
+uint16_t	make_options(int peer, char *buffer, uint16_t size);
+int		parse_options(int peer, char *buffer, uint16_t size);
+
+/* Call back functions */
+int	option_tsize(int peer, struct tftphdr *, int, struct stat *);
+int	option_timeout(int peer);
+int	option_blksize(int peer);
+int	option_blksize2(int peer);
+int	option_rollover(int peer);
+
+extern int options_extra_enabled;
+extern int options_rfc_enabled;
+
+struct options {
+	const char	*o_type;
+	char		*o_request;
+	char		*o_reply;
+	int		(*o_handler)(int peer);
+	int		rfc;
+};
+
+extern struct options	options[];
+enum opt_enum {
+	OPT_TSIZE = 0,
+	OPT_TIMEOUT,
+	OPT_BLKSIZE,
+	OPT_BLKSIZE2,
+	OPT_ROLLOVER,
+};

Copied: stable/8/libexec/tftpd/tftp-transfer.c (from r207614, head/libexec/tftpd/tftp-transfer.c)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ stable/8/libexec/tftpd/tftp-transfer.c	Wed Sep 22 21:54:30 2010	(r213038, copy of r207614, head/libexec/tftpd/tftp-transfer.c)
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include "tftp-file.h"
+#include "tftp-io.h"
+#include "tftp-utils.h"
+#include "tftp-options.h"
+#include "tftp-transfer.h"
+
+/*
+ * Send a file via the TFTP data session.
+ */
+void
+tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
+{
+	struct tftphdr *rp;
+	int size, n_data, n_ack, try;
+	uint16_t oldblock;
+	char sendbuffer[MAXPKTSIZE];
+	char recvbuffer[MAXPKTSIZE];
+
+	rp = (struct tftphdr *)recvbuffer;
+	*block = 1;
+	ts->amount = 0;
+	do {
+		if (debug&DEBUG_SIMPLE)
+			tftp_log(LOG_DEBUG, "Sending block %d", *block);
+
+		size = read_file(sendbuffer, segsize);
+		if (size < 0) {
+			tftp_log(LOG_ERR, "read_file returned %d", size);
+			send_error(peer, errno + 100);
+			goto abort;
+		}
+
+		for (try = 0; ; try++) {
+			n_data = send_data(peer, *block, sendbuffer, size);
+			if (n_data > 0) {
+				if (try == maxtimeouts) {
+					tftp_log(LOG_ERR,
+					    "Cannot send DATA packet #%d, "
+					    "giving up", *block);
+					return;
+				}
+				tftp_log(LOG_ERR,
+				    "Cannot send DATA packet #%d, trying again",
+				    *block);
+				continue;
+			}
+
+			n_ack = receive_packet(peer, recvbuffer,
+			    MAXPKTSIZE, NULL, timeoutpacket);
+			if (n_ack < 0) {
+				if (n_ack == RP_TIMEOUT) {
+					if (try == maxtimeouts) {
+						tftp_log(LOG_ERR,
+						    "Timeout #%d send ACK %d "
+						    "giving up", try, *block);
+						return;
+					}
+					tftp_log(LOG_WARNING,
+					    "Timeout #%d on ACK %d",
+					    try, *block);
+					continue;
+				}
+
+				/* Either read failure or ERROR packet */
+				if (debug&DEBUG_SIMPLE)
+					tftp_log(LOG_ERR, "Aborting: %s",
+					    rp_strerror(n_ack));
+				goto abort;
+			}
+			if (rp->th_opcode == ACK) {
+				ts->blocks++;
+				if (rp->th_block == *block) {
+					ts->amount += size;
+					break;
+				}
+
+				/* Re-synchronize with the other side */
+				(void) synchnet(peer);
+				if (rp->th_block == (*block - 1)) {
+					ts->retries++;
+					continue;
+				}
+			}
+
+		}
+		oldblock = *block;
+		(*block)++;
+		if (oldblock > *block) {
+			if (options[OPT_ROLLOVER].o_request == NULL) {
+				tftp_log(LOG_ERR,
+				    "Block rollover but not allowed.");
+				send_error(peer, EBADOP);
+				gettimeofday(&(ts->tstop), NULL);
+				return;
+			}
+
+			*block = atoi(options[OPT_ROLLOVER].o_request);
+			ts->rollovers++;
+		}
+		gettimeofday(&(ts->tstop), NULL);
+	} while (size == segsize);
+abort:
+	return;
+}
+
+/*
+ * Receive a file via the TFTP data session.
+ *

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list