Date: Wed, 22 Sep 2010 21:54:34 +0000 (UTC) From: Marius Strobl <marius@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org Subject: svn commit: r213039 - stable/7/libexec/tftpd Message-ID: <201009222154.o8MLsYn1045188@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: marius Date: Wed Sep 22 21:54:34 2010 New Revision: 213039 URL: http://svn.freebsd.org/changeset/base/213039 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@FreeBSD.org> Added: stable/7/libexec/tftpd/tftp-file.c - copied unchanged from r207614, head/libexec/tftpd/tftp-file.c stable/7/libexec/tftpd/tftp-file.h - copied unchanged from r207614, head/libexec/tftpd/tftp-file.h stable/7/libexec/tftpd/tftp-io.c - copied, changed from r207614, head/libexec/tftpd/tftp-io.c stable/7/libexec/tftpd/tftp-io.h - copied unchanged from r207614, head/libexec/tftpd/tftp-io.h stable/7/libexec/tftpd/tftp-options.c - copied unchanged from r207614, head/libexec/tftpd/tftp-options.c stable/7/libexec/tftpd/tftp-options.h - copied unchanged from r207614, head/libexec/tftpd/tftp-options.h stable/7/libexec/tftpd/tftp-transfer.c - copied unchanged from r207614, head/libexec/tftpd/tftp-transfer.c stable/7/libexec/tftpd/tftp-transfer.h - copied unchanged from r207614, head/libexec/tftpd/tftp-transfer.h stable/7/libexec/tftpd/tftp-utils.c - copied unchanged from r207614, head/libexec/tftpd/tftp-utils.c stable/7/libexec/tftpd/tftp-utils.h - copied unchanged from r207614, head/libexec/tftpd/tftp-utils.h Modified: stable/7/libexec/tftpd/Makefile stable/7/libexec/tftpd/tftpd.8 stable/7/libexec/tftpd/tftpd.c Directory Properties: stable/7/libexec/tftpd/ (props changed) Modified: stable/7/libexec/tftpd/Makefile ============================================================================== --- stable/7/libexec/tftpd/Makefile Wed Sep 22 21:54:30 2010 (r213038) +++ stable/7/libexec/tftpd/Makefile Wed Sep 22 21:54:34 2010 (r213039) @@ -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/7/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/7/libexec/tftpd/tftp-file.c Wed Sep 22 21:54:34 2010 (r213039, 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/7/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/7/libexec/tftpd/tftp-file.h Wed Sep 22 21:54:34 2010 (r213039, 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/7/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/7/libexec/tftpd/tftp-io.c Wed Sep 22 21:54:34 2010 (r213039) @@ -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/7/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/7/libexec/tftpd/tftp-io.h Wed Sep 22 21:54:34 2010 (r213039, 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/7/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/7/libexec/tftpd/tftp-options.c Wed Sep 22 21:54:34 2010 (r213039, 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/7/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/7/libexec/tftpd/tftp-options.h Wed Sep 22 21:54:34 2010 (r213039, 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/7/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/7/libexec/tftpd/tftp-transfer.c Wed Sep 22 21:54:34 2010 (r213039, 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 ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201009222154.o8MLsYn1045188>