Page MenuHomeFreeBSD

D14310.id39162.diff
No OneTemporary

D14310.id39162.diff

Index: etc/mtree/BSD.tests.dist
===================================================================
--- etc/mtree/BSD.tests.dist
+++ etc/mtree/BSD.tests.dist
@@ -382,6 +382,8 @@
..
rtld-elf
..
+ tftp
+ ..
..
sbin
dhclient
Index: libexec/tftpd/Makefile
===================================================================
--- libexec/tftpd/Makefile
+++ libexec/tftpd/Makefile
@@ -14,4 +14,7 @@
LIBADD= wrap
.endif
+HAS_TESTS=
+SUBDIR.${MK_TESTS}+= tests
+
.include <bsd.prog.mk>
Index: libexec/tftpd/tests/Makefile
===================================================================
--- /dev/null
+++ libexec/tftpd/tests/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+ATF_TESTS_C= functional
+TEST_METADATA.functional+= timeout=15
+
+LIBADD= util
+WARNS?= 6
+
+.include <bsd.test.mk>
Index: libexec/tftpd/tests/functional.c
===================================================================
--- /dev/null
+++ libexec/tftpd/tests/functional.c
@@ -0,0 +1,350 @@
+/*-
+ * 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/wait.h>
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static const uint16_t BASEPORT = 6969;
+static const char pidfile[] = "tftpd.pid";
+
+/* Helper functions*/
+
+static void
+cleanup()
+{
+ int fd = -1;
+ char buffer[80];
+ 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);
+}
+
+/* Assert that two binary buffers are identical */
+static void
+require_bufeq(char *expected, size_t expected_len, char *actual, size_t len)
+{
+ size_t i;
+
+ ATF_REQUIRE_EQ_MSG(expected_len, len,
+ "Expected %lu bytes but got %lu", expected_len, len);
+ for (i=0; i<len; i++) {
+ ATF_REQUIRE_EQ_MSG(actual[i], expected[i],
+ "Expected %#hhx at position %lu; 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_in *to, uint16_t idx)
+{
+ int client_s, server_s, pid;
+ char execname[] = "/usr/libexec/tftpd";
+ char pwd[MAXPATHLEN];
+ char *argv[4];
+ struct sockaddr_in addr;
+ struct pidfh* pfh;
+ uint16_t port = BASEPORT + idx;
+ socklen_t len;
+
+ len = sizeof(addr);
+ bzero(&addr, len);
+ addr.sin_len = len;
+ addr.sin_family = PF_INET;
+ addr.sin_port = htons(port);
+
+ 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(PF_INET, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE_EQ_MSG(bind(server_s, (struct sockaddr*) &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(pfh != NULL);
+ ATF_REQUIRE_EQ(pidfile_write(pfh), 0);
+ ATF_REQUIRE_EQ(pidfile_close(pfh), 0);
+
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ argv[0] = execname;
+ argv[1] = pwd;
+ argv[2] = NULL;
+ 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));
+ to->sin_len = sizeof(*to);
+ to->sin_family = PF_INET;
+ to->sin_port = htons(port);
+ to->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ close(server_s);
+ ATF_REQUIRE((client_s = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
+ break;
+ }
+ return (client_s);
+}
+
+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 void*)((const char*)buf + r);
+ }
+}
+
+
+/*
+ * Test Cases
+ */
+
+/*
+ * Read an empty file
+ */
+ATF_TC_WITH_CLEANUP(rrq_empty);
+ATF_TC_HEAD(rrq_empty, tc)
+{
+}
+ATF_TC_BODY(rrq_empty, tc)
+{
+ int fd, r, s;
+ char buffer[1024];
+ struct sockaddr_in addr, from;
+ socklen_t fromlen = sizeof(from);
+
+ s = setup(&addr, 0);
+
+ char cmd[] = {
+ 0, 1, /* RRQ opcode in BE */
+ 'e', 'm', 'p', 't', 'y', '.', 't', 'x', 't', 0,
+ 'o', 'c', 't', 'e', 't', 0
+ };
+ char expected[] = {
+ 0, 3, /* DATA opcode in BE */
+ 0, 1, /* Block 1 in BE */
+ /* No data, indicating a 0-length file */
+ };
+ char ack0[] = {
+ 0, 4, /* ACK opcode in BE */
+ 0, 1 /* Block 1 in BE */
+ };
+
+ fd = open("empty.txt", O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ ATF_REQUIRE_EQ(sendto(s, cmd, sizeof(cmd), 0,
+ (struct sockaddr*)&addr, sizeof(addr)), sizeof(cmd));
+ r = (size_t) recvfrom(s, buffer, sizeof(buffer), 0,
+ (struct sockaddr*)&from, &fromlen);
+ require_bufeq(expected, sizeof(expected), buffer, r);
+
+ addr.sin_port = from.sin_port;
+ ATF_REQUIRE_EQ(sendto(s, ack0, sizeof(ack0), 0,
+ (struct sockaddr*)&addr, sizeof(addr)), sizeof(ack0));
+}
+ATF_TC_CLEANUP(rrq_empty, tc)
+{
+ cleanup();
+}
+
+/*
+ * Read a small file of less than one block
+ */
+ATF_TC_WITH_CLEANUP(rrq_small);
+ATF_TC_HEAD(rrq_small, tc)
+{
+}
+ATF_TC_BODY(rrq_small, tc)
+{
+ int fd, r, s;
+ char buffer[1024];
+ char contents[] = "small";
+ struct sockaddr_in addr, from;
+ socklen_t fromlen = sizeof(from);
+
+ s = setup(&addr, 1);
+
+ char cmd[] = {
+ 0, 1, /* RRQ opcode in BE */
+ 's', 'm', 'a', 'l', 'l', '.', 't', 'x', 't', 0,
+ 'o', 'c', 't', 'e', 't', 0
+ };
+ char expected[] = {
+ 0, 3, /* DATA opcode in BE */
+ 0, 1, /* Block 1 in BE */
+ 's', 'm', 'a', 'l', 'l', 0, /* data */
+ };
+ char ack0[] = {
+ 0, 4, /* ACK opcode in BE */
+ 0, 1 /* Block 1 in BE */
+ };
+
+ fd = open("small.txt", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, strlen(contents) + 1);
+ close(fd);
+
+ ATF_REQUIRE_EQ(sendto(s, cmd, sizeof(cmd), 0,
+ (struct sockaddr*)&addr, sizeof(addr)), sizeof(cmd));
+
+ r = (size_t) recvfrom(s, buffer, sizeof(buffer), 0,
+ (struct sockaddr*)&from, &fromlen);
+ require_bufeq(expected, sizeof(expected), buffer, r);
+
+ addr.sin_port = from.sin_port;
+ ATF_REQUIRE_EQ(sendto(s, ack0, sizeof(ack0), 0,
+ (struct sockaddr*)&addr, sizeof(addr)), sizeof(ack0));
+}
+ATF_TC_CLEANUP(rrq_small, tc)
+{
+ cleanup();
+}
+
+/*
+ * Read a medium file of more than one block
+ */
+ATF_TC_WITH_CLEANUP(rrq_medium);
+ATF_TC_HEAD(rrq_medium, tc)
+{
+}
+ATF_TC_BODY(rrq_medium, tc)
+{
+ int fd, s;
+ size_t i, r;
+ char buffer[1024];
+ uint32_t contents[192];
+ struct sockaddr_in addr, from;
+ socklen_t fromlen = sizeof(from);
+
+ s = setup(&addr, 2);
+
+ char cmd[] = {
+ 0, 1, /* RRQ opcode in BE */
+ 'm', 'e', 'd', 'i', 'u', 'm', '.', 't', 'x', 't', 0,
+ 'o', 'c', 't', 'e', 't', 0
+ };
+ char expected_hdr0[] = {
+ 0, 3, /* DATA opcode in BE */
+ 0, 1, /* Block 1 in BE */
+ };
+ char ack0[] = {
+ 0, 4, /* ACK opcode in BE */
+ 0, 1 /* Block 1 in BE */
+ };
+ char expected_hdr1[] = {
+ 0, 3, /* DATA opcode in BE */
+ 0, 2, /* Block 2 in BE */
+ };
+
+ for (i=0; i<nitems(contents); i=i+1)
+ contents[i] = i;
+
+ fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, sizeof(contents));
+ close(fd);
+
+ ATF_REQUIRE_EQ(sendto(s, cmd, sizeof(cmd), 0,
+ (struct sockaddr*)&addr, sizeof(addr)), sizeof(cmd));
+
+ r = (size_t) recvfrom(s, buffer, sizeof(buffer), 0,
+ (struct sockaddr*)&from, &fromlen);
+ require_bufeq(expected_hdr0, sizeof(expected_hdr0), buffer,
+ MIN(r, sizeof(expected_hdr0)));
+ require_bufeq((char*) &contents[0], 512,
+ &buffer[sizeof(expected_hdr0)], r - sizeof(expected_hdr0));
+
+ addr.sin_port = from.sin_port;
+ ATF_REQUIRE_EQ(sendto(s, ack0, sizeof(ack0), 0,
+ (struct sockaddr*)&addr, sizeof(addr)), sizeof(ack0));
+
+ r = recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL);
+ require_bufeq(expected_hdr1, sizeof(expected_hdr1), buffer,
+ MIN(r, sizeof(expected_hdr1)));
+ require_bufeq((char*) &contents[128], 256,
+ &buffer[sizeof(expected_hdr1)], r - sizeof(expected_hdr1));
+}
+ATF_TC_CLEANUP(rrq_medium, tc)
+{
+ cleanup();
+}
+
+/*
+ * Main
+ */
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, rrq_empty);
+ ATF_TP_ADD_TC(tp, rrq_small);
+ ATF_TP_ADD_TC(tp, rrq_medium);
+
+ return (atf_no_error());
+}

File Metadata

Mime Type
text/plain
Expires
Sat, Jan 24, 2:33 PM (19 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27897724
Default Alt Text
D14310.id39162.diff (9 KB)

Event Timeline