summaryrefslogtreecommitdiffstats
path: root/lib/tsocket/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /lib/tsocket/tests
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/tsocket/tests')
-rw-r--r--lib/tsocket/tests/socketpair_tcp.c89
-rw-r--r--lib/tsocket/tests/socketpair_tcp.h29
-rw-r--r--lib/tsocket/tests/test_bsd_addr.c369
-rw-r--r--lib/tsocket/tests/test_tstream.c517
4 files changed, 1004 insertions, 0 deletions
diff --git a/lib/tsocket/tests/socketpair_tcp.c b/lib/tsocket/tests/socketpair_tcp.c
new file mode 100644
index 0000000..251b8bc
--- /dev/null
+++ b/lib/tsocket/tests/socketpair_tcp.c
@@ -0,0 +1,89 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Tim Potter 2000-2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include "socketpair_tcp.h"
+
+/*******************************************************************
+this is like socketpair but uses tcp. It is used by the Samba
+regression test code
+The function guarantees that nobody else can attach to the socket,
+or if they do that this function fails and the socket gets closed
+returns 0 on success, -1 on failure
+the resulting file descriptors are symmetrical
+ ******************************************************************/
+int socketpair_tcp(int fd[2])
+{
+ int listener;
+ struct sockaddr_in sock;
+ struct sockaddr_in sock2;
+ socklen_t socklen = sizeof(sock);
+ int connect_done = 0;
+
+ fd[0] = fd[1] = listener = -1;
+
+ memset(&sock, 0, sizeof(sock));
+
+ if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
+
+ memset(&sock2, 0, sizeof(sock2));
+#ifdef HAVE_SOCK_SIN_LEN
+ sock2.sin_len = sizeof(sock2);
+#endif
+ sock2.sin_family = PF_INET;
+
+ if (bind(listener, (struct sockaddr *)&sock2, sizeof(sock2)) != 0) goto failed;
+
+ if (listen(listener, 1) != 0) goto failed;
+
+ if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0) goto failed;
+
+ if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
+
+ set_blocking(fd[1], 0);
+
+ sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (connect(fd[1], (struct sockaddr *)&sock, socklen) == -1) {
+ if (errno != EINPROGRESS) goto failed;
+ } else {
+ connect_done = 1;
+ }
+
+ if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1) goto failed;
+
+ if (connect_done == 0) {
+ if (connect(fd[1], (struct sockaddr *)&sock, socklen) != 0
+ && errno != EISCONN) goto failed;
+ }
+ close(listener);
+
+ set_blocking(fd[1], 1);
+
+ /* all OK! */
+ return 0;
+
+ failed:
+ if (fd[0] != -1) close(fd[0]);
+ if (fd[1] != -1) close(fd[1]);
+ if (listener != -1) close(listener);
+ return -1;
+}
diff --git a/lib/tsocket/tests/socketpair_tcp.h b/lib/tsocket/tests/socketpair_tcp.h
new file mode 100644
index 0000000..dbee4ef
--- /dev/null
+++ b/lib/tsocket/tests/socketpair_tcp.h
@@ -0,0 +1,29 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Tim Potter 2000-2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*******************************************************************
+this is like socketpair but uses tcp. It is used by the Samba
+regression test code
+The function guarantees that nobody else can attach to the socket,
+or if they do that this function fails and the socket gets closed
+returns 0 on success, -1 on failure
+the resulting file descriptors are symmetrical
+ ******************************************************************/
+int socketpair_tcp(int fd[2]);
diff --git a/lib/tsocket/tests/test_bsd_addr.c b/lib/tsocket/tests/test_bsd_addr.c
new file mode 100644
index 0000000..ac6e798
--- /dev/null
+++ b/lib/tsocket/tests/test_bsd_addr.c
@@ -0,0 +1,369 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2021 Uri Simchoni <uri@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <cmocka.h>
+#include <tsocket.h>
+
+static int setup(void **state)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ assert_non_null(mem_ctx);
+ *state = mem_ctx;
+
+ return 0;
+}
+
+static int teardown(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ TALLOC_FREE(mem_ctx);
+
+ return 0;
+}
+
+static void test_address_inet_from_strings(void **state)
+{
+ int rc = 0;
+ int save_errno;
+ TALLOC_CTX *mem_ctx = *state;
+ struct tsocket_address *addr = NULL;
+ char *addr_s = NULL;
+
+ /*
+ * Unspecified IP family, given an IPv4 address
+ */
+ rc = tsocket_address_inet_from_strings(mem_ctx, "ip", "1.2.3.4", 1234,
+ &addr);
+ assert_return_code(rc, errno);
+ assert_non_null(addr);
+ addr_s = tsocket_address_string(addr, mem_ctx);
+ assert_non_null(addr_s);
+ assert_string_equal(addr_s, "ipv4:1.2.3.4:1234");
+ assert_true(tsocket_address_is_inet(addr, "ip"));
+ assert_true(tsocket_address_is_inet(addr, "ipv4"));
+ assert_false(tsocket_address_is_inet(addr, "ipv6"));
+ assert_int_equal(tsocket_address_inet_port(addr), 1234);
+ TALLOC_FREE(addr);
+ TALLOC_FREE(addr_s);
+
+ /*
+ * Expecting IPv4, given an IPv4 address
+ */
+ rc = tsocket_address_inet_from_strings(mem_ctx, "ipv4", "1.2.3.4", 1234,
+ &addr);
+ assert_return_code(rc, errno);
+ assert_non_null(addr);
+ addr_s = tsocket_address_string(addr, mem_ctx);
+ assert_non_null(addr_s);
+ assert_string_equal(addr_s, "ipv4:1.2.3.4:1234");
+ assert_true(tsocket_address_is_inet(addr, "ip"));
+ assert_true(tsocket_address_is_inet(addr, "ipv4"));
+ assert_false(tsocket_address_is_inet(addr, "ipv6"));
+ assert_int_equal(tsocket_address_inet_port(addr), 1234);
+ TALLOC_FREE(addr);
+ TALLOC_FREE(addr_s);
+
+ /*
+ * Expecting IPv6, given an IPv4 address
+ */
+ errno = 0;
+ rc = tsocket_address_inet_from_strings(mem_ctx, "ipv6", "1.2.3.4", 1234,
+ &addr);
+ save_errno = errno;
+ assert_int_equal(rc, -1);
+ assert_int_not_equal(save_errno, 0);
+ assert_null(addr);
+
+ /*
+ * Unspecified IP family, given an IPv6 address
+ */
+ rc = tsocket_address_inet_from_strings(mem_ctx, "ip", "2001::1", 1234,
+ &addr);
+ assert_return_code(rc, errno);
+ assert_non_null(addr);
+ addr_s = tsocket_address_string(addr, mem_ctx);
+ assert_non_null(addr_s);
+ assert_string_equal(addr_s, "ipv6:2001::1:1234");
+ assert_true(tsocket_address_is_inet(addr, "ip"));
+ assert_false(tsocket_address_is_inet(addr, "ipv4"));
+ assert_true(tsocket_address_is_inet(addr, "ipv6"));
+ assert_int_equal(tsocket_address_inet_port(addr), 1234);
+ TALLOC_FREE(addr);
+ TALLOC_FREE(addr_s);
+
+ /*
+ * Expecting IPv4, given an IPv6 address
+ */
+ errno = 0;
+ rc = tsocket_address_inet_from_strings(mem_ctx, "ipv4", "2001::1", 1234,
+ &addr);
+ save_errno = errno;
+ assert_int_equal(rc, -1);
+ assert_int_not_equal(save_errno, 0);
+ assert_null(addr);
+
+ /*
+ * expecting IPv6, given an IPv6 address
+ */
+ rc = tsocket_address_inet_from_strings(mem_ctx, "ipv6", "2001::1", 1234,
+ &addr);
+ assert_return_code(rc, errno);
+ assert_non_null(addr);
+ addr_s = tsocket_address_string(addr, mem_ctx);
+ assert_non_null(addr_s);
+ assert_string_equal(addr_s, "ipv6:2001::1:1234");
+ assert_true(tsocket_address_is_inet(addr, "ip"));
+ assert_false(tsocket_address_is_inet(addr, "ipv4"));
+ assert_true(tsocket_address_is_inet(addr, "ipv6"));
+ assert_int_equal(tsocket_address_inet_port(addr), 1234);
+ TALLOC_FREE(addr);
+ TALLOC_FREE(addr_s);
+
+ /*
+ * Unspecified IP family, given an illegal address
+ */
+ errno = 0;
+ rc = tsocket_address_inet_from_strings(mem_ctx, "ip", "localhost", 1234,
+ &addr);
+ save_errno = errno;
+ assert_int_equal(rc, -1);
+ assert_int_not_equal(save_errno, 0);
+ assert_null(addr);
+
+ /*
+ * Illegal IP family
+ */
+ errno = 0;
+ rc = tsocket_address_inet_from_strings(mem_ctx, "ipx", "1.2.3.4", 1234,
+ &addr);
+ save_errno = errno;
+ assert_int_equal(rc, -1);
+ assert_int_not_equal(save_errno, 0);
+ assert_null(addr);
+
+ /*
+ * Unspecified IP family, given NULL, verify it returns something
+ */
+ rc = tsocket_address_inet_from_strings(mem_ctx, "ip", NULL, 1234,
+ &addr);
+ assert_return_code(rc, errno);
+ assert_non_null(addr);
+ TALLOC_FREE(addr);
+
+ /*
+ * IPv4, given NULL, verify it returns 0.0.0.0
+ */
+ rc = tsocket_address_inet_from_strings(mem_ctx, "ipv4", NULL, 1234,
+ &addr);
+ assert_return_code(rc, errno);
+ assert_non_null(addr);
+ addr_s = tsocket_address_string(addr, mem_ctx);
+ assert_non_null(addr_s);
+ assert_string_equal(addr_s, "ipv4:0.0.0.0:1234");
+ assert_true(tsocket_address_is_inet(addr, "ip"));
+ assert_true(tsocket_address_is_inet(addr, "ipv4"));
+ assert_false(tsocket_address_is_inet(addr, "ipv6"));
+ assert_int_equal(tsocket_address_inet_port(addr), 1234);
+ TALLOC_FREE(addr);
+ TALLOC_FREE(addr_s);
+
+ /*
+ * IPv6, given NULL, verify it returns ::
+ */
+ rc = tsocket_address_inet_from_strings(mem_ctx, "ipv6", NULL, 1234,
+ &addr);
+ assert_return_code(rc, errno);
+ assert_non_null(addr);
+ addr_s = tsocket_address_string(addr, mem_ctx);
+ assert_non_null(addr_s);
+ assert_string_equal(addr_s, "ipv6::::1234");
+ assert_true(tsocket_address_is_inet(addr, "ip"));
+ assert_false(tsocket_address_is_inet(addr, "ipv4"));
+ assert_true(tsocket_address_is_inet(addr, "ipv6"));
+ assert_int_equal(tsocket_address_inet_port(addr), 1234);
+ TALLOC_FREE(addr);
+ TALLOC_FREE(addr_s);
+}
+
+static void test_address_inet_from_hostport_strings(void **state)
+{
+ int rc = 0;
+ int save_errno;
+ TALLOC_CTX *mem_ctx = *state;
+ struct tsocket_address *addr = NULL;
+ char *addr_s = NULL;
+
+ /*
+ * IPv4 host:port
+ */
+ rc = tsocket_address_inet_from_hostport_strings(
+ mem_ctx, "ip", "1.2.3.4:5678", 1234, &addr);
+ assert_return_code(rc, errno);
+ assert_non_null(addr);
+ addr_s = tsocket_address_string(addr, mem_ctx);
+ assert_non_null(addr_s);
+ assert_string_equal(addr_s, "ipv4:1.2.3.4:5678");
+ TALLOC_FREE(addr);
+ TALLOC_FREE(addr_s);
+
+ /*
+ * IPv4 host
+ */
+ rc = tsocket_address_inet_from_hostport_strings(
+ mem_ctx, "ip", "1.2.3.4", 1234, &addr);
+ assert_return_code(rc, errno);
+ assert_non_null(addr);
+ addr_s = tsocket_address_string(addr, mem_ctx);
+ assert_non_null(addr_s);
+ assert_string_equal(addr_s, "ipv4:1.2.3.4:1234");
+ TALLOC_FREE(addr);
+ TALLOC_FREE(addr_s);
+
+ /*
+ * IPv6 [host]:port
+ */
+ rc = tsocket_address_inet_from_hostport_strings(
+ mem_ctx, "ip", "[2001::1]:5678", 1234, &addr);
+ assert_return_code(rc, errno);
+ assert_non_null(addr);
+ addr_s = tsocket_address_string(addr, mem_ctx);
+ assert_non_null(addr_s);
+ assert_string_equal(addr_s, "ipv6:2001::1:5678");
+ TALLOC_FREE(addr);
+ TALLOC_FREE(addr_s);
+
+ /*
+ * IPv6 [host]
+ */
+ rc = tsocket_address_inet_from_hostport_strings(
+ mem_ctx, "ip", "[2001::1]", 1234, &addr);
+ assert_return_code(rc, errno);
+ assert_non_null(addr);
+ addr_s = tsocket_address_string(addr, mem_ctx);
+ assert_non_null(addr_s);
+ assert_string_equal(addr_s, "ipv6:2001::1:1234");
+ TALLOC_FREE(addr);
+ TALLOC_FREE(addr_s);
+
+ /*
+ * IPv6 host
+ */
+ rc = tsocket_address_inet_from_hostport_strings(
+ mem_ctx, "ip", "2001::1", 1234, &addr);
+ assert_return_code(rc, errno);
+ assert_non_null(addr);
+ addr_s = tsocket_address_string(addr, mem_ctx);
+ assert_non_null(addr_s);
+ assert_string_equal(addr_s, "ipv6:2001::1:1234");
+ TALLOC_FREE(addr);
+ TALLOC_FREE(addr_s);
+
+ /*
+ * Given NULL, verify it returns something
+ */
+ rc = tsocket_address_inet_from_hostport_strings(
+ mem_ctx, "ipv6", NULL, 1234, &addr);
+ assert_return_code(rc, errno);
+ assert_non_null(addr);
+ addr_s = tsocket_address_string(addr, mem_ctx);
+ assert_non_null(addr_s);
+ assert_string_equal(addr_s, "ipv6::::1234");
+ TALLOC_FREE(addr);
+ TALLOC_FREE(addr_s);
+
+ /*
+ * [host]grarbage
+ */
+ errno = 0;
+ rc = tsocket_address_inet_from_hostport_strings(
+ mem_ctx, "ip", "[2001::1]garbage", 1234, &addr);
+ save_errno = errno;
+ assert_int_equal(rc, -1);
+ assert_int_not_equal(save_errno, 0);
+ assert_null(addr);
+
+ /*
+ * [host]:grarbage
+ */
+ errno = 0;
+ rc = tsocket_address_inet_from_hostport_strings(
+ mem_ctx, "ip", "[2001::1]:garbage", 1234, &addr);
+ save_errno = errno;
+ assert_int_equal(rc, -1);
+ assert_int_not_equal(save_errno, 0);
+ assert_null(addr);
+
+ /*
+ * host:grarbage
+ */
+ errno = 0;
+ rc = tsocket_address_inet_from_hostport_strings(
+ mem_ctx, "ip", "1.2.3.4:garbage", 1234, &addr);
+ save_errno = errno;
+ assert_int_equal(rc, -1);
+ assert_int_not_equal(save_errno, 0);
+ assert_null(addr);
+
+ /*
+ * [host]:<port-too-large>
+ */
+ errno = 0;
+ rc = tsocket_address_inet_from_hostport_strings(
+ mem_ctx, "ip", "[2001::1]:100000", 1234, &addr);
+ save_errno = errno;
+ assert_int_equal(rc, -1);
+ assert_int_not_equal(save_errno, 0);
+ assert_null(addr);
+
+ /*
+ * host:<port-too-large>
+ */
+ errno = 0;
+ rc = tsocket_address_inet_from_hostport_strings(
+ mem_ctx, "ip", "1.2.3.4:100000", 1234, &addr);
+ save_errno = errno;
+ assert_int_equal(rc, -1);
+ assert_int_not_equal(save_errno, 0);
+ assert_null(addr);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_address_inet_from_strings),
+ cmocka_unit_test(test_address_inet_from_hostport_strings),
+ };
+
+ if (argc == 2) {
+ cmocka_set_test_filter(argv[1]);
+ }
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ rc = cmocka_run_group_tests(tests, setup, teardown);
+
+ return rc;
+}
diff --git a/lib/tsocket/tests/test_tstream.c b/lib/tsocket/tests/test_tstream.c
new file mode 100644
index 0000000..47008bb
--- /dev/null
+++ b/lib/tsocket/tests/test_tstream.c
@@ -0,0 +1,517 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2022 Andrew Bartlett <abartlet@samba.org>
+ * Copyright (C) 2021 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include "includes.h"
+#include "system/network.h"
+#include "socketpair_tcp.h"
+#include "tsocket.h"
+
+enum socket_pair_selector {
+ SOCKET_SERVER = 0,
+ SOCKET_CLIENT = 1,
+};
+
+struct socket_pair {
+ struct tevent_context *ev;
+ int socket_server;
+ int socket_client;
+
+ /* for tstream tests */
+ int rc;
+ int sys_errno;
+ int expected_errno;
+ struct timeval endtime;
+ size_t max_loops;
+ size_t num_loops;
+};
+
+/* If this is too large, we get EPIPE rather than EAGAIN */
+static const uint8_t TEST_STRING[128] = { 0 };
+
+static int sigpipe_setup(void **state)
+{
+ BlockSignals(true, SIGPIPE);
+ return 0;
+}
+
+static int setup_socketpair_tcp_context(void **state)
+{
+ int fd[2];
+ struct socket_pair *sp = talloc_zero(NULL, struct socket_pair);
+ assert_non_null(sp);
+
+ /* Set up a socketpair over TCP to test with */
+ assert_return_code(socketpair_tcp(fd), errno);
+
+ sp->socket_server = fd[SOCKET_SERVER];
+ sp->socket_client = fd[SOCKET_CLIENT];
+
+ sp->ev = tevent_context_init(sp);
+ assert_non_null(sp->ev);
+
+ *state = sp;
+ return 0;
+}
+
+static int setup_socketpair_context(void **state)
+{
+ int fd[2];
+ struct socket_pair *sp = talloc_zero(NULL, struct socket_pair);
+ assert_non_null(sp);
+
+ /* Set up a socketpair over TCP to test with */
+ assert_return_code(socketpair(AF_UNIX, SOCK_STREAM, 0, fd), errno);
+
+ sp->socket_server = fd[SOCKET_SERVER];
+ sp->socket_client = fd[SOCKET_CLIENT];
+
+ sp->ev = tevent_context_init(sp);
+ assert_non_null(sp->ev);
+
+ *state = sp;
+ return 0;
+}
+
+static int teardown_socketpair_context(void **state)
+{
+ struct socket_pair *sp = *state;
+ struct socket_pair sp_save = *sp;
+
+ TALLOC_FREE(sp);
+
+ /*
+ * Close these after the TALLOC_FREE() to allow clean shutdown
+ * of epoll() in tstream
+ */
+ if (sp_save.socket_client != -1) {
+ close(sp_save.socket_client);
+ }
+ if (sp_save.socket_server != -1) {
+ close(sp_save.socket_server);
+ }
+ return 0;
+}
+
+
+/* Test socket behaviour */
+static void test_simple_socketpair(void **state) {
+
+ struct socket_pair *sp = *state;
+
+ char buf[sizeof(TEST_STRING)];
+
+ assert_int_equal(write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING)),
+ sizeof(TEST_STRING));
+ assert_int_equal(read(sp->socket_client, buf, sizeof(buf)),
+ sizeof(buf));
+
+
+}
+
+/* Test socket behaviour */
+static void test_read_client_after_close_server_socket(void **state) {
+
+ struct socket_pair *sp = *state;
+ int rc;
+ char buf[sizeof(TEST_STRING)];
+
+ rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
+ assert_return_code(rc, errno);
+ assert_int_equal(rc, sizeof(TEST_STRING));
+
+ assert_return_code(close(sp->socket_server), 0);
+
+ rc = read(sp->socket_client, buf, sizeof(buf));
+
+ assert_return_code(rc, errno);
+ assert_int_equal(rc, sizeof(buf));
+}
+
+static void test_write_server_after_close_client_socket(void **state) {
+
+ struct socket_pair *sp = *state;
+ int rc;
+
+ assert_return_code(close(sp->socket_client), 0);
+ sp->socket_client = -1;
+
+ rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
+ assert_return_code(rc, errno);
+ assert_int_equal(rc, sizeof(TEST_STRING));
+}
+
+static void test_fill_socket(int sock)
+{
+ size_t num_busy = 0;
+ int rc;
+
+ while (true) {
+ rc = write(sock, TEST_STRING, sizeof(TEST_STRING));
+ if (rc == -1 && errno == EAGAIN) {
+ /*
+ * This makes sure we write until we get a whole second
+ * only with EAGAIN every 50 ms (20 times)
+ *
+ * Otherwise the tests are not reliable...
+ */
+ num_busy++;
+ if (num_busy > 20) {
+ break;
+ }
+ smb_msleep(50);
+ continue;
+ }
+ /* try again next time */
+ num_busy = 0;
+ }
+
+ assert_int_equal(rc, -1);
+ assert_int_equal(errno, EAGAIN);
+}
+
+static void test_big_write_server(void **state) {
+
+ struct socket_pair *sp = *state;
+ int rc;
+
+ rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
+ assert_return_code(rc, errno);
+ assert_int_equal(rc, sizeof(TEST_STRING));
+
+ rc = set_blocking(sp->socket_server, 0);
+ assert_return_code(rc, errno);
+
+ test_fill_socket(sp->socket_server);
+}
+
+static void test_big_write_server_close_write(void **state) {
+
+ struct socket_pair *sp = *state;
+ int rc;
+
+ rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
+ assert_return_code(rc, errno);
+ assert_int_equal(rc, sizeof(TEST_STRING));
+
+ rc = set_blocking(sp->socket_server, 0);
+ assert_return_code(rc, errno);
+
+ test_fill_socket(sp->socket_server);
+
+ assert_return_code(close(sp->socket_client), 0);
+ sp->socket_client = -1;
+
+ rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
+ assert_int_equal(errno, ECONNRESET);
+
+}
+
+static void test_big_write_server_shutdown_wr_write(void **state) {
+
+ struct socket_pair *sp = *state;
+ int rc;
+
+ rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
+ assert_return_code(rc, errno);
+ assert_int_equal(rc, sizeof(TEST_STRING));
+
+ rc = set_blocking(sp->socket_server, 0);
+ assert_return_code(rc, errno);
+
+ test_fill_socket(sp->socket_server);
+
+ assert_return_code(shutdown(sp->socket_client, SHUT_WR), 0);
+ sp->socket_client = -1;
+
+ rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
+ assert_int_equal(rc, -1);
+ assert_int_equal(errno, EAGAIN);
+}
+
+static void test_big_write_server_shutdown_rd_write(void **state) {
+
+ struct socket_pair *sp = *state;
+ int rc;
+
+ rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
+ assert_return_code(rc, errno);
+ assert_int_equal(rc, sizeof(TEST_STRING));
+
+ rc = set_blocking(sp->socket_server, 0);
+ assert_return_code(rc, errno);
+
+ test_fill_socket(sp->socket_server);
+
+ assert_return_code(shutdown(sp->socket_client, SHUT_RD), 0);
+ sp->socket_client = -1;
+
+ rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
+ assert_int_equal(rc, -1);
+ assert_int_equal(errno, EAGAIN);
+}
+
+static void test_call_writev_done(struct tevent_req *subreq)
+{
+ struct socket_pair *sp =
+ tevent_req_callback_data(subreq,
+ struct socket_pair);
+ int rc;
+
+ rc = tstream_writev_recv(subreq, &sp->sys_errno);
+ TALLOC_FREE(subreq);
+
+ sp->rc = rc;
+}
+
+static void test_tstream_server_spin_client_shutdown(struct socket_pair *sp)
+{
+ int rc;
+
+ rc = shutdown(sp->socket_client, SHUT_WR);
+ assert_return_code(rc, errno);
+ /*
+ * It should only take a few additional loop to realise that this socket is
+ * in CLOSE_WAIT
+ */
+ sp->max_loops = sp->num_loops + 2;
+ sp->expected_errno = ECONNRESET;
+}
+
+static void test_tstream_server_spin_client_write(struct socket_pair *sp)
+{
+ int rc;
+ int timeout = 5000;
+
+ sp->endtime = timeval_current_ofs_msec(timeout);
+
+ rc = write(sp->socket_client, TEST_STRING, sizeof(TEST_STRING));
+ assert_return_code(rc, errno);
+ sp->expected_errno = ETIMEDOUT;
+}
+
+static void test_tstream_server_spin_client_tcp_user_timeout(struct socket_pair *sp)
+{
+ int rc;
+ int timeout = 5000;
+
+ rc = setsockopt(sp->socket_server, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout, sizeof(timeout));
+ assert_return_code(rc, errno);
+
+ rc = write(sp->socket_client, TEST_STRING, sizeof(TEST_STRING));
+ assert_return_code(rc, errno);
+ sp->expected_errno = ETIMEDOUT;
+ sp->max_loops = 30;
+}
+
+static void test_tstream_server_spin_client_both_timer(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *private_data)
+{
+ struct socket_pair *sp =
+ talloc_get_type_abort(private_data,
+ struct socket_pair);
+
+ test_tstream_server_spin_client_shutdown(sp);
+}
+
+static void test_tstream_server_spin_client_both(struct socket_pair *sp)
+{
+ struct tevent_timer *te = NULL;
+ struct timeval endtime;
+
+ test_tstream_server_spin_client_write(sp);
+
+ endtime = timeval_current_ofs_msec(2500);
+
+ te = tevent_add_timer(sp->ev,
+ sp,
+ endtime,
+ test_tstream_server_spin_client_both_timer,
+ sp);
+ assert_non_null(te);
+ sp->expected_errno = ENXIO;
+}
+
+static void test_tstream_server_spin(struct socket_pair *sp,
+ void (*client_fn)(struct socket_pair *sp))
+{
+ struct tstream_context *stream = NULL;
+ struct tevent_req *req = NULL;
+ struct iovec iov;
+ int rc;
+
+ rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
+ assert_return_code(rc, errno);
+ assert_int_equal(rc, sizeof(TEST_STRING));
+
+ rc = set_blocking(sp->socket_server, 0);
+ assert_return_code(rc, errno);
+
+ test_fill_socket(sp->socket_server);
+
+ /*
+ * by default we don't expect more then 2 loop iterations
+ * for a timeout of 5 seconds.
+ */
+ sp->max_loops = 10;
+
+ client_fn(sp);
+
+ rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
+ assert_int_equal(rc, -1);
+ assert_int_equal(errno, EAGAIN);
+
+ /* OK, so we now know the socket is in CLOSE_WAIT */
+
+ rc = tstream_bsd_existing_socket(sp->ev, sp->socket_server, &stream);
+ assert_return_code(rc, errno);
+ sp->socket_server = -1;
+
+ iov.iov_base = discard_const_p(char, TEST_STRING);
+ iov.iov_len = sizeof(TEST_STRING);
+
+ req = tstream_writev_send(stream, sp->ev, stream, &iov, 1);
+ assert_non_null(req);
+ if (!timeval_is_zero(&sp->endtime)) {
+ assert_true(tevent_req_set_endtime(req, sp->ev, sp->endtime));
+ }
+ tevent_req_set_callback(req, test_call_writev_done, sp);
+
+ while (tevent_req_is_in_progress(req)) {
+ if (sp->num_loops >= sp->max_loops) {
+ assert_int_not_equal(sp->num_loops, sp->max_loops);
+ assert_int_equal(sp->num_loops, sp->max_loops);
+ }
+ sp->num_loops += 1;
+
+ rc = tevent_loop_once(sp->ev);
+ assert_int_equal(rc, 0);
+ }
+
+ assert_int_equal(sp->rc, -1);
+ assert_int_equal(sp->sys_errno, sp->expected_errno);
+ return;
+}
+
+/*
+ * We need two names to run this with the two different setup
+ * routines
+ */
+static void test_tstream_disconnected_tcp_client_spin(void **state)
+{
+ struct socket_pair *sp = *state;
+ test_tstream_server_spin(sp, test_tstream_server_spin_client_shutdown);
+}
+
+static void test_tstream_disconnected_unix_client_spin(void **state)
+{
+ struct socket_pair *sp = *state;
+ test_tstream_server_spin(sp, test_tstream_server_spin_client_shutdown);
+}
+
+static void test_tstream_more_tcp_client_spin(void **state)
+{
+ struct socket_pair *sp = *state;
+ test_tstream_server_spin(sp, test_tstream_server_spin_client_write);
+}
+
+static void test_tstream_more_unix_client_spin(void **state)
+{
+ struct socket_pair *sp = *state;
+ test_tstream_server_spin(sp, test_tstream_server_spin_client_write);
+}
+
+static void test_tstream_more_disconnect_tcp_client_spin(void **state)
+{
+ struct socket_pair *sp = *state;
+ test_tstream_server_spin(sp, test_tstream_server_spin_client_both);
+}
+
+static void test_tstream_more_disconnect_unix_client_spin(void **state)
+{
+ struct socket_pair *sp = *state;
+ test_tstream_server_spin(sp, test_tstream_server_spin_client_both);
+}
+
+static void test_tstream_more_tcp_user_timeout_spin(void **state)
+{
+ struct socket_pair *sp = *state;
+ if (socket_wrapper_enabled()) {
+ skip();
+ }
+ test_tstream_server_spin(sp, test_tstream_server_spin_client_tcp_user_timeout);
+}
+
+int main(void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(test_simple_socketpair,
+ setup_socketpair_tcp_context,
+ teardown_socketpair_context),
+ cmocka_unit_test_setup_teardown(test_read_client_after_close_server_socket,
+ setup_socketpair_tcp_context,
+ teardown_socketpair_context),
+ cmocka_unit_test_setup_teardown(test_write_server_after_close_client_socket,
+ setup_socketpair_tcp_context,
+ teardown_socketpair_context),
+ cmocka_unit_test_setup_teardown(test_big_write_server,
+ setup_socketpair_tcp_context,
+ teardown_socketpair_context),
+ cmocka_unit_test_setup_teardown(test_big_write_server_close_write,
+ setup_socketpair_tcp_context,
+ teardown_socketpair_context),
+ cmocka_unit_test_setup_teardown(test_big_write_server_shutdown_wr_write,
+ setup_socketpair_tcp_context,
+ teardown_socketpair_context),
+ cmocka_unit_test_setup_teardown(test_big_write_server_shutdown_rd_write,
+ setup_socketpair_tcp_context,
+ teardown_socketpair_context),
+ cmocka_unit_test_setup_teardown(test_tstream_disconnected_tcp_client_spin,
+ setup_socketpair_tcp_context,
+ teardown_socketpair_context),
+ cmocka_unit_test_setup_teardown(test_tstream_disconnected_unix_client_spin,
+ setup_socketpair_context,
+ teardown_socketpair_context),
+ cmocka_unit_test_setup_teardown(test_tstream_more_tcp_client_spin,
+ setup_socketpair_tcp_context,
+ teardown_socketpair_context),
+ cmocka_unit_test_setup_teardown(test_tstream_more_unix_client_spin,
+ setup_socketpair_context,
+ teardown_socketpair_context),
+ cmocka_unit_test_setup_teardown(test_tstream_more_disconnect_tcp_client_spin,
+ setup_socketpair_tcp_context,
+ teardown_socketpair_context),
+ cmocka_unit_test_setup_teardown(test_tstream_more_disconnect_unix_client_spin,
+ setup_socketpair_context,
+ teardown_socketpair_context),
+ cmocka_unit_test_setup_teardown(test_tstream_more_tcp_user_timeout_spin,
+ setup_socketpair_tcp_context,
+ teardown_socketpair_context),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ return cmocka_run_group_tests(tests, sigpipe_setup, NULL);
+}