From owner-svn-src-user@FreeBSD.ORG Sun Oct 12 10:10:42 2008 Return-Path: Delivered-To: svn-src-user@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id A1B3B1065690; Sun, 12 Oct 2008 10:10:42 +0000 (UTC) (envelope-from edwin@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 8EF448FC18; Sun, 12 Oct 2008 10:10:42 +0000 (UTC) (envelope-from edwin@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id m9CAAgpX041861; Sun, 12 Oct 2008 10:10:42 GMT (envelope-from edwin@svn.freebsd.org) Received: (from edwin@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id m9CAAgGO041854; Sun, 12 Oct 2008 10:10:42 GMT (envelope-from edwin@svn.freebsd.org) Message-Id: <200810121010.m9CAAgGO041854@svn.freebsd.org> From: Edwin Groothuis Date: Sun, 12 Oct 2008 10:10:42 +0000 (UTC) To: src-committers@freebsd.org, svn-src-user@freebsd.org X-SVN-Group: user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r183798 - in user/edwin/tftpd: libexec/tftpd usr.bin/tftp X-BeenThere: svn-src-user@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the experimental " user" src tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Oct 2008 10:10:42 -0000 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 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 +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 +__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 +__FBSDID("$FreeBSD"); + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +__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 +__FBSDID("$FreeBSD"); + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#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 ***