From: Anoop C S Date: Thu, 23 Jul 2020 10:29:29 +0000 (+0530) Subject: tests: Add test for socket_wrapper fd-passing support X-Git-Tag: socket_wrapper-1.3.0~5 X-Git-Url: http://git.samba.org/?p=socket_wrapper.git;a=commitdiff_plain;h=66a5bef7f9aac4d70124bc8efec72dcd434c660a tests: Add test for socket_wrapper fd-passing support Signed-off-by: Anoop C S Reviewed-by: Stefan Metzmacher Reviewed-by: Andreas Schneider --- diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 72cda71..fce69e4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -61,7 +61,7 @@ set(SWRAP_TESTS ${SWRAP_THREADED_TESTS}) if (HAVE_STRUCT_MSGHDR_MSG_CONTROL) - set(SWRAP_TESTS ${SWRAP_TESTS} test_sendmsg_recvmsg_fd) + set(SWRAP_TESTS ${SWRAP_TESTS} test_sendmsg_recvmsg_fd test_echo_tcp_sendmsg_recvmsg_fd) endif (HAVE_STRUCT_MSGHDR_MSG_CONTROL) function(ADD_CMOCKA_TEST_ENVIRONMENT _TEST_NAME) @@ -116,6 +116,10 @@ foreach(_SWRAP_TEST ${SWRAP_TESTS}) add_cmocka_test_environment(${_SWRAP_TEST}) endforeach() +if (HAVE_STRUCT_MSGHDR_MSG_CONTROL) + set_tests_properties(test_echo_tcp_sendmsg_recvmsg_fd PROPERTIES WILL_FAIL TRUE) +endif (HAVE_STRUCT_MSGHDR_MSG_CONTROL) + if (HELGRIND_TESTING) find_program(VALGRIND_EXECUTABLE valgrind) if (VALGRIND_EXECUTABLE) diff --git a/tests/test_echo_tcp_sendmsg_recvmsg_fd.c b/tests/test_echo_tcp_sendmsg_recvmsg_fd.c new file mode 100644 index 0000000..8ae1a60 --- /dev/null +++ b/tests/test_echo_tcp_sendmsg_recvmsg_fd.c @@ -0,0 +1,188 @@ +#include +#include +#include +#include + +#include "config.h" +#include "torture.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int setup_echo_srv_tcp_ipv4(void **state) +{ + torture_setup_echo_srv_tcp_ipv4(state); + + return 0; +} + +static int teardown(void **state) +{ + torture_teardown_echo_srv(state); + + return 0; +} + +static void test_tcp_sendmsg_recvmsg_fd(void **state) +{ + struct torture_address addr = { + .sa_socklen = sizeof(struct sockaddr_in), + }; + int pass_sock_fd; + int sv[2]; + int child_fd, parent_fd; + pid_t pid; + int rc; + + (void) state; /* unused */ + + /* create socket file descriptor to be passed */ + pass_sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + assert_int_not_equal(pass_sock_fd, -1); + + addr.sa.in.sin_family = AF_INET; + addr.sa.in.sin_port = htons(torture_server_port()); + + rc = inet_pton(addr.sa.in.sin_family, + torture_server_address(AF_INET), + &addr.sa.in.sin_addr); + assert_int_equal(rc, 1); + + rc = connect(pass_sock_fd, &addr.sa.s, addr.sa_socklen); + assert_int_equal(rc, 0); + + /* create unix domain socket stream */ + rc = socketpair(AF_LOCAL, SOCK_STREAM, 0, sv); + assert_int_not_equal(rc, -1); + + parent_fd = sv[0]; + child_fd = sv[1]; + + pid = fork(); + assert_int_not_equal(pid, -1); + + if (pid == 0) { + /* Child */ + struct torture_address peer_addr = { + .sa_socklen = sizeof(struct sockaddr_in), + }; + struct msghdr child_msg; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + int rcv_sock_fd, port; + ssize_t ret; + char send_buf[64] = {0}; + char recv_buf[64] = {0}; + char ipstr[INET_ADDRSTRLEN]; + char byte = { 0, }; + struct iovec iov; + + iov.iov_base = &byte; + iov.iov_len = 1; + + memset(&child_msg, 0, sizeof(child_msg)); + child_msg.msg_iov = &iov; + child_msg.msg_iovlen = 1; + child_msg.msg_control = cmsgbuf; + child_msg.msg_controllen = sizeof(cmsgbuf); + + rc = recvmsg(child_fd, &child_msg, 0); + assert_int_equal(rc, iov.iov_len); + assert_int_equal(byte, '!'); + + cmsg = CMSG_FIRSTHDR(&child_msg); + assert_non_null(cmsg); + assert_int_equal(cmsg->cmsg_type, SCM_RIGHTS); + + memcpy(&rcv_sock_fd, CMSG_DATA(cmsg), sizeof(rcv_sock_fd)); + assert_int_not_equal(rcv_sock_fd, -1); + + /* extract peer info from received socket fd */ + ret = getpeername(rcv_sock_fd, &peer_addr.sa.s, &peer_addr.sa_socklen); + assert_int_not_equal(ret, -1); + + port = ntohs(peer_addr.sa.in.sin_port); + inet_ntop(AF_INET, &peer_addr.sa.in.sin_addr, ipstr, sizeof(ipstr)); + + /* check whether it is the same socket previously connected */ + assert_string_equal(ipstr, torture_server_address(AF_INET)); + assert_int_equal(port, torture_server_port()); + + snprintf(send_buf, sizeof(send_buf), "packet"); + + ret = write(rcv_sock_fd, + send_buf, + sizeof(send_buf)); + assert_int_not_equal(ret, -1); + + ret = read(rcv_sock_fd, + recv_buf, + sizeof(recv_buf)); + assert_int_not_equal(ret, -1); + + assert_memory_equal(send_buf, recv_buf, sizeof(send_buf)); + + exit(0); + } else { + /* Parent */ + struct msghdr parent_msg; + struct cmsghdr *cmsg; + char cmsgbuf[CMSG_SPACE(sizeof(pass_sock_fd))]; + char byte = '!'; + struct iovec iov; + int cs; + + (void) state; /* unused */ + + iov.iov_base = &byte; + iov.iov_len = 1; + + memset(&parent_msg, 0, sizeof(parent_msg)); + parent_msg.msg_iov = &iov; + parent_msg.msg_iovlen = 1; + parent_msg.msg_control = cmsgbuf; + parent_msg.msg_controllen = sizeof(cmsgbuf); + + cmsg = CMSG_FIRSTHDR(&parent_msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(pass_sock_fd)); + + /* place previously connected socket fd as ancillary data */ + memcpy(CMSG_DATA(cmsg), &pass_sock_fd, sizeof(pass_sock_fd)); + parent_msg.msg_controllen = cmsg->cmsg_len; + + rc = sendmsg(parent_fd, &parent_msg, 0); + assert_int_not_equal(rc, -1); + + alarm(5); /* 5 seconds timeout for the child */ + waitpid(pid, &cs, 0); + if (WIFEXITED(cs)) { + assert_int_equal(WEXITSTATUS(cs), 0); + } + } +} + +int main(void) { + int rc; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd, + setup_echo_srv_tcp_ipv4, + teardown) + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + + return rc; +}