#include #include #include #include #include "config.h" #include "torture.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_RPC_RPC_H #include #endif static int setup_echo_srv_tcp_ipv4(void **state) { torture_setup_echo_srv_tcp_ipv4(state); return 0; } #ifdef HAVE_IPV6 static int setup_echo_srv_tcp_ipv6(void **state) { torture_setup_echo_srv_tcp_ipv6(state); return 0; } #endif static int teardown(void **state) { torture_teardown_echo_srv(state); return 0; } static void test_bind_ipv4(void **state) { struct torture_address addr = { .sa_socklen = sizeof(struct sockaddr_storage), }; struct torture_address addr_in = { .sa_socklen = sizeof(struct sockaddr_in), }; struct torture_address addr_un = { .sa_socklen = sizeof(struct sockaddr_un), }; int rc; int s; (void) state; /* unused */ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); /* * Test various cases with family AF_UNSPEC */ /* UNSPEC, len == 1: EINVAL */ addr_in.sa.in = (struct sockaddr_in) { .sin_family = AF_UNSPEC, }; rc = bind(s, &addr.sa.s, 1); assert_int_equal(rc, -1); assert_int_equal(errno, EINVAL); /* UNSPEC: EAFNOSUPPORT */ addr_in.sa.in = (struct sockaddr_in) { .sin_family = AF_UNSPEC, }; rc = inet_pton(AF_INET, "127.0.0.20", &addr_in.sa.in.sin_addr); assert_int_equal(rc, 1); rc = bind(s, &addr_in.sa.s, addr_in.sa_socklen); assert_int_equal(rc, -1); /* FreeBSD uses EADDRNOTAVAIL here ... */ assert_true(errno == EAFNOSUPPORT || errno == EADDRNOTAVAIL); /* special case: AF_UNSPEC with INADDR_ANY: success mapped to AF_INET */ addr_in.sa.in = (struct sockaddr_in) { .sin_family = AF_UNSPEC, }; assert_int_equal(addr_in.sa.in.sin_addr.s_addr, htonl(INADDR_ANY)); rc = bind(s, &addr_in.sa.s, addr_in.sa_socklen); assert_return_code(rc, errno); close(s); s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); /* * Test various cases with family AF_UNIX * all fail with EAFNOSUPPORT */ addr.sa.ss = (struct sockaddr_storage) { .ss_family = AF_UNIX, }; rc = bind(s, &addr.sa.s, addr.sa_socklen); assert_int_equal(rc, -1); assert_int_equal(errno, EAFNOSUPPORT); addr_in.sa.in = (struct sockaddr_in) { .sin_family = AF_UNIX, }; rc = bind(s, &addr_in.sa.s, addr_in.sa_socklen); assert_int_equal(rc, -1); assert_int_equal(errno, EAFNOSUPPORT); addr_un.sa.un = (struct sockaddr_un) { .sun_family = AF_UNIX, }; rc = bind(s, &addr_un.sa.s, addr_un.sa_socklen); assert_int_equal(rc, -1); assert_int_equal(errno, EAFNOSUPPORT); #ifdef HAVE_IPV6 /* * Test with family AF_INET6 - fail */ addr_in.sa.in = (struct sockaddr_in) { .sin_family = AF_INET6, }; rc = bind(s, &addr_in.sa.s, addr_in.sa_socklen); assert_int_equal(rc, -1); assert_int_equal(errno, EAFNOSUPPORT); #endif /* * Finally, success binding a new IPv4 address. */ addr_in = (struct torture_address) { .sa_socklen = sizeof(struct sockaddr_in), .sa.in = (struct sockaddr_in) { .sin_family = AF_INET, }, }; rc = inet_pton(AF_INET, "127.0.0.20", &addr_in.sa.in.sin_addr); assert_int_equal(rc, 1); rc = bind(s, &addr_in.sa.s, addr_in.sa_socklen); assert_return_code(rc, errno); addr_in = (struct torture_address) { .sa_socklen = sizeof(struct sockaddr_in), .sa.in = (struct sockaddr_in) { .sin_family = AF_INET, .sin_port = htons(torture_server_port()), }, }; rc = inet_pton(AF_INET, torture_server_address(AF_INET), &addr_in.sa.in.sin_addr); assert_int_equal(rc, 1); rc = connect(s, &addr_in.sa.s, addr_in.sa_socklen); assert_return_code(rc, errno); close(s); } #if 0 /* TODO */ static void test_bind_ipv4_addr_in_use(void **state) { struct sockaddr_in sin, sin2; socklen_t slen = sizeof(struct sockaddr_in); int rc; int s, s2; (void) state; /* unused */ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); /* * Try to bind to the same address as already bound by a * different process. */ /* Without specifying the port - success */ ZERO_STRUCT(sin); sin.sin_family = AF_INET; rc = inet_pton(AF_INET, torture_server_address(AF_INET), &sin.sin_addr); assert_int_equal(rc, 1); rc = bind(s, (struct sockaddr *)&sin, slen); assert_return_code(rc, errno); close(s); s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); #if 0 /* specify the same port - fail with EADDRINUSE. */ /* Not supported by socket_wrapper yet. ==> TODO! */ ZERO_STRUCT(sin); sin.sin_family = AF_INET, sin.sin_port = htons(torture_server_port()); rc = inet_pton(AF_INET, torture_server_address(AF_INET), &sin.sin_addr); assert_int_equal(rc, 1); rc = bind(s, (struct sockaddr *)&sin, slen); assert_int_equal(rc, -1); assert_int_equal(errno, EADDRINUSE); #endif /* * Try double binding when the firs bind is with port == 0 */ ZERO_STRUCT(sin); sin.sin_family = AF_INET; rc = inet_pton(AF_INET, "127.0.0.20", &sin.sin_addr); assert_int_equal(rc, 1); rc = bind(s, (struct sockaddr *)&sin, slen); assert_return_code(rc, errno); /* * Open a second socket locally and try to bind to the same address. */ /* Succeeds with port == 0 */ s2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); ZERO_STRUCT(sin2); sin2.sin_family = AF_INET; rc = inet_pton(AF_INET, "127.0.0.20", &sin2.sin_addr); assert_int_equal(rc, 1); rc = bind(s2, (struct sockaddr *)&sin2, slen); assert_return_code(rc, errno); close(s2); /* second bind with port != 0 - succeeds */ s2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); ZERO_STRUCT(sin2); sin2.sin_family = AF_INET; sin2.sin_port = htons(12345); rc = inet_pton(AF_INET, "127.0.0.20", &sin2.sin_addr); assert_int_equal(rc, 1); rc = bind(s2, (struct sockaddr *)&sin2, slen); assert_return_code(rc, errno); close(s2); close(s); /* * Try double binding when the first bind is with port != 0 */ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); ZERO_STRUCT(sin); sin.sin_family = AF_INET; sin.sin_port = htons(12345); rc = inet_pton(AF_INET, "127.0.0.20", &sin.sin_addr); assert_int_equal(rc, 1); rc = bind(s, (struct sockaddr *)&sin, slen); assert_return_code(rc, errno); /* * Open a second socket locally and try to bind to the same address. */ /* Succeeds with port == 0 */ s2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); ZERO_STRUCT(sin2); sin2.sin_family = AF_INET; rc = inet_pton(AF_INET, "127.0.0.20", &sin2.sin_addr); assert_int_equal(rc, 1); rc = bind(s2, (struct sockaddr *)&sin2, slen); assert_return_code(rc, errno); close(s2); /* with same port as above - fail with EADDRINUSE */ s2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); ZERO_STRUCT(sin2); sin2.sin_family = AF_INET; sin2.sin_port = htons(12345); rc = inet_pton(AF_INET, "127.0.0.20", &sin2.sin_addr); assert_int_equal(rc, 1); rc = bind(s2, (struct sockaddr *)&sin2, slen); assert_int_equal(rc, -1); assert_int_equal(errno, EADDRINUSE); close(s); } #endif #ifdef HAVE_BINDRESVPORT static void test_bindresvport_ipv4(void **state) { struct torture_address addr = { .sa_socklen = sizeof(struct sockaddr_storage), }; int rc; int s; (void) state; /* unused */ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); addr.sa.in.sin_family = AF_INET; rc = inet_pton(AF_INET, "127.0.0.20", &addr.sa.in.sin_addr); assert_int_equal(rc, 1); rc = bindresvport(s, &addr.sa.in); assert_return_code(rc, errno); addr = (struct torture_address) { .sa_socklen = sizeof(struct sockaddr_storage), .sa.in = (struct sockaddr_in) { .sin_family = AF_INET, .sin_port = htons(torture_server_port()), }, }; rc = inet_pton(AF_INET, torture_server_address(AF_INET), &addr.sa.in.sin_addr); assert_int_equal(rc, 1); rc = connect(s, &addr.sa.s, addr.sa_socklen); assert_return_code(rc, errno); close(s); } static void test_bindresvport_ipv4_null(void **state) { struct torture_address addr = { .sa_socklen = sizeof(struct sockaddr_in), }; int rc; int s; (void) state; /* unused */ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); rc = bindresvport(s, NULL); assert_return_code(rc, errno); addr.sa.in.sin_family = AF_INET; addr.sa.in.sin_port = htons(torture_server_port()); rc = inet_pton(AF_INET, torture_server_address(AF_INET), &addr.sa.in.sin_addr); assert_int_equal(rc, 1); rc = connect(s, &addr.sa.s, addr.sa_socklen); assert_return_code(rc, errno); close(s); } #endif /* HAVE_BINDRESVPORT */ #ifdef HAVE_IPV6 static void test_bind_on_ipv6_sock(void **state) { struct torture_address addr_in = { .sa_socklen = sizeof(struct sockaddr_in), }; struct torture_address addr_un = { .sa_socklen = sizeof(struct sockaddr_un), }; int rc; int s; (void) state; /* unused */ s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); addr_un.sa.un.sun_family = AF_UNIX; rc = bind(s, &addr_un.sa.s, addr_un.sa_socklen); assert_int_equal(rc, -1); /* FreeBSD uses EINVAL here... */ assert_true(errno == EAFNOSUPPORT || errno == EINVAL); addr_in.sa.in.sin_family = AF_INET; rc = bind(s, &addr_in.sa.s, addr_in.sa_socklen); assert_int_equal(rc, -1); assert_int_equal(errno, EINVAL); addr_in.sa.in = (struct sockaddr_in) { .sin_family = AF_INET, }; rc = inet_pton(AF_INET, "127.0.0.20", &addr_in.sa.in.sin_addr); assert_int_equal(rc, 1); rc = bind(s, &addr_in.sa.s, addr_in.sa_socklen); assert_int_equal(rc, -1); assert_int_equal(errno, EINVAL); addr_in = (struct torture_address) { .sa_socklen = sizeof(struct sockaddr_in6), .sa.in = (struct sockaddr_in) { .sin_family = AF_INET, }, }; rc = bind(s, &addr_in.sa.s, addr_in.sa_socklen); assert_int_equal(rc, -1); assert_int_equal(errno, EAFNOSUPPORT); close(s); } #ifdef HAVE_BINDRESVPORT static void test_bindresvport_on_ipv6_sock(void **state) { struct sockaddr_in sin; int rc; int s; (void) state; /* unused */ s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); ZERO_STRUCT(sin); sin.sin_family = AF_INET; rc = inet_pton(AF_INET, "127.0.0.20", &sin.sin_addr); assert_int_equal(rc, 1); rc = bindresvport(s, &sin); assert_int_equal(rc, -1); assert_int_equal(errno, EINVAL); close(s); } static void test_bindresvport_on_ipv6_sock_null(void **state) { int rc; int s; (void) state; /* unused */ s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); assert_return_code(s, errno); rc = bindresvport(s, NULL); assert_return_code(rc, errno); close(s); } #endif /* HAVE_BINDRESVPORT */ #endif /* HAVE_IPV6 */ int main(void) { int rc; const struct CMUnitTest tcp_bind_tests[] = { cmocka_unit_test_setup_teardown(test_bind_ipv4, setup_echo_srv_tcp_ipv4, teardown), #if 0 /* TODO */ cmocka_unit_test_setup_teardown(test_bind_ipv4_addr_in_use, setup_echo_srv_tcp_ipv4, teardown), #endif #ifdef HAVE_BINDRESVPORT cmocka_unit_test_setup_teardown(test_bindresvport_ipv4, setup_echo_srv_tcp_ipv4, teardown), cmocka_unit_test_setup_teardown(test_bindresvport_ipv4_null, setup_echo_srv_tcp_ipv4, teardown), #endif /* HAVE_BINDRESVPORT */ #ifdef HAVE_IPV6 cmocka_unit_test_setup_teardown(test_bind_on_ipv6_sock, setup_echo_srv_tcp_ipv6, teardown), #ifdef HAVE_BINDRESVPORT cmocka_unit_test_setup_teardown(test_bindresvport_on_ipv6_sock, setup_echo_srv_tcp_ipv6, teardown), cmocka_unit_test_setup_teardown(test_bindresvport_on_ipv6_sock_null, setup_echo_srv_tcp_ipv6, teardown), #endif /* HAVE_BINDRESVPORT */ #endif /* HAVE_IPV6 */ }; rc = cmocka_run_group_tests(tcp_bind_tests, NULL, NULL); return rc; }