From owner-svn-src-all@freebsd.org Sat Mar 10 06:49:47 2018 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 8E252F29C4F for ; Sat, 10 Mar 2018 06:49:46 +0000 (UTC) (envelope-from ilya@bakulin.de) Received: from mail-lf0-x22f.google.com (mail-lf0-x22f.google.com [IPv6:2a00:1450:4010:c07::22f]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (Client CN "smtp.gmail.com", Issuer "Google Internet Authority G2" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id B2E928676F for ; Sat, 10 Mar 2018 06:49:45 +0000 (UTC) (envelope-from ilya@bakulin.de) Received: by mail-lf0-x22f.google.com with SMTP id l191-v6so16199255lfe.1 for ; Fri, 09 Mar 2018 22:49:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bakulin-de.20150623.gappssmtp.com; s=20150623; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=MWLAUHNPwpFNpLF4Rv9L5oZrptorCBJ4Sh+iJhlbeqY=; b=BO8LBwjqi9LnQEGR+BuVgiN5j4qulP82iZvkX+bnRAO3YTXzKe8pbQ+fdldcoRQb98 u6F8AFFcNEVX+kVvwf3YdUyd8ZiZ1ySjOSUj1mfnxwLMHSxbxP1PkqrV0/vG4bMNp52Y OSVmqk7wLYdeoSY/bWwSoaNcAboFkx3zJxpPKk3VnwgUN1BTQrFB5pTCeaiZsRcUtPlJ FM5V7yAMaQ1rv/smDb/i8aTYf51L3XCUajBjk8Yrf6umx2M6bKOfgDqgLw15ufCsayKD BLeivjD720U/y+h9FwllxxpAXp5k9LUJyzvhzApaB0t2QtltYr+QqXTJWQSO6ii+6oXT GEag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=MWLAUHNPwpFNpLF4Rv9L5oZrptorCBJ4Sh+iJhlbeqY=; b=fFZhIz6qdV/beTpC4+m0m3vNjx94VtlGYjdSgSrRTXr7ex/1zCOI5cSyuRqKbdpkgm MYDG79766Cw6gfrE/28LhsgnzdOPIqGBsvq9FnmSgRK/Z1vwMkHn4Yi9DaSh6RNwWLlh YHFAarkAbk9dewg+IFLlq0O3BjLEaMDZ/9dYyfxJlibPbVa1syMSZqVd2ZB1xSmC/lla Wir95j5N28pRWr6vD9sv31v941rRHwc3WFoyGoe+ix9dB9O6bCvWmJ1bQ4uRSh6rORhb oiCHkoSwbgyMcdoIyGJSB8NQoLakJDWRGLLpe9FnpimLuRLPoNNSu1gVSj7equWywVTf lYaA== X-Gm-Message-State: AElRT7G6V5/InP6+oC8iZmN27d92o8Bh3LkGTKSQw17IlBCIZzfAecZQ g3/L9HNOjQaxRMfZVmDE8MknDlWTcSWgzKBvWqjeQNKh X-Google-Smtp-Source: AG47ELuZ0Bq1U2A/7zOk371yyIYPnbEYEG8HVuHt+TW+bXMrw0gAyaS6053Mg1AlPEpDlqNeREXAPNTA6c7JNkphqo0= X-Received: by 10.46.16.74 with SMTP id j71mr656817lje.139.1520664583970; Fri, 09 Mar 2018 22:49:43 -0800 (PST) MIME-Version: 1.0 References: <201803091530.w29FUKin041283@repo.freebsd.org> In-Reply-To: <201803091530.w29FUKin041283@repo.freebsd.org> From: Ilya Bakulin Date: Sat, 10 Mar 2018 06:49:33 +0000 Message-ID: Subject: Re: svn commit: r330696 - in head/libexec/tftpd: . tests To: Alan Somers Cc: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Content-Type: text/plain; charset="UTF-8" X-Content-Filtered-By: Mailman/MimeDel 2.1.25 X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.25 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 10 Mar 2018 06:49:47 -0000 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 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 > > 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 > + > +# 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 > > 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 > +__FBSDID("$FreeBSD$"); > + > +#include > +#include > +#include > +#include > + > +#include > + > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +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 \ > + > "AReallyBigFileNameXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + > "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ > + ".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 *** > >