/* * Copyright (c) 2016, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, * as published by the Free Software Foundation. * * This program is also distributed with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, * as designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an additional * permission to link the program and your derivative works with the * separately licensed software that they have included with MySQL. * * 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, version 2.0, for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include "mock/ngs_general.h" #include "io/xpl_listener_tcp.h" namespace xpl { namespace tests { using namespace ::testing; const std::string ADDRESS = "0.1.2.3"; const std::string ALL_INTERFACES_4 = "0.0.0.0"; const std::string ALL_INTERFACES_6 = "::"; const uint16 PORT = 3030; const std::string PORT_STRING = "3030"; const uint32 PORT_TIMEOUT = 123; const uint32 BACKLOG = 122; const my_socket SOCKET_OK = 10; const int POSIX_OK = 0; const int POSIX_FAILURE = -1; MATCHER(EqInvalidSocket, "") { return INVALID_SOCKET == mysql_socket_getfd(arg); } MATCHER_P(EqCastToCStr, expected, "") { std::string force_string = expected; return force_string == (char*)arg; } class Listener_tcp_testsuite : public Test { public: void SetUp() { KEY_socket_x_tcpip = 1; m_mock_factory = ngs::make_shared >(); m_mock_socket = ngs::make_shared >(); m_mock_system = ngs::make_shared >(); m_mock_socket_invalid = ngs::make_shared >(); ASSERT_NO_FATAL_FAILURE(assert_verify_and_reinitailize_rules()); } void assert_verify_and_reinitailize_rules() { ASSERT_TRUE(Mock::VerifyAndClearExpectations(m_mock_factory.get())); ASSERT_TRUE(Mock::VerifyAndClearExpectations(m_mock_socket_invalid.get())); ASSERT_TRUE(Mock::VerifyAndClearExpectations(m_mock_socket.get())); ASSERT_TRUE(Mock::VerifyAndClearExpectations(m_mock_system.get())); EXPECT_CALL(*m_mock_factory, create_system_interface()).WillRepeatedly(Return(m_mock_system)); EXPECT_CALL(*m_mock_factory, create_socket(EqInvalidSocket())).WillRepeatedly(Return(m_mock_socket_invalid)); EXPECT_CALL(*m_mock_socket_invalid, get_socket_fd()).WillRepeatedly(Return(INVALID_SOCKET)); EXPECT_CALL(*m_mock_socket, get_socket_fd()).WillRepeatedly(Return(SOCKET_OK)); } void make_sut(const std::string &interface, const uint32 port = PORT, const uint32 port_timeout = PORT_TIMEOUT) { m_resulting_bind_address = interface; sut = ngs::make_shared( m_mock_factory, ngs::ref(m_resulting_bind_address), port, port_timeout, ngs::ref(m_mock_socket_events), BACKLOG); } void expect_create_socket( addrinfo &ai, const std::string &interface, const int family, const int result = SOCKET_OK) { make_sut(interface, PORT, PORT_TIMEOUT); EXPECT_CALL(*m_mock_system, getaddrinfo( StrEq(interface), StrEq(PORT_STRING), _, _)).WillOnce(DoAll(SetArgPointee<3>(&ai),Return(POSIX_OK))); EXPECT_CALL(*m_mock_socket, get_socket_fd()) .WillOnce(Return(result)); EXPECT_CALL(*m_mock_factory, create_socket(KEY_socket_x_tcpip, family, SOCK_STREAM, 0)) .WillOnce(Return(m_mock_socket)); #ifdef IPV6_V6ONLY EXPECT_CALL(*m_mock_socket, set_socket_opt(IPPROTO_IPV6, IPV6_V6ONLY, _, sizeof(int))) .WillRepeatedly(Return(POSIX_OK)); #endif } void expect_listen_socket( ngs::shared_ptr mock_socket, addrinfo &ai, const bool socket_events_listen = true) { EXPECT_CALL(*mock_socket, set_socket_thread_owner()); EXPECT_CALL(*mock_socket, bind(ai.ai_addr, ai.ai_addrlen)) .WillOnce(Return(POSIX_OK)); EXPECT_CALL(*mock_socket, listen(BACKLOG)) .WillOnce(Return(POSIX_OK)); ngs::Socket_interface::Shared_ptr socket_ptr = mock_socket; EXPECT_CALL(m_mock_socket_events, listen(socket_ptr, _)) .WillOnce(Return(socket_events_listen)); } struct addrinfo get_ai_ipv6() { struct addrinfo result; static struct sockaddr_in6 in6; in6.sin6_family = result.ai_family = AF_INET6; result.ai_socktype = 0; result.ai_protocol = 0; result.ai_addrlen = sizeof(in6); result.ai_addr = (sockaddr*)&in6; result.ai_next = NULL; return result; } struct addrinfo get_ai_ipv4() { struct addrinfo result; static struct sockaddr_in in4; in4.sin_family = result.ai_family = AF_INET; result.ai_socktype = 0; result.ai_protocol = 0; result.ai_addrlen = sizeof(in4); result.ai_addr = (sockaddr*)&in4; result.ai_next = NULL; return result; } std::string m_resulting_bind_address; ngs::shared_ptr m_mock_socket; ngs::shared_ptr m_mock_socket_invalid; ngs::shared_ptr m_mock_system; StrictMock m_mock_socket_events; ngs::shared_ptr m_mock_factory; ngs::shared_ptr sut; }; TEST_F(Listener_tcp_testsuite, setup_listener_does_nothing_when_resolve_failes) { make_sut(ADDRESS); EXPECT_CALL(*m_mock_system, getaddrinfo( StrEq(ADDRESS), StrEq(PORT_STRING), _, _)).WillOnce(Return(POSIX_FAILURE)); ASSERT_FALSE(sut->setup_listener(ngs::Listener_interface::On_connection())); ASSERT_TRUE(sut->get_state().is(ngs::State_listener_initializing)); } TEST_F(Listener_tcp_testsuite, setup_listener_does_resolved_IP6_and_IP4_localhost_when_asterisk_and_IP6_supported) { make_sut("*"); EXPECT_CALL(*m_mock_socket, get_socket_fd()).WillOnce(Return(SOCKET_OK)); EXPECT_CALL(*m_mock_factory, create_socket(PSI_NOT_INSTRUMENTED, AF_INET6, SOCK_STREAM, 0)) .WillOnce(Return(m_mock_socket)); EXPECT_CALL(*m_mock_system, getaddrinfo( StrEq(ALL_INTERFACES_6), StrEq(PORT_STRING), _, _)).WillOnce(Return(POSIX_FAILURE)); EXPECT_CALL(*m_mock_system, getaddrinfo( StrEq(ALL_INTERFACES_4), StrEq(PORT_STRING), _, _)).WillOnce(Return(POSIX_FAILURE)); ASSERT_FALSE(sut->setup_listener(ngs::Listener_interface::On_connection())); ASSERT_TRUE(sut->get_state().is(ngs::State_listener_initializing)); } TEST_F(Listener_tcp_testsuite, setup_listener_does_resolved_IP4_localhost_when_asterisk_and_IP6_not_supported) { make_sut("*"); EXPECT_CALL(*m_mock_socket, get_socket_fd()).WillOnce(Return(INVALID_SOCKET)); EXPECT_CALL(*m_mock_factory, create_socket(PSI_NOT_INSTRUMENTED, AF_INET6, SOCK_STREAM, 0)) .WillOnce(Return(m_mock_socket)); EXPECT_CALL(*m_mock_system, getaddrinfo( StrEq(ALL_INTERFACES_4), StrEq(PORT_STRING), _, _)).WillOnce(Return(POSIX_FAILURE)); ASSERT_FALSE(sut->setup_listener(ngs::Listener_interface::On_connection())); ASSERT_TRUE(sut->get_state().is(ngs::State_listener_initializing)); } struct TimeOutAndExpectedRetries { TimeOutAndExpectedRetries(const uint32 timeout, const uint32 expected_retries) : m_timeout(timeout), m_expected_retries(expected_retries){ } uint32 m_timeout; uint32 m_expected_retries; }; class Listener_tcp_retry_testsuite: public Listener_tcp_testsuite, public WithParamInterface {}; TEST_P(Listener_tcp_retry_testsuite, setup_listener_retry_socket_allocation_when_it_is_in_use) { addrinfo ai = get_ai_ipv6(); make_sut(ALL_INTERFACES_6, PORT, GetParam().m_timeout); EXPECT_CALL(*m_mock_system, getaddrinfo( StrEq(ALL_INTERFACES_6), StrEq(PORT_STRING), _, _)).WillOnce(DoAll(SetArgPointee<3>(&ai),Return(POSIX_OK))); const int n = GetParam().m_expected_retries; EXPECT_CALL(*m_mock_socket, get_socket_fd()) .Times(n).WillRepeatedly(Return(INVALID_SOCKET)); EXPECT_CALL(*m_mock_factory, create_socket(KEY_socket_x_tcpip, AF_INET6, SOCK_STREAM, 0)) .Times(n).WillRepeatedly(Return(m_mock_socket)); EXPECT_CALL(*m_mock_system, get_socket_error_and_message(_,_)) .Times(n); EXPECT_CALL(*m_mock_system, get_socket_errno()) .Times(n).WillRepeatedly(Return(SOCKET_EADDRINUSE)); EXPECT_CALL(*m_mock_system, sleep(Gt(0))) .Times(n); EXPECT_CALL(*m_mock_system, freeaddrinfo(&ai)); ASSERT_FALSE(sut->setup_listener(ngs::Listener_interface::On_connection())); ASSERT_TRUE(sut->get_state().is(ngs::State_listener_initializing)); } INSTANTIATE_TEST_CASE_P(Instantiation_tcp_retry_when_already_in_use, Listener_tcp_retry_testsuite, Values(TimeOutAndExpectedRetries(0, 1), TimeOutAndExpectedRetries(1, 2), TimeOutAndExpectedRetries(5, 3), TimeOutAndExpectedRetries(6, 3), TimeOutAndExpectedRetries(7, 4), TimeOutAndExpectedRetries(PORT_TIMEOUT, 10))); //123, 10 TEST_F(Listener_tcp_testsuite, setup_listener_bind_failure) { addrinfo ai = get_ai_ipv6(); expect_create_socket( ai, ALL_INTERFACES_6, AF_INET6, SOCKET_OK); EXPECT_CALL(*m_mock_socket, set_socket_opt(SOL_SOCKET, SO_REUSEADDR, _, sizeof(int))) .WillOnce(Return(POSIX_OK)); EXPECT_CALL(*m_mock_socket, set_socket_thread_owner()); EXPECT_CALL(*m_mock_socket, bind(ai.ai_addr, ai.ai_addrlen)) .WillOnce(Return(POSIX_FAILURE)); EXPECT_CALL(*m_mock_system, get_socket_error_and_message(_,_)); EXPECT_CALL(*m_mock_system, get_socket_errno()) .WillRepeatedly(Return(SOCKET_ETIMEDOUT)); EXPECT_CALL(*m_mock_system, freeaddrinfo(&ai)); ASSERT_FALSE(sut->setup_listener(ngs::Listener_interface::On_connection())); ASSERT_TRUE(sut->get_state().is(ngs::State_listener_initializing)); } TEST_F(Listener_tcp_testsuite, setup_listener_listen_failure) { addrinfo ai = get_ai_ipv6(); expect_create_socket( ai, ALL_INTERFACES_6, AF_INET6, SOCKET_OK); EXPECT_CALL(*m_mock_socket, set_socket_opt(SOL_SOCKET, SO_REUSEADDR, _, sizeof(int))) .WillOnce(Return(POSIX_OK)); EXPECT_CALL(*m_mock_socket, set_socket_thread_owner()); EXPECT_CALL(*m_mock_socket, bind(ai.ai_addr, ai.ai_addrlen)) .WillOnce(Return(POSIX_OK)); EXPECT_CALL(*m_mock_socket, listen(BACKLOG)) .WillOnce(Return(POSIX_FAILURE)); EXPECT_CALL(*m_mock_system, get_socket_error_and_message(_,_)); EXPECT_CALL(*m_mock_system, get_socket_errno()) .WillRepeatedly(Return(SOCKET_ETIMEDOUT)); EXPECT_CALL(*m_mock_system, freeaddrinfo(&ai)); ASSERT_FALSE(sut->setup_listener(ngs::Listener_interface::On_connection())); ASSERT_TRUE(sut->get_state().is(ngs::State_listener_initializing)); } TEST_F(Listener_tcp_testsuite, setup_listener_ipv6_success) { addrinfo ai = get_ai_ipv6(); expect_create_socket( ai, ALL_INTERFACES_6, AF_INET6, SOCKET_OK); EXPECT_CALL(*m_mock_socket, set_socket_opt(SOL_SOCKET, SO_REUSEADDR, _, sizeof(int))) .WillOnce(Return(POSIX_OK)); expect_listen_socket(m_mock_socket, ai); EXPECT_CALL(*m_mock_system, freeaddrinfo(&ai)); ASSERT_TRUE(sut->setup_listener(ngs::Listener_interface::On_connection())); ASSERT_TRUE(sut->get_state().is(ngs::State_listener_prepared)); // SUT destructor ASSERT_NO_FATAL_FAILURE(assert_verify_and_reinitailize_rules()); EXPECT_CALL(*m_mock_socket, close()); } TEST_F(Listener_tcp_testsuite, setup_listener_ipv4_success) { addrinfo ai = get_ai_ipv4(); expect_create_socket( ai, ALL_INTERFACES_4, AF_INET, SOCKET_OK); EXPECT_CALL(*m_mock_socket, set_socket_opt(SOL_SOCKET, SO_REUSEADDR, _, sizeof(int))) .WillOnce(Return(POSIX_OK)); expect_listen_socket(m_mock_socket, ai); EXPECT_CALL(*m_mock_system, freeaddrinfo(&ai)); ASSERT_TRUE(sut->setup_listener(ngs::Listener_interface::On_connection())); ASSERT_TRUE(sut->get_state().is(ngs::State_listener_prepared)); // SUT destructor ASSERT_NO_FATAL_FAILURE(assert_verify_and_reinitailize_rules()); EXPECT_CALL(*m_mock_socket, close()); } TEST_F(Listener_tcp_testsuite, setup_listener_failure_when_socket_event_registry_failed) { addrinfo ai = get_ai_ipv4(); expect_create_socket( ai, ALL_INTERFACES_4, AF_INET, SOCKET_OK); EXPECT_CALL(*m_mock_socket, set_socket_opt(SOL_SOCKET, SO_REUSEADDR, _, sizeof(int))) .WillOnce(Return(POSIX_OK)); const bool socket_event_listen_failed = false; expect_listen_socket(m_mock_socket, ai, socket_event_listen_failed); EXPECT_CALL(*m_mock_system, freeaddrinfo(&ai)); ASSERT_FALSE(sut->setup_listener(ngs::Listener_interface::On_connection())); ASSERT_TRUE(sut->get_state().is(ngs::State_listener_initializing)); // SUT destructor ASSERT_NO_FATAL_FAILURE(assert_verify_and_reinitailize_rules()); } TEST_F(Listener_tcp_testsuite, setup_listener_ipv4_and_ip6_addresses_successful_is_ip4) { addrinfo ai4 = get_ai_ipv4(); addrinfo ai6 = get_ai_ipv6(); ai4.ai_next = &ai6; expect_create_socket( ai4, ALL_INTERFACES_4, AF_INET, SOCKET_OK); EXPECT_CALL(*m_mock_socket, set_socket_opt(SOL_SOCKET, SO_REUSEADDR, _, sizeof(int))) .WillOnce(Return(POSIX_OK)); expect_listen_socket(m_mock_socket, ai4); EXPECT_CALL(*m_mock_system, freeaddrinfo(&ai4)); ASSERT_TRUE(sut->setup_listener(ngs::Listener_interface::On_connection())); ASSERT_TRUE(sut->get_state().is(ngs::State_listener_prepared)); // SUT destructor ASSERT_NO_FATAL_FAILURE(assert_verify_and_reinitailize_rules()); EXPECT_CALL(*m_mock_socket, close()); } TEST_F(Listener_tcp_testsuite, setup_listener_ipv4_and_ip6_addresses_successful_is_ip4_beacause_it_is_always_first_to_try) { addrinfo ai4 = get_ai_ipv4(); addrinfo ai6 = get_ai_ipv6(); ai4.ai_next = &ai6; expect_create_socket( ai4, ALL_INTERFACES_6, AF_INET, SOCKET_OK); EXPECT_CALL(*m_mock_socket, set_socket_opt(SOL_SOCKET, SO_REUSEADDR, _, sizeof(int))) .WillOnce(Return(POSIX_OK)); expect_listen_socket(m_mock_socket, ai4); EXPECT_CALL(*m_mock_system, freeaddrinfo(&ai4)); ASSERT_TRUE(sut->setup_listener(ngs::Listener_interface::On_connection())); ASSERT_TRUE(sut->get_state().is(ngs::State_listener_prepared)); // SUT destructor ASSERT_NO_FATAL_FAILURE(assert_verify_and_reinitailize_rules()); EXPECT_CALL(*m_mock_socket, close()); } TEST_F(Listener_tcp_testsuite, setup_listener_ipv4_and_ip6_addresses_successful_is_ip6_at_retry) { addrinfo ai4 = get_ai_ipv4(); addrinfo ai6 = get_ai_ipv6(); ai4.ai_next = &ai6; expect_create_socket( ai4, ALL_INTERFACES_6, AF_INET, INVALID_SOCKET); ngs::shared_ptr mock_socket_ipv6( new StrictMock()); EXPECT_CALL(*mock_socket_ipv6, get_socket_fd()) .WillOnce(Return(SOCKET_OK)); EXPECT_CALL(*m_mock_factory, create_socket(KEY_socket_x_tcpip, AF_INET6, SOCK_STREAM, 0)) .WillOnce(Return(mock_socket_ipv6)); #ifdef IPV6_V6ONLY EXPECT_CALL(*mock_socket_ipv6, set_socket_opt(IPPROTO_IPV6, IPV6_V6ONLY, _, sizeof(int))) .WillRepeatedly(Return(POSIX_OK)); #endif EXPECT_CALL(*mock_socket_ipv6, set_socket_opt(SOL_SOCKET, SO_REUSEADDR, _, sizeof(int))) .WillOnce(Return(POSIX_OK)); expect_listen_socket(mock_socket_ipv6, ai6); EXPECT_CALL(*m_mock_system, freeaddrinfo(&ai4)); ASSERT_TRUE(sut->setup_listener(ngs::Listener_interface::On_connection())); ASSERT_TRUE(sut->get_state().is(ngs::State_listener_prepared)); // SUT destructor ASSERT_NO_FATAL_FAILURE(assert_verify_and_reinitailize_rules()); EXPECT_CALL(*mock_socket_ipv6, close()); } TEST_F(Listener_tcp_testsuite, setup_listener_success_evean_socket_opt_fails) { addrinfo ai = get_ai_ipv6(); expect_create_socket( ai, ALL_INTERFACES_6, AF_INET6, SOCKET_OK); EXPECT_CALL(*m_mock_socket, set_socket_opt(SOL_SOCKET, SO_REUSEADDR, _, sizeof(int))) .WillOnce(Return(POSIX_FAILURE)); EXPECT_CALL(*m_mock_system, get_socket_errno()); expect_listen_socket(m_mock_socket, ai); EXPECT_CALL(*m_mock_system, freeaddrinfo(&ai)); ASSERT_TRUE(sut->setup_listener(ngs::Listener_interface::On_connection())); ASSERT_TRUE(sut->get_state().is(ngs::State_listener_prepared)); // SUT destructor ASSERT_NO_FATAL_FAILURE(assert_verify_and_reinitailize_rules()); EXPECT_CALL(*m_mock_socket, close()); } TEST_F(Listener_tcp_testsuite, is_handled_by_socket_event_always_true) { make_sut(ALL_INTERFACES_6); ASSERT_TRUE(sut->is_handled_by_socket_event()); } TEST_F(Listener_tcp_testsuite, get_name_and_configuration) { make_sut(ALL_INTERFACES_6, 2222); ASSERT_STREQ("TCP (bind-address:'::', port:2222)", sut->get_name_and_configuration().c_str()); } TEST_F(Listener_tcp_testsuite, close_listener_does_nothing_when_socket_not_started) { make_sut(ALL_INTERFACES_6); sut->close_listener(); //After stopping, start must not work ! sut->setup_listener(ngs::Listener_interface::On_connection()); } TEST_F(Listener_tcp_testsuite, loop_does_nothing_always) { make_sut(ALL_INTERFACES_6); sut->loop(); } } // namespace tests } // namespace xpl