Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F142784757
D14310.id39162.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D14310.id39162.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D14310: Add some functional tests for tftpd(8)
Attached
Detach File
Event Timeline
Log In to Comment