svn commit: r207614 - head/libexec/tftpd

Warner Losh imp at FreeBSD.org
Tue May 4 13:07:40 UTC 2010


Author: imp
Date: Tue May  4 13:07:40 2010
New Revision: 207614
URL: http://svn.freebsd.org/changeset/base/207614

Log:
  Bring in new files from edwin's tftp

Added:
  head/libexec/tftpd/tftp-file.c   (contents, props changed)
  head/libexec/tftpd/tftp-file.h   (contents, props changed)
  head/libexec/tftpd/tftp-io.c   (contents, props changed)
  head/libexec/tftpd/tftp-io.h   (contents, props changed)
  head/libexec/tftpd/tftp-options.c   (contents, props changed)
  head/libexec/tftpd/tftp-options.h   (contents, props changed)
  head/libexec/tftpd/tftp-transfer.c   (contents, props changed)
  head/libexec/tftpd/tftp-transfer.h   (contents, props changed)
  head/libexec/tftpd/tftp-utils.c   (contents, props changed)
  head/libexec/tftpd/tftp-utils.h   (contents, props changed)

Added: head/libexec/tftpd/tftp-file.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/libexec/tftpd/tftp-file.c	Tue May  4 13:07:40 2010	(r207614)
@@ -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: head/libexec/tftpd/tftp-file.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/libexec/tftpd/tftp-file.h	Tue May  4 13:07:40 2010	(r207614)
@@ -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: head/libexec/tftpd/tftp-io.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/libexec/tftpd/tftp-io.c	Tue May  4 13:07:40 2010	(r207614)
@@ -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: head/libexec/tftpd/tftp-io.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/libexec/tftpd/tftp-io.h	Tue May  4 13:07:40 2010	(r207614)
@@ -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: head/libexec/tftpd/tftp-options.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/libexec/tftpd/tftp-options.c	Tue May  4 13:07:40 2010	(r207614)
@@ -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)

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


More information about the svn-src-head mailing list