Date: Sat, 10 Mar 2018 11:08:28 -0700 From: Alan Somers <asomers@freebsd.org> To: Ilya Bakulin <ilya@bakulin.de> Cc: src-committers <src-committers@freebsd.org>, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: Re: svn commit: r330696 - in head/libexec/tftpd: . tests Message-ID: <CAOtMX2hUibRO8-bvfMwUKmeyJC=UcBPdN9fjtrDtU_0ApGg1Pw@mail.gmail.com> In-Reply-To: <CADzbx%2BrV_f%2Brxoae1vrUd-Ee_%2BAv5JhFNszQgunOovWKcrzyHg@mail.gmail.com> References: <201803091530.w29FUKin041283@repo.freebsd.org> <CADzbx%2BrV_f%2Brxoae1vrUd-Ee_%2BAv5JhFNszQgunOovWKcrzyHg@mail.gmail.com>
next in thread | previous in thread | raw e-mail | index | archive | help
Sorry, should be fixed by 330742. On Fri, Mar 9, 2018 at 11:49 PM, Ilya Bakulin <ilya@bakulin.de> wrote: > This broke build for at least ARM. > Fix: > diff --git a/libexec/tftpd/tests/functional.c b/libexec/tftpd/tests/ > functional.c > index fea6870cac59..c467dfee3ba0 100644 > --- a/libexec/tftpd/tests/functional.c > +++ b/libexec/tftpd/tests/functional.c > @@ -248,10 +248,10 @@ require_bufeq(const char *expected, ssize_t > expected_len, const char *actual, > ssize_t i; > > ATF_REQUIRE_EQ_MSG(expected_len, len, > - "Expected %ld bytes but got %ld", expected_len, len); > + "Expected %zd bytes but got %zd", expected_len, len); > for (i = 0; i < len; i++) { > ATF_REQUIRE_EQ_MSG(actual[i], expected[i], > - "Expected %#hhx at position %ld; got %hhx instead", > + "Expected %#hhx at position %zd; got %hhx instead", > expected[i], i, actual[i]); > } > } > > On Sat, Mar 10, 2018 at 12:30 AM Alan Somers <asomers@freebsd.org> wrote: > >> Author: asomers >> Date: Fri Mar 9 15:30:20 2018 >> New Revision: 330696 >> URL: https://svnweb.freebsd.org/changeset/base/330696 >> >> Log: >> Add some functional tests for tftpd(8) >> >> tftpd(8) is difficult to test in isolation due to its relationship with >> inetd. Create a test program that mimics the behavior of tftp(1) and >> inetd(8) and verifies tftpd's response in several different scenarios. >> >> These test cases cover all of the basic TFTP protocol, but not the >> optional >> parts. >> >> PR: 157700 >> PR: 225996 >> PR: 226004 >> PR: 226005 >> MFC after: 3 weeks >> Differential Revision: https://reviews.freebsd.org/D14310 >> >> Added: >> head/libexec/tftpd/tests/ >> head/libexec/tftpd/tests/Makefile (contents, props changed) >> head/libexec/tftpd/tests/functional.c (contents, props changed) >> Modified: >> head/libexec/tftpd/Makefile >> >> Modified: head/libexec/tftpd/Makefile >> ============================================================ >> ================== >> --- head/libexec/tftpd/Makefile Fri Mar 9 14:45:47 2018 (r330695) >> +++ head/libexec/tftpd/Makefile Fri Mar 9 15:30:20 2018 (r330696) >> @@ -14,4 +14,7 @@ CFLAGS+= -DLIBWRAP >> LIBADD= wrap >> .endif >> >> +HAS_TESTS= >> +SUBDIR.${MK_TESTS}+= tests >> + >> .include <bsd.prog.mk> >> >> Added: head/libexec/tftpd/tests/Makefile >> ============================================================ >> ================== >> --- /dev/null 00:00:00 1970 (empty, because file is newly added) >> +++ head/libexec/tftpd/tests/Makefile Fri Mar 9 15:30:20 2018 >> (r330696) >> @@ -0,0 +1,14 @@ >> +# $FreeBSD$ >> + >> +.include <bsd.own.mk> >> + >> +# Skip on GCC 4.2, because it lacks __COUNTER__ >> +.if ${COMPILER_TYPE} != "gcc" || ${COMPILER_VERSION} >= 40300 >> +ATF_TESTS_C= functional >> +TEST_METADATA.functional+= timeout=15 >> +.endif >> + >> +LIBADD= util >> +WARNS?= 6 >> + >> +.include <bsd.test.mk> >> >> Added: head/libexec/tftpd/tests/functional.c >> ============================================================ >> ================== >> --- /dev/null 00:00:00 1970 (empty, because file is newly added) >> +++ head/libexec/tftpd/tests/functional.c Fri Mar 9 15:30:20 >> 2018 (r330696) >> @@ -0,0 +1,1006 @@ >> +/*- >> + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD >> + * >> + * Copyright (c) 2018 Alan Somers. 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/param.h> >> +#include <sys/socket.h> >> +#include <sys/stat.h> >> +#include <sys/wait.h> >> + >> +#include <netinet/in.h> >> + >> +#include <errno.h> >> +#include <fcntl.h> >> +#include <signal.h> >> +#include <stdio.h> >> +#include <unistd.h> >> + >> +#include <atf-c.h> >> +#include <libutil.h> >> + >> +static const uint16_t BASEPORT = 6969; >> +static const char pidfile[] = "tftpd.pid"; >> +static int protocol = PF_UNSPEC; >> +static int s = -1; /* tftp client socket */ >> +static struct sockaddr_storage addr; /* Destination address for the >> client */ >> +static bool s_flag = false; /* Pass -s to tftpd */ >> +static bool w_flag = false; /* Pass -w to tftpd */ >> + >> +/* Helper functions*/ >> +static void require_bufeq(const char *expected, ssize_t expected_len, >> + const char *actual, ssize_t len); >> + >> +/* >> + * Receive a response from tftpd >> + * @param hdr The reply's expected header, as a char >> array >> + * @param contents The reply's expected contents, as a char >> array >> + * @param contents_len Length of contents >> + */ >> +#define RECV(hdr, contents, contents_len) do { \ >> + char buffer[1024]; \ >> + struct sockaddr_storage from; \ >> + socklen_t fromlen = sizeof(from); \ >> + ssize_t r = recvfrom(s, buffer, sizeof(buffer), 0, \ >> + (struct sockaddr*)&from, &fromlen); \ >> + ATF_REQUIRE(r > 0); \ >> + require_bufeq((hdr), sizeof(hdr), buffer, \ >> + MIN(r, (ssize_t)sizeof(hdr))); \ >> + require_bufeq((const char*) (contents), (contents_len), \ >> + &buffer[sizeof(hdr)], r - sizeof(hdr)); \ >> + if (protocol == PF_INET) { \ >> + ((struct sockaddr_in*)&addr)->sin_port = \ >> + ((struct sockaddr_in*)&from)->sin_port; \ >> + } else { \ >> + ((struct sockaddr_in6*)&addr)->sin6_port = \ >> + ((struct sockaddr_in6*)&from)->sin6_port; \ >> + } \ >> +} while(0) >> + >> +static void >> +recv_ack(uint16_t blocknum) >> +{ >> + char hdr[] = {0, 4, blocknum >> 8, blocknum & 0xFF}; >> + RECV(hdr, NULL, 0); >> +} >> + >> +/* >> + * Receive a data packet from tftpd >> + * @param blocknum Expected block number to be received >> + * @param contents Pointer to expected contents >> + * @param contents_len Length of contents expected to receive >> + */ >> +static void >> +recv_data(uint16_t blocknum, const char* contents, size_t contents_len) >> +{ >> + char hdr[] = {0, 3, blocknum >> 8, blocknum & 0xFF}; >> + RECV(hdr, contents, contents_len); >> +} >> + >> +#define RECV_ERROR(code, msg) do { \ >> + char hdr[] = {0, 5, code >> 8, code & 0xFF}; \ >> + RECV(hdr, msg, sizeof(msg)); \ >> +} while (0) >> + >> +/* >> + * send a command to tftpd. >> + * @param cmd Command to send, as a char array >> + */ >> +static void >> +send_bytes(const void* cmd, ssize_t len) >> +{ >> + ssize_t r; >> + >> + r = sendto(s, cmd, len, 0, (struct sockaddr*)(&addr), >> addr.ss_len); >> + ATF_REQUIRE_EQ(r, len); >> +} >> + >> +static void >> +send_data(uint16_t blocknum, const char* contents, size_t contents_len) >> +{ >> + char buffer[1024]; >> + >> + buffer[0] = 0; /* DATA opcode high byte */ >> + buffer[1] = 3; /* DATA opcode low byte */ >> + buffer[2] = blocknum >> 8; >> + buffer[3] = blocknum & 0xFF; >> + memmove(&buffer[4], contents, contents_len); >> + send_bytes(buffer, 4 + contents_len); >> +} >> + >> +/* >> + * send a command to tftpd. >> + * @param cmd Command to send, as a const string >> + * (terminating NUL will be ignored) >> + */ >> +#define SEND_STR(cmd) ATF_REQUIRE_EQ( \ >> + sendto(s, (cmd), sizeof(cmd) - 1, 0, (struct sockaddr*)(&addr), \ >> + addr.ss_len), \ >> + sizeof(cmd) - 1) >> + >> +/* >> + * Acknowledge block blocknum >> + */ >> +static void >> +send_ack(uint16_t blocknum) >> +{ >> + char packet[] = { >> + 0, 4, /* ACK opcode in BE */ >> + blocknum >> 8, >> + blocknum & 0xFF >> + }; >> + >> + send_bytes(packet, sizeof(packet)); >> + >> +} >> + >> +/* >> + * send a read request to tftpd. >> + * @param filename filename as a string, absolute or relative >> + * @param mode either "octet" or "netascii" >> + */ >> +#define SEND_RRQ(filename, mode) SEND_STR("\0\001" filename "\0" mode >> "\0") >> + >> +/* >> + * send a write request to tftpd. >> + * @param filename filename as a string, absolute or relative >> + * @param mode either "octet" or "netascii" >> + */ >> +#define SEND_WRQ(filename, mode) SEND_STR("\0\002" filename "\0" mode >> "\0") >> + >> +/* Define a test case, for both IPv4 and IPv6 */ >> +#define TFTPD_TC_DEFINE(name, head, ...) \ >> +static void \ >> +name ## _body(void); \ >> +ATF_TC_WITH_CLEANUP(name ## _v4); \ >> +ATF_TC_HEAD(name ## _v4, tc) \ >> +{ \ >> + head \ >> +} \ >> +ATF_TC_BODY(name ## _v4, tc) \ >> +{ \ >> + __VA_ARGS__; \ >> + protocol = AF_INET; \ >> + s = setup(&addr, __COUNTER__); \ >> + name ## _body(); \ >> + close(s); \ >> +} \ >> +ATF_TC_CLEANUP(name ## _v4, tc) \ >> +{ \ >> + cleanup(); \ >> +} \ >> +ATF_TC_WITH_CLEANUP(name ## _v6); \ >> +ATF_TC_HEAD(name ## _v6, tc) \ >> +{ \ >> + head \ >> +} \ >> +ATF_TC_BODY(name ## _v6, tc) \ >> +{ \ >> + __VA_ARGS__; \ >> + protocol = AF_INET6; \ >> + s = setup(&addr, __COUNTER__); \ >> + name ## _body(); \ >> + close(s); \ >> +} \ >> +ATF_TC_CLEANUP(name ## _v6, tc) \ >> +{ \ >> + cleanup(); \ >> +} \ >> +static void \ >> +name ## _body(void) >> + >> +/* Add the IPv4 and IPv6 versions of a test case */ >> +#define TFTPD_TC_ADD(tp, name ) \ >> +do { \ >> + ATF_TP_ADD_TC(tp, name ## _v4); \ >> + ATF_TP_ADD_TC(tp, name ## _v6); \ >> +} while (0) >> + >> +/* Standard cleanup used by all testcases */ >> +static void >> +cleanup(void) >> +{ >> + int fd = -1; >> + char buffer[80] = {0}; >> + pid_t pid; >> + >> + fd = open(pidfile, O_RDONLY); >> + if (fd < 0) >> + return; >> + if (read(fd, buffer, sizeof(buffer)) > 0) { >> + sscanf(buffer, "%d", &pid); >> + kill(pid, SIGTERM); >> + waitpid(pid, NULL, 0); >> + } >> + close(fd); >> + unlink(pidfile); >> +} >> + >> +/* Assert that two binary buffers are identical */ >> +static void >> +require_bufeq(const char *expected, ssize_t expected_len, const char >> *actual, >> + ssize_t len) >> +{ >> + ssize_t i; >> + >> + ATF_REQUIRE_EQ_MSG(expected_len, len, >> + "Expected %ld bytes but got %ld", expected_len, len); >> + for (i = 0; i < len; i++) { >> + ATF_REQUIRE_EQ_MSG(actual[i], expected[i], >> + "Expected %#hhx at position %ld; got %hhx instead", >> + expected[i], i, actual[i]); >> + } >> +} >> + >> +/* >> + * Start tftpd and return its communicating socket >> + * @param to Will be filled in for use with sendto >> + * @param idx Unique identifier of the test case >> + * @return Socket ready to use >> + */ >> +static int >> +setup(struct sockaddr_storage *to, uint16_t idx) >> +{ >> + int client_s, server_s, pid, argv_idx; >> + char execname[] = "/usr/libexec/tftpd"; >> + char s_flag_str[] = "-s"; >> + char w_flag_str[] = "-w"; >> + char pwd[MAXPATHLEN]; >> + char *argv[10]; >> + struct sockaddr_in addr4; >> + struct sockaddr_in6 addr6; >> + struct sockaddr *server_addr; >> + struct pidfh *pfh; >> + uint16_t port = BASEPORT + idx; >> + socklen_t len; >> + >> + if (protocol == PF_INET) { >> + len = sizeof(addr4); >> + bzero(&addr4, len); >> + addr4.sin_len = len; >> + addr4.sin_family = PF_INET; >> + addr4.sin_port = htons(port); >> + server_addr = (struct sockaddr*)&addr4; >> + } else { >> + len = sizeof(addr6); >> + bzero(&addr6, len); >> + addr6.sin6_len = len; >> + addr6.sin6_family = PF_INET6; >> + addr6.sin6_port = htons(port); >> + server_addr = (struct sockaddr*)&addr6; >> + } >> + >> + ATF_REQUIRE_EQ(getcwd(pwd, sizeof(pwd)), pwd); >> + >> + /* Must bind(2) pre-fork so it happens before the client's >> send(2) */ >> + ATF_REQUIRE((server_s = socket(protocol, SOCK_DGRAM, 0)) > 0); >> + ATF_REQUIRE_EQ_MSG(bind(server_s, server_addr, len), 0, >> + "bind failed with error %s", strerror(errno)); >> + >> + pid = fork(); >> + switch (pid) { >> + case -1: >> + atf_tc_fail("fork failed"); >> + break; >> + case 0: >> + /* In child */ >> + pfh = pidfile_open(pidfile, 0644, NULL); >> + ATF_REQUIRE_MSG(pfh != NULL, >> + "pidfile_open: %s", strerror(errno)); >> + ATF_REQUIRE_EQ(pidfile_write(pfh), 0); >> + ATF_REQUIRE_EQ(pidfile_close(pfh), 0); >> + >> + bzero(argv, sizeof(argv)); >> + argv[0] = execname; >> + argv_idx = 1; >> + if (w_flag) >> + argv[argv_idx++] = w_flag_str; >> + if (s_flag) >> + argv[argv_idx++] = s_flag_str; >> + argv[argv_idx++] = pwd; >> + ATF_REQUIRE_EQ(dup2(server_s, STDOUT_FILENO), >> STDOUT_FILENO); >> + ATF_REQUIRE_EQ(dup2(server_s, STDIN_FILENO), >> STDIN_FILENO); >> + ATF_REQUIRE_EQ(dup2(server_s, STDERR_FILENO), >> STDERR_FILENO); >> + execv(execname, argv); >> + atf_tc_fail("exec failed"); >> + break; >> + default: >> + /* In parent */ >> + bzero(to, sizeof(*to)); >> + if (protocol == PF_INET) { >> + struct sockaddr_in *to4 = (struct sockaddr_in*)to; >> + to4->sin_len = sizeof(*to4); >> + to4->sin_family = PF_INET; >> + to4->sin_port = htons(port); >> + to4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); >> + } else { >> + struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT; >> + struct sockaddr_in6 *to6 = (struct >> sockaddr_in6*)to; >> + to6->sin6_len = sizeof(*to6); >> + to6->sin6_family = PF_INET6; >> + to6->sin6_port = htons(port); >> + to6->sin6_addr = loopback; >> + } >> + >> + close(server_s); >> + ATF_REQUIRE((client_s = socket(protocol, SOCK_DGRAM, 0)) >> > 0); >> + break; >> + } >> + return (client_s); >> +} >> + >> +/* Like write(2), but never returns less than the requested length */ >> +static void >> +write_all(int fd, const void *buf, size_t nbytes) >> +{ >> + ssize_t r; >> + >> + while (nbytes > 0) { >> + r = write(fd, buf, nbytes); >> + ATF_REQUIRE(r > 0); >> + nbytes -= r; >> + buf = (const char*)buf + r; >> + } >> +} >> + >> + >> +/* >> + * Test Cases >> + */ >> + >> +/* >> + * Read a file, specified by absolute pathname. >> + */ >> +TFTPD_TC_DEFINE(abspath,) >> +{ >> + int fd; >> + char command[1024]; >> + size_t pathlen; >> + char suffix[] = {'\0', 'o', 'c', 't', 'e', 't', '\0'}; >> + >> + command[0] = 0; /* RRQ high byte */ >> + command[1] = 1; /* RRQ low byte */ >> + ATF_REQUIRE(getcwd(&command[2], sizeof(command) - 2) != NULL); >> + pathlen = strlcat(&command[2], "/abspath.txt", sizeof(command) - >> 2); >> + ATF_REQUIRE(pathlen + sizeof(suffix) < sizeof(command) - 2); >> + memmove(&command[2 + pathlen], suffix, sizeof(suffix)); >> + >> + fd = open("abspath.txt", O_CREAT | O_RDONLY, 0644); >> + ATF_REQUIRE(fd >= 0); >> + close(fd); >> + >> + send_bytes(command, 2 + pathlen + sizeof(suffix)); >> + recv_data(1, NULL, 0); >> + send_ack(1); >> +} >> + >> +/* >> + * Attempt to read a file outside of the allowed directory(ies) >> + */ >> +TFTPD_TC_DEFINE(dotdot,) >> +{ >> + ATF_REQUIRE_EQ(mkdir("subdir", 0777), 0); >> + SEND_RRQ("../disallowed.txt", "octet"); >> + RECV_ERROR(2, "Access violation"); >> + s = setup(&addr, __COUNTER__); \ >> + SEND_RRQ("subdir/../../disallowed.txt", "octet"); >> + RECV_ERROR(2, "Access violation"); >> + s = setup(&addr, __COUNTER__); \ >> + SEND_RRQ("/etc/passwd", "octet"); >> + RECV_ERROR(2, "Access violation"); >> +} >> + >> +/* >> + * With "-s", tftpd should chroot to the specified directory >> + */ >> +TFTPD_TC_DEFINE(s_flag, atf_tc_set_md_var(tc, "require.user", "root");, >> + s_flag = true) >> +{ >> + int fd; >> + char contents[] = "small"; >> + >> + fd = open("small.txt", O_RDWR | O_CREAT, 0644); >> + ATF_REQUIRE(fd >= 0); >> + write_all(fd, contents, strlen(contents) + 1); >> + close(fd); >> + >> + SEND_RRQ("/small.txt", "octet"); >> + recv_data(1, contents, strlen(contents) + 1); >> + send_ack(1); >> +} >> + >> +/* >> + * Read a file, and simulate a dropped ACK packet >> + */ >> +TFTPD_TC_DEFINE(rrq_dropped_ack,) >> +{ >> + int fd; >> + char contents[] = "small"; >> + >> + fd = open("small.txt", O_RDWR | O_CREAT, 0644); >> + ATF_REQUIRE(fd >= 0); >> + write_all(fd, contents, strlen(contents) + 1); >> + close(fd); >> + >> + SEND_RRQ("small.txt", "octet"); >> + recv_data(1, contents, strlen(contents) + 1); >> + /* >> + * client "sends" the ack, but network drops it >> + * Eventually, tftpd should resend the data packet >> + */ >> + recv_data(1, contents, strlen(contents) + 1); >> + send_ack(1); >> +} >> + >> +/* >> + * Read a file, and simulate a dropped DATA packet >> + */ >> +TFTPD_TC_DEFINE(rrq_dropped_data,) >> +{ >> + int fd; >> + size_t i; >> + uint32_t contents[192]; >> + char buffer[1024]; >> + >> + for (i = 0; i < nitems(contents); i++) >> + contents[i] = i; >> + >> + fd = open("medium.txt", O_RDWR | O_CREAT, 0644); >> + ATF_REQUIRE(fd >= 0); >> + write_all(fd, contents, sizeof(contents)); >> + close(fd); >> + >> + SEND_RRQ("medium.txt", "octet"); >> + recv_data(1, (const char*)&contents[0], 512); >> + send_ack(1); >> + (void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL); >> + /* >> + * server "sends" the data, but network drops it >> + * Eventually, client should resend the last ACK >> + */ >> + send_ack(1); >> + recv_data(2, (const char*)&contents[128], 256); >> + send_ack(2); >> +} >> + >> +/* >> + * Read a medium file, and simulate a duplicated ACK packet >> + */ >> +TFTPD_TC_DEFINE(rrq_duped_ack,) >> +{ >> + int fd; >> + size_t i; >> + uint32_t contents[192]; >> + >> + for (i = 0; i < nitems(contents); i++) >> + contents[i] = i; >> + >> + fd = open("medium.txt", O_RDWR | O_CREAT, 0644); >> + ATF_REQUIRE(fd >= 0); >> + write_all(fd, contents, sizeof(contents)); >> + close(fd); >> + >> + SEND_RRQ("medium.txt", "octet"); >> + recv_data(1, (const char*)&contents[0], 512); >> + send_ack(1); >> + send_ack(1); /* Dupe an ACK packet */ >> + recv_data(2, (const char*)&contents[128], 256); >> + recv_data(2, (const char*)&contents[128], 256); >> + send_ack(2); >> +} >> + >> + >> +/* >> + * Attempt to read a file without read permissions >> + */ >> +TFTPD_TC_DEFINE(rrq_eaccess,) >> +{ >> + int fd; >> + >> + fd = open("empty.txt", O_CREAT | O_RDONLY, 0000); >> + ATF_REQUIRE(fd >= 0); >> + close(fd); >> + >> + SEND_RRQ("empty.txt", "octet"); >> + RECV_ERROR(2, "Access violation"); >> +} >> + >> +/* >> + * Read an empty file >> + */ >> +TFTPD_TC_DEFINE(rrq_empty,) >> +{ >> + int fd; >> + >> + fd = open("empty.txt", O_CREAT | O_RDONLY, 0644); >> + ATF_REQUIRE(fd >= 0); >> + close(fd); >> + >> + SEND_RRQ("empty.txt", "octet"); >> + recv_data(1, NULL, 0); >> + send_ack(1); >> +} >> + >> +/* >> + * Read a medium file of more than one block >> + */ >> +TFTPD_TC_DEFINE(rrq_medium,) >> +{ >> + int fd; >> + size_t i; >> + uint32_t contents[192]; >> + >> + for (i = 0; i < nitems(contents); i++) >> + contents[i] = i; >> + >> + fd = open("medium.txt", O_RDWR | O_CREAT, 0644); >> + ATF_REQUIRE(fd >= 0); >> + write_all(fd, contents, sizeof(contents)); >> + close(fd); >> + >> + SEND_RRQ("medium.txt", "octet"); >> + recv_data(1, (const char*)&contents[0], 512); >> + send_ack(1); >> + recv_data(2, (const char*)&contents[128], 256); >> + send_ack(2); >> +} >> + >> +/* >> + * Read a file in netascii format >> + */ >> +TFTPD_TC_DEFINE(rrq_netascii,) >> +{ >> + int fd; >> + char contents[] = "foo\nbar\rbaz\n"; >> + /* >> + * Weirdly, RFC-764 says that CR must be followed by NUL if a >> line feed >> + * is not intended >> + */ >> + char expected[] = "foo\r\nbar\r\0baz\r\n"; >> + >> + fd = open("unix.txt", O_RDWR | O_CREAT, 0644); >> + ATF_REQUIRE(fd >= 0); >> + write_all(fd, contents, strlen(contents) + 1); >> + close(fd); >> + >> + SEND_RRQ("unix.txt", "netascii"); >> + recv_data(1, expected, sizeof(expected)); >> + send_ack(1); >> +} >> + >> +/* >> + * Read a file that doesn't exist >> + */ >> +TFTPD_TC_DEFINE(rrq_nonexistent,) >> +{ >> + SEND_RRQ("nonexistent.txt", "octet"); >> + RECV_ERROR(1, "File not found"); >> +} >> + >> +/* >> + * Attempt to read a file whose name exceeds PATH_MAX >> + */ >> +TFTPD_TC_DEFINE(rrq_path_max,) >> +{ >> +#define AReallyBigFileName \ >> + "AReallyBigFileNameXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >> XXXX"\ >> + ".txt" >> + ATF_REQUIRE_MSG(strlen(AReallyBigFileName) > PATH_MAX, >> + "Somebody increased PATH_MAX. Update the test"); >> + SEND_RRQ(AReallyBigFileName, "octet"); >> + RECV_ERROR(4, "Illegal TFTP operation"); >> +} >> + >> +/* >> + * Read a small file of less than one block >> + */ >> +TFTPD_TC_DEFINE(rrq_small,) >> +{ >> + int fd; >> + char contents[] = "small"; >> + >> + fd = open("small.txt", O_RDWR | O_CREAT, 0644); >> + ATF_REQUIRE(fd >= 0); >> + write_all(fd, contents, strlen(contents) + 1); >> + close(fd); >> + >> + SEND_RRQ("small.txt", "octet"); >> + recv_data(1, contents, strlen(contents) + 1); >> + send_ack(1); >> +} >> + >> +/* >> + * Try to transfer a file with an unknown mode. >> + */ >> +TFTPD_TC_DEFINE(unknown_modes,) >> +{ >> + SEND_RRQ("foo.txt", "ascii"); /* Misspelling of "ascii" */ >> + RECV_ERROR(4, "Illegal TFTP operation"); >> + s = setup(&addr, __COUNTER__); \ >> + SEND_RRQ("foo.txt", "binary"); /* Obsolete. Use "octet" instead >> */ >> + RECV_ERROR(4, "Illegal TFTP operation"); >> + s = setup(&addr, __COUNTER__); \ >> + SEND_RRQ("foo.txt", "en_US.UTF-8"); >> + RECV_ERROR(4, "Illegal TFTP operation"); >> + s = setup(&addr, __COUNTER__); \ >> + SEND_RRQ("foo.txt", "mail"); /* Obsolete in RFC-1350 */ >> + RECV_ERROR(4, "Illegal TFTP operation"); >> +} >> + >> +/* >> + * Send an unknown opcode. tftpd should respond with the appropriate >> error >> + */ >> +TFTPD_TC_DEFINE(unknown_opcode,) >> +{ >> + /* Looks like an RRQ or WRQ request, but with a bad opcode */ >> + SEND_STR("\0\007foo.txt\0octet\0"); >> + atf_tc_expect_timeout("PR 226005 tftpd ignores bad opcodes but >> doesn't reject them"); >> + RECV_ERROR(4, "Illegal TFTP operation"); >> +} >> + >> +/* >> + * Invoke tftpd with "-w" and write to a nonexistent file. >> + */ >> +TFTPD_TC_DEFINE(w_flag,, w_flag = 1;) >> +{ >> + int fd; >> + ssize_t r; >> + char contents[] = "small"; >> + char buffer[1024]; >> + size_t contents_len; >> + >> + contents_len = strlen(contents) + 1; >> + SEND_WRQ("small.txt", "octet"); >> + recv_ack(0); >> + send_data(1, contents, contents_len); >> + recv_ack(1); >> + >> + atf_tc_expect_fail("PR 157700 tftpd expects more data after EOF"); >> + fd = open("small.txt", O_RDONLY); >> + r = read(fd, buffer, sizeof(buffer)); >> + close(fd); >> + require_bufeq(contents, contents_len, buffer, r); >> +} >> + >> +/* >> + * Write a medium file, and simulate a dropped ACK packet >> + */ >> +TFTPD_TC_DEFINE(wrq_dropped_ack,) >> +{ >> + int fd; >> + size_t i; >> + ssize_t r; >> + uint32_t contents[192]; >> + char buffer[1024]; >> + >> + for (i = 0; i < nitems(contents); i++) >> + contents[i] = i; >> + >> + fd = open("medium.txt", O_RDWR | O_CREAT, 0644); >> + ATF_REQUIRE(fd >= 0); >> + close(fd); >> + >> + SEND_WRQ("medium.txt", "octet"); >> + recv_ack(0); >> + send_data(1, (const char*)&contents[0], 512); >> + /* >> + * Servers "sends" an ACK packet, but network drops it. >> + * Eventually, server should resend the last ACK >> + */ >> + (void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL); >> + recv_ack(1); >> + send_data(2, (const char*)&contents[128], 256); >> + recv_ack(2); >> + >> + atf_tc_expect_fail("PR 157700 tftpd expects more data after EOF"); >> + fd = open("medium.txt", O_RDONLY); >> + r = read(fd, buffer, sizeof(buffer)); >> + close(fd); >> + require_bufeq((const char*)contents, 768, buffer, r); >> +} >> + >> +/* >> + * Write a small file, and simulate a dropped DATA packet >> + */ >> +TFTPD_TC_DEFINE(wrq_dropped_data,) >> +{ >> + int fd; >> + ssize_t r; >> + char contents[] = "small"; >> + size_t contents_len; >> + char buffer[1024]; >> + >> + fd = open("small.txt", O_RDWR | O_CREAT, 0644); >> + ATF_REQUIRE(fd >= 0); >> + close(fd); >> + contents_len = strlen(contents) + 1; >> + >> + SEND_WRQ("small.txt", "octet"); >> + recv_ack(0); >> + /* >> + * Client "sends" a DATA packet, but network drops it. >> + * Eventually, server should resend the last ACK >> + */ >> + recv_ack(0); >> + send_data(1, contents, contents_len); >> + recv_ack(1); >> + >> + atf_tc_expect_fail("PR 157700 tftpd expects more data after EOF"); >> + fd = open("small.txt", O_RDONLY); >> + r = read(fd, buffer, sizeof(buffer)); >> + close(fd); >> + require_bufeq(contents, contents_len, buffer, r); >> +} >> + >> +/* >> + * Write a medium file, and simulate a duplicated DATA packet >> + */ >> +TFTPD_TC_DEFINE(wrq_duped_data,) >> +{ >> + int fd; >> + size_t i; >> + ssize_t r; >> + uint32_t contents[192]; >> + char buffer[1024]; >> + >> + for (i = 0; i < nitems(contents); i++) >> + contents[i] = i; >> + >> + fd = open("medium.txt", O_RDWR | O_CREAT, 0644); >> + ATF_REQUIRE(fd >= 0); >> + close(fd); >> + >> + SEND_WRQ("medium.txt", "octet"); >> + recv_ack(0); >> + send_data(1, (const char*)&contents[0], 512); >> + send_data(1, (const char*)&contents[0], 512); >> + recv_ack(1); >> + recv_ack(1); >> + send_data(2, (const char*)&contents[128], 256); >> + recv_ack(2); >> + >> + atf_tc_expect_fail("PR 157700 tftpd expects more data after EOF"); >> + fd = open("medium.txt", O_RDONLY); >> + r = read(fd, buffer, sizeof(buffer)); >> + close(fd); >> + require_bufeq((const char*)contents, 768, buffer, r); >> +} >> + >> +/* >> + * Attempt to write a file without write permissions >> + */ >> +TFTPD_TC_DEFINE(wrq_eaccess,) >> +{ >> + int fd; >> + >> + fd = open("empty.txt", O_CREAT | O_RDONLY, 0440); >> + ATF_REQUIRE(fd >= 0); >> + close(fd); >> + >> + SEND_WRQ("empty.txt", "octet"); >> + atf_tc_expect_fail("PR 225996 tftpd doesn't abort on a WRQ access >> " >> + "violation"); >> + RECV_ERROR(2, "Access violation"); >> +} >> + >> +/* >> + * Attempt to write a file without world write permissions, but with >> world >> + * read permissions >> + */ >> +TFTPD_TC_DEFINE(wrq_eaccess_world_readable,) >> +{ >> + int fd; >> + >> + fd = open("empty.txt", O_CREAT | O_RDONLY, 0444); >> + ATF_REQUIRE(fd >= 0); >> + close(fd); >> + >> + SEND_WRQ("empty.txt", "octet"); >> + atf_tc_expect_fail("PR 226004 with relative pathnames, tftpd >> doesn't validate world writability"); >> + RECV_ERROR(2, "Access violation"); >> +} >> + >> + >> +/* >> + * Write a medium file of more than one block >> + */ >> +TFTPD_TC_DEFINE(wrq_medium,) >> +{ >> + int fd; >> + size_t i; >> + ssize_t r; >> + uint32_t contents[192]; >> + char buffer[1024]; >> + >> + for (i = 0; i < nitems(contents); i++) >> + contents[i] = i; >> + >> + fd = open("medium.txt", O_RDWR | O_CREAT, 0666); >> + ATF_REQUIRE(fd >= 0); >> + close(fd); >> + >> + SEND_WRQ("medium.txt", "octet"); >> + recv_ack(0); >> + send_data(1, (const char*)&contents[0], 512); >> + recv_ack(1); >> + send_data(2, (const char*)&contents[128], 256); >> + recv_ack(2); >> + >> + atf_tc_expect_fail("PR 157700 tftpd expects more data after EOF"); >> + fd = open("medium.txt", O_RDONLY); >> + r = read(fd, buffer, sizeof(buffer)); >> + close(fd); >> + require_bufeq((const char*)contents, 768, buffer, r); >> +} >> + >> +/* >> + * Write a file in netascii format >> + */ >> +TFTPD_TC_DEFINE(wrq_netascii,) >> +{ >> + int fd; >> + ssize_t r; >> + /* >> + * Weirdly, RFC-764 says that CR must be followed by NUL if a >> line feed >> + * is not intended >> + */ >> + char contents[] = "foo\r\nbar\r\0baz\r\n"; >> + char expected[] = "foo\nbar\rbaz\n"; >> + size_t contents_len; >> + char buffer[1024]; >> + >> + fd = open("unix.txt", O_RDWR | O_CREAT, 0666); >> + ATF_REQUIRE(fd >= 0); >> + close(fd); >> + contents_len = strlen(contents) + 1; >> + >> + SEND_WRQ("unix.txt", "netascii"); >> + recv_ack(0); >> + send_data(1, contents, contents_len); >> + recv_ack(1); >> + >> + atf_tc_expect_fail("PR 157700 tftpd expects more data after EOF"); >> + fd = open("unix.txt", O_RDONLY); >> + r = read(fd, buffer, sizeof(buffer)); >> + close(fd); >> + require_bufeq(expected, sizeof(expected), buffer, r); >> +} >> + >> +/* >> + * Attempt to write to a nonexistent file. With the default options, >> this >> + * isn't allowed. >> + */ >> +TFTPD_TC_DEFINE(wrq_nonexistent,) >> +{ >> + SEND_WRQ("nonexistent.txt", "octet"); >> + atf_tc_expect_fail("PR 225996 tftpd doesn't abort on a WRQ access >> " >> + "violation"); >> + RECV_ERROR(1, "File not found"); >> +} >> + >> +/* >> + * Write a small file of less than one block >> + */ >> +TFTPD_TC_DEFINE(wrq_small,) >> +{ >> + int fd; >> + ssize_t r; >> + char contents[] = "small"; >> + size_t contents_len; >> + char buffer[1024]; >> + >> + fd = open("small.txt", O_RDWR | O_CREAT, 0666); >> + ATF_REQUIRE(fd >= 0); >> + close(fd); >> + contents_len = strlen(contents) + 1; >> + >> + SEND_WRQ("small.txt", "octet"); >> + recv_ack(0); >> + send_data(1, contents, contents_len); >> + recv_ack(1); >> + >> + atf_tc_expect_fail("PR 157700 tftpd expects more data after EOF"); >> + fd = open("small.txt", O_RDONLY); >> + r = read(fd, buffer, sizeof(buffer)); >> + close(fd); >> + require_bufeq(contents, contents_len, buffer, r); >> +} >> + >> +/* >> + * Write an empty file over a non-empty one >> + */ >> +TFTPD_TC_DEFINE(wrq_truncate,) >> +{ >> + int fd; >> + char contents[] = "small"; >> + struct stat sb; >> + >> + fd = open("small.txt", O_RDWR | O_CREAT, 0666); >> + ATF_REQUIRE(fd >= 0); >> + write_all(fd, contents, strlen(contents) + 1); >> + close(fd); >> + >> + SEND_WRQ("small.txt", "octet"); >> + recv_ack(0); >> + send_data(1, NULL, 0); >> + recv_ack(1); >> + >> + ATF_REQUIRE_EQ(stat("small.txt", &sb), 0); >> + ATF_REQUIRE_EQ(sb.st_size, 0); >> +} >> + >> >> *** DIFF OUTPUT TRUNCATED AT 1000 LINES *** >> >>
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAOtMX2hUibRO8-bvfMwUKmeyJC=UcBPdN9fjtrDtU_0ApGg1Pw>