svn commit: r183798 - in user/edwin/tftpd: libexec/tftpd usr.bin/tftp

Edwin Groothuis edwin at FreeBSD.org
Sun Oct 12 10:10:42 UTC 2008


Author: edwin
Date: Sun Oct 12 10:10:42 2008
New Revision: 183798
URL: http://svn.freebsd.org/changeset/base/183798

Log:
  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.

Added:
  user/edwin/tftpd/libexec/tftpd/tftp-file.c
  user/edwin/tftpd/libexec/tftpd/tftp-file.h
  user/edwin/tftpd/libexec/tftpd/tftp-io.c
  user/edwin/tftpd/libexec/tftpd/tftp-io.h
  user/edwin/tftpd/libexec/tftpd/tftp-options.c
  user/edwin/tftpd/libexec/tftpd/tftp-options.h
  user/edwin/tftpd/libexec/tftpd/tftp-transfer.c
  user/edwin/tftpd/libexec/tftpd/tftp-transfer.h
  user/edwin/tftpd/libexec/tftpd/tftp-utils.c
  user/edwin/tftpd/libexec/tftpd/tftp-utils.h
  user/edwin/tftpd/usr.bin/tftp/tftp.h
Deleted:
  user/edwin/tftpd/usr.bin/tftp/extern.h
  user/edwin/tftpd/usr.bin/tftp/tftpsubs.c
  user/edwin/tftpd/usr.bin/tftp/tftpsubs.h
Modified:
  user/edwin/tftpd/libexec/tftpd/Makefile
  user/edwin/tftpd/libexec/tftpd/tftpd.8
  user/edwin/tftpd/libexec/tftpd/tftpd.c
  user/edwin/tftpd/usr.bin/tftp/Makefile
  user/edwin/tftpd/usr.bin/tftp/main.c
  user/edwin/tftpd/usr.bin/tftp/tftp.1
  user/edwin/tftpd/usr.bin/tftp/tftp.c

Modified: user/edwin/tftpd/libexec/tftpd/Makefile
==============================================================================
--- user/edwin/tftpd/libexec/tftpd/Makefile	Sun Oct 12 10:05:06 2008	(r183797)
+++ user/edwin/tftpd/libexec/tftpd/Makefile	Sun Oct 12 10:10:42 2008	(r183798)
@@ -2,12 +2,14 @@
 # $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=-g -Wall
+CFLAGS+=-I${.CURDIR}/../../usr.bin/tftp -I${.CURDIR}/../../libexec/tftpd
 .PATH:	${.CURDIR}/../../usr.bin/tftp
+COPTFLAGS = -O
+LDFLAGS= -lwrap
 
 .include <bsd.prog.mk>

Added: user/edwin/tftpd/libexec/tftpd/tftp-file.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/edwin/tftpd/libexec/tftpd/tftp-file.c	Sun Oct 12 10:10:42 2008	(r183798)
@@ -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;
+}

Added: user/edwin/tftpd/libexec/tftpd/tftp-file.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/edwin/tftpd/libexec/tftpd/tftp-file.h	Sun Oct 12 10:10:42 2008	(r183798)
@@ -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);

Added: user/edwin/tftpd/libexec/tftpd/tftp-io.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/edwin/tftpd/libexec/tftpd/tftp-io.c	Sun Oct 12 10:10:42 2008	(r183798)
@@ -0,0 +1,478 @@
+/*
+ * 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/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "tftp-file.h"
+#include "tftp-io.h"
+#include "tftp-utils.h"
+#include "tftp-options.h"
+
+struct sockaddr_storage peer_sock;
+struct sockaddr_storage me_sock;
+
+static int send_packet(int peer, uint16_t block, char *pkt, int size);
+
+struct errmsg {
+	int	e_code;
+	const char	*e_msg;
+} errmsgs[] = {
+	{ EUNDEF,	"Undefined error code" },
+	{ ENOTFOUND,	"File not found" },
+	{ EACCESS,	"Access violation" },
+	{ ENOSPACE,	"Disk full or allocation exceeded" },
+	{ EBADOP,	"Illegal TFTP operation" },
+	{ EBADID,	"Unknown transfer ID" },
+	{ EEXISTS,	"File already exists" },
+	{ ENOUSER,	"No such user" },
+	{ EOPTNEG,	"Option negotiation" },
+	{ -1,		NULL }
+};
+
+#define DROPPACKET(s)							\
+	if (packetdroppercentage != 0 &&				\
+	    random()%100 < packetdroppercentage) {			\
+		tftp_log(LOG_DEBUG, "Artifical packet drop in %s", s);	\
+		return;							\
+	}
+#define DROPPACKETn(s,n)						\
+	if (packetdroppercentage != 0 &&				\
+	    random()%100 < packetdroppercentage) {			\
+		tftp_log(LOG_DEBUG, "Artifical packet drop in %s", s);	\
+		return (n);						\
+	}
+
+const char *
+errtomsg(int error)
+{
+	static char ebuf[40];
+	struct errmsg *pe;
+	char buf[MAXPKTSIZE];
+
+	if (error == 0)
+		return ("success");
+	for (pe = errmsgs; pe->e_code >= 0; pe++)
+		if (pe->e_code == error)
+			return (pe->e_msg);
+	snprintf(ebuf, sizeof(buf), "error %d", error);
+	return (ebuf);
+}
+
+static int
+send_packet(int peer, uint16_t block, char *pkt, int size)
+{
+	int i;
+	int t = 1;
+
+	for (i = 0; i < 12 ; i++) {
+		DROPPACKETn("send_packet", 0);
+
+		if (sendto(peer, pkt, size, 0,
+			(struct sockaddr *)&peer_sock, peer_sock.ss_len)
+			== size) {
+			if (i)
+				tftp_log(LOG_ERR,
+				    "%s block %d, attempt %d successful",
+				    block, i);
+			return (0);
+		}
+		tftp_log(LOG_ERR,
+		    "%s block %d, attempt %d failed (Error %d: %s)", 
+		    packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)),
+		    block, i, errno, strerror(errno));
+		sleep(t);
+		if (t < 32)
+			t <<= 1;
+	}
+	tftp_log(LOG_ERR, "send_packet: %s", strerror(errno));
+	return (1);
+}
+
+/*
+ * Send an ERROR packet (error message).
+ * Error code passed in is one of the
+ * standard TFTP codes, or a UNIX errno
+ * offset by 100.
+ */
+void
+send_error(int peer, int error)
+{
+	struct tftphdr *tp;
+	int length;
+	struct errmsg *pe;
+	char buf[MAXPKTSIZE];
+
+	if (debug&DEBUG_PACKETS)
+		tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error);
+
+	DROPPACKET("send_error");
+
+	tp = (struct tftphdr *)buf;
+	tp->th_opcode = htons((u_short)ERROR);
+	tp->th_code = htons((u_short)error);
+	for (pe = errmsgs; pe->e_code >= 0; pe++)
+		if (pe->e_code == error)
+			break;
+	if (pe->e_code < 0) {
+		pe->e_msg = strerror(error - 100);
+		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
+	}
+	strcpy(tp->th_msg, pe->e_msg);
+	length = strlen(pe->e_msg);
+	tp->th_msg[length] = '\0';
+	length += 5;
+
+	if (debug&DEBUG_PACKETS)
+		tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg);
+
+	if (sendto(peer, buf, length, 0,
+		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != length)
+		tftp_log(LOG_ERR, "send_error: %s", strerror(errno));
+}
+
+/*
+ * Send an WRQ packet (write request).
+ */
+int
+send_wrq(int peer, char *filename, char *mode)
+{
+	int n;
+	struct tftphdr *tp;
+	char *bp;
+	char buf[MAXPKTSIZE];
+	int size;
+
+	if (debug&DEBUG_PACKETS)
+		tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'",
+			filename, mode
+		);
+
+	DROPPACKETn("send_wrq", 1);
+
+	tp = (struct tftphdr *)buf;
+	tp->th_opcode = htons((u_short)WRQ);
+	size = 2;
+
+	bp = tp->th_stuff;
+	strcpy(bp, filename);
+	bp += strlen(filename);
+	*bp = 0;
+	bp++;
+	size += strlen(filename) + 1;
+
+	strcpy(bp, mode);
+	bp += strlen(mode);
+	*bp = 0;
+	bp++;
+	size += strlen(mode) + 1;
+
+	if (options_rfc_enabled)
+		size += make_options(peer, bp, sizeof(buf) - size);
+
+	n = sendto(peer, buf, size, 0,
+	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
+	if (n != size) {
+		tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno));
+		return (1);
+	}
+	return (0);
+}
+
+/*
+ * Send an RRQ packet (write request).
+ */
+int
+send_rrq(int peer, char *filename, char *mode)
+{
+	int n;
+	struct tftphdr *tp;
+	char *bp;
+	char buf[MAXPKTSIZE];
+	int size;
+
+	if (debug&DEBUG_PACKETS)
+		tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'",
+			filename, mode
+		);
+
+	DROPPACKETn("send_rrq", 1);
+
+	tp = (struct tftphdr *)buf;
+	tp->th_opcode = htons((u_short)RRQ);
+	size = 2;
+
+	bp = tp->th_stuff;
+	strcpy(bp, filename);
+	bp += strlen(filename);
+	*bp = 0;
+	bp++;
+	size += strlen(filename) + 1;
+
+	strcpy(bp, mode);
+	bp += strlen(mode);
+	*bp = 0;
+	bp++;
+	size += strlen(mode) + 1;
+
+	if (options_rfc_enabled) {
+		options[OPT_TSIZE].o_request = strdup("0");
+		size += make_options(peer, bp, sizeof(buf) - size);
+	}
+
+	n = sendto(peer, buf, size, 0,
+	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
+	if (n != size) {
+		tftp_log(LOG_ERR, "send_rrq: %s", n, strerror(errno));
+		return (1);
+	}
+	return (0);
+}
+
+/*
+ * Send an OACK packet (option acknowledgement).
+ */
+int
+send_oack(int peer)
+{
+	struct tftphdr *tp;
+	int size, i, n;
+	char *bp;
+	char buf[MAXPKTSIZE];
+
+	if (debug&DEBUG_PACKETS)
+		tftp_log(LOG_DEBUG, "Sending OACK");
+
+	DROPPACKETn("send_oack", 0);
+
+	/*
+	 * Send back an options acknowledgement (only the ones with
+	 * a reply for)
+	 */
+	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_reply != NULL) {
+			n = snprintf(bp, size, "%s%c%s", options[i].o_type,
+				     0, options[i].o_reply);
+			bp += n+1;
+			size -= n+1;
+			if (size < 0) {
+				tftp_log(LOG_ERR, "oack: buffer overflow");
+				exit(1);
+			}
+		}
+	}
+	size = bp - buf;
+
+	if (sendto(peer, buf, size, 0,
+		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
+		tftp_log(LOG_INFO, "send_oack: %s", strerror(errno));
+		return (1);
+	}
+
+	return (0);
+}
+
+/*
+ * Send an ACK packet (acknowledgement).
+ */
+int
+send_ack(int fp, uint16_t block)
+{
+	struct tftphdr *tp;
+	int size;
+	char *bp;
+	char buf[MAXPKTSIZE];
+
+	if (debug&DEBUG_PACKETS)
+		tftp_log(LOG_DEBUG, "Sending ACK for block %d", block);
+
+	DROPPACKETn("send_ack", 0);
+
+	tp = (struct tftphdr *)buf;
+	bp = buf + 2;
+	size = sizeof(buf) - 2;
+	tp->th_opcode = htons((u_short)ACK);
+	tp->th_block = htons((u_short)block);
+	size = 4;
+
+	if (sendto(fp, buf, size, 0,
+	    (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
+		tftp_log(LOG_INFO, "send_ack: %s", strerror(errno));
+		return (1);
+	}
+
+	return (0);
+}
+
+/*
+ * Send a DATA packet
+ */
+int
+send_data(int peer, uint16_t block, char *data, int size)
+{
+	char buf[MAXPKTSIZE];
+	struct tftphdr *pkt;
+	int n;
+
+	if (debug&DEBUG_PACKETS)
+		tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes",
+			block, size);
+
+	DROPPACKETn("send_data", 0);
+
+	pkt = (struct tftphdr *)buf;
+
+	pkt->th_opcode = htons((u_short)DATA);
+	pkt->th_block = htons((u_short)block);
+	memcpy(pkt->th_data, data, size);
+
+	n = send_packet(peer, block, (char *)pkt, size + 4);
+	return (n);
+}
+
+
+/*
+ * Receive a packet
+ */
+jmp_buf	timeoutbuf;
+
+static void
+timeout(int sig __unused)
+{
+
+	/* tftp_log(LOG_DEBUG, "Timeout\n");	Inside a signal handler... */
+	longjmp(timeoutbuf, 1);
+}
+
+int
+receive_packet(int peer, char *data, int size, struct sockaddr_storage *from,
+    int thistimeout)
+{
+	struct tftphdr *pkt;
+	struct sockaddr_storage from_local;
+	struct sockaddr_storage *pfrom;
+	socklen_t fromlen;
+	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);
+
+	pkt = (struct tftphdr *)data;
+
+	waiting = 0;
+	signal(SIGALRM, timeout);
+	setjmp(timeoutbuf);
+	alarm(thistimeout);
+
+	if (waiting > 0) {
+		alarm(0);
+		return (RP_TIMEOUT);
+	}
+
+	if (waiting > 0) {
+		tftp_log(LOG_ERR, "receive_packet: timeout");
+		alarm(0);
+		return (RP_TIMEOUT);
+	}
+
+	waiting++;
+	fromlen = sizeof(*pfrom);
+	n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);
+
+	alarm(0);
+
+	DROPPACKETn("receive_packet", RP_TIMEOUT);
+
+	if (n < 0) {
+		tftp_log(LOG_ERR, "receive_packet: timeout");
+		return (RP_TIMEOUT);
+	}
+
+	alarm(0);
+
+	if (n < 0) {
+		/* No idea what could have happened if it isn't a timeout */
+		tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno));
+		return (RP_RECVFROM);
+	}
+	if (n < 4) {
+		tftp_log(LOG_ERR,
+		    "receive_packet: packet too small (%d bytes)", n);
+		return (RP_TOOSMALL);
+	}
+
+	pkt->th_opcode = ntohs((u_short)pkt->th_opcode);
+	if (pkt->th_opcode == DATA ||
+	    pkt->th_opcode == ACK)
+		pkt->th_block = ntohs((u_short)pkt->th_block);
+
+	if (pkt->th_opcode == DATA && n > pktsize) {
+		tftp_log(LOG_ERR, "receive_packet: packet too big");
+		return (RP_TOOBIG);
+	}
+
+	if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr !=
+	    ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) {
+		tftp_log(LOG_ERR,
+			"receive_packet: received packet from wrong source");
+		return (RP_WRONGSOURCE);
+	}
+
+	if (pkt->th_opcode == ERROR) {
+		tftp_log(LOG_ERR, "Got ERROR packet: %s", pkt->th_msg);
+		return (RP_ERROR);
+	}
+
+	if (debug&DEBUG_PACKETS)
+		tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet",
+			n, packettype(pkt->th_opcode));
+
+	return n - 4;
+}

Added: user/edwin/tftpd/libexec/tftpd/tftp-io.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/edwin/tftpd/libexec/tftpd/tftp-io.h	Sun Oct 12 10:10:42 2008	(r183798)
@@ -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;

Added: user/edwin/tftpd/libexec/tftpd/tftp-options.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/edwin/tftpd/libexec/tftpd/tftp-options.c	Sun Oct 12 10:10:42 2008	(r183798)
@@ -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);

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


More information about the svn-src-user mailing list