diff options
Diffstat (limited to 'test/malicious_data_tests')
4 files changed, 1350 insertions, 20 deletions
diff --git a/test/malicious_data_tests/conf/malicious_data_test_master_starter.sh.in b/test/malicious_data_tests/conf/malicious_data_test_master_starter.sh.in index 199f318..5072632 100755 --- a/test/malicious_data_tests/conf/malicious_data_test_master_starter.sh.in +++ b/test/malicious_data_tests/conf/malicious_data_test_master_starter.sh.in @@ -12,12 +12,22 @@ FAIL=0 +if [ $# -lt 1 ] +then + echo "Please pass a test mode to this script." + echo "For example: $0 MALICIOUS_EVENTS" + echo "Valid subscription types include:" + echo " [MALICIOUS_EVENTS, PROTOCOL_VERSION, MESSAGE_TYPE, RETURN_CODE, WRONG_HEADER_FIELDS_UDP]" + exit 1 +fi +TESTMODE=$1 + export VSOMEIP_CONFIGURATION=malicious_data_test_master.json # start daemon ../daemon/./vsomeipd & PID_VSOMEIPD=$! # Start the services -./malicious_data_test_service & +./malicious_data_test_service $TESTMODE & PID_SERIVCE=$! sleep 1 @@ -26,16 +36,16 @@ if [ ! -z "$USE_LXC_TEST" ]; then echo "Waiting for 5s" sleep 5 echo "starting offer test on slave LXC offer_test_external_slave_starter.sh" - ssh -tt -i $SANDBOX_ROOT_DIR/commonapi_main/lxc-config/.ssh/mgc_lxc/rsa_key_file.pub -o StrictHostKeyChecking=no root@$LXC_TEST_SLAVE_IP "bash -ci \"set -m; cd \\\$SANDBOX_TARGET_DIR/vsomeip/test; ./malicious_data_test_msg_sender @TEST_IP_MASTER@ @TEST_IP_SLAVE@\"" & + ssh -tt -i $SANDBOX_ROOT_DIR/commonapi_main/lxc-config/.ssh/mgc_lxc/rsa_key_file.pub -o StrictHostKeyChecking=no root@$LXC_TEST_SLAVE_IP "bash -ci \"set -m; cd \\\$SANDBOX_TARGET_DIR/vsomeip/test; ./malicious_data_test_msg_sender @TEST_IP_MASTER@ @TEST_IP_SLAVE@ $TESTMODE\"" & echo "remote ssh pid: $!" elif [ ! -z "$USE_DOCKER" ]; then - docker run --name otems --cap-add NET_ADMIN $DOCKER_IMAGE sh -c "route add -net 224.0.0.0/4 dev eth0 && cd $DOCKER_TESTS && sleep 10; ./malicious_data_test_msg_sender @TEST_IP_MASTER@ @TEST_IP_SLAVE@" & + docker run --name otems --cap-add NET_ADMIN $DOCKER_IMAGE sh -c "route add -net 224.0.0.0/4 dev eth0 && cd $DOCKER_TESTS && sleep 10; ./malicious_data_test_msg_sender @TEST_IP_MASTER@ @TEST_IP_SLAVE@ $TESTMODE" & else cat <<End-of-message ******************************************************************************* ******************************************************************************* ** Please now run: -** malicious_data_test_msg_sender @TEST_IP_MASTER@ @TEST_IP_SLAVE@ +** malicious_data_test_msg_sender @TEST_IP_MASTER@ @TEST_IP_SLAVE@ $TESTMODE ** from an external host to successfully complete this test. ** ** You probably will need to adapt the 'unicast' settings in diff --git a/test/malicious_data_tests/malicious_data_test_globals.hpp b/test/malicious_data_tests/malicious_data_test_globals.hpp index f6336a4..de14d5e 100644 --- a/test/malicious_data_tests/malicious_data_test_globals.hpp +++ b/test/malicious_data_tests/malicious_data_test_globals.hpp @@ -21,10 +21,11 @@ struct service_info { struct service_info service = { 0x3344, 0x1, 0x1111, 0x8002, 0x1, 0x1404, 0x4242 }; enum test_mode_e { - SUBSCRIBE, - SUBSCRIBE_UNSUBSCRIBE, - UNSUBSCRIBE, - SUBSCRIBE_UNSUBSCRIBE_NACK + MALICIOUS_EVENTS, + PROTOCOL_VERSION, + MESSAGE_TYPE, + RETURN_CODE, + WRONG_HEADER_FIELDS_UDP }; } diff --git a/test/malicious_data_tests/malicious_data_test_msg_sender.cpp b/test/malicious_data_tests/malicious_data_test_msg_sender.cpp index 27a62c2..84d09bf 100644 --- a/test/malicious_data_tests/malicious_data_test_msg_sender.cpp +++ b/test/malicious_data_tests/malicious_data_test_msg_sender.cpp @@ -23,7 +23,10 @@ #include "../../implementation/service_discovery/include/constants.hpp" #include "../../implementation/service_discovery/include/enumeration_types.hpp" #include "../../implementation/service_discovery/include/eventgroupentry_impl.hpp" +#include "../../implementation/service_discovery/include/serviceentry_impl.hpp" #include "../../implementation/message/include/message_impl.hpp" +#include "../../implementation/service_discovery/include/option_impl.hpp" +#include "../../implementation/service_discovery/include/ipv4_option_impl.hpp" #include "malicious_data_test_globals.hpp" static char* remote_address; @@ -258,31 +261,1330 @@ TEST_F(malicious_data, send_malicious_events) 30001); udp_socket.send_to(boost::asio::buffer(shutdown_call), target_service); } catch (const std::exception& _e) { - std::cout << "catched exception: " << _e.what(); - ASSERT_FALSE(true); + ADD_FAILURE() << "catched exception: " << _e.what(); } }); send_thread.join(); receive_thread.join(); - udp_socket.shutdown(boost::asio::socket_base::shutdown_both); - tcp_socket.shutdown(boost::asio::socket_base::shutdown_both); - udp_socket.close(); - tcp_socket.close(); + boost::system::error_code ec; + udp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket.close(ec); + tcp_socket.close(ec); } +/* + * @test Send a message with an invalid protocol version to the test master + * two times as client and two time as service. Ensure that message with the + * WRONG_PROTOCOL_VERSION is sent back in both cases and that the client + * reestablishes the TCP connection after the service sent an message with an + * invalid protocol version back + */ +TEST_F(malicious_data, send_wrong_protocol_version) +{ + std::promise<void> remote_client_subscribed; + std::promise<void> offer_received; + + boost::asio::ip::tcp::socket tcp_socket(io_); + boost::asio::ip::udp::socket udp_socket(io_, + boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); + + std::thread sd_receive_thread([&](){ + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + std::vector<vsomeip::event_t> its_received_events; + std::atomic<bool> service_offered(false); + std::atomic<bool> client_subscribed(false); + + // join the sd multicast group 224.0.24.1 + udp_socket.set_option(boost::asio::ip::multicast::join_group( + boost::asio::ip::address::from_string("224.0.24.1").to_v4())); + + while (keep_receiving) { + boost::system::error_code error; + std::size_t bytes_transferred = udp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (error) { + keep_receiving = false; + ADD_FAILURE() << __func__ << " error: " << error.message(); + return; + } else { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + vsomeip::service_t its_service = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_SERVICE_POS_MIN], + receive_buffer[VSOMEIP_SERVICE_POS_MAX]); + vsomeip::method_t its_method = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_METHOD_POS_MIN], + receive_buffer[VSOMEIP_METHOD_POS_MAX]); + if (its_service == vsomeip::sd::service && its_method == vsomeip::sd::method) { + vsomeip::sd::message_impl sd_msg; + EXPECT_TRUE(sd_msg.deserialize(&its_deserializer)); + EXPECT_EQ(1u, sd_msg.get_entries().size()); + for (auto e : sd_msg.get_entries()) { + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP && !client_subscribed) { + EXPECT_TRUE(e->is_eventgroup_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP, e->get_type()); + EXPECT_EQ(1,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(1u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP) { + std::shared_ptr<vsomeip::sd::eventgroupentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::eventgroupentry_impl>(e); + EXPECT_EQ(1u, its_casted_entry->get_eventgroup()); + } + remote_client_subscribed.set_value(); + client_subscribed = true; + } else if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE && !service_offered) { + EXPECT_TRUE(e->is_service_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::OFFER_SERVICE, e->get_type()); + EXPECT_EQ(2,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id + 1u, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(2u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE) { + std::shared_ptr<vsomeip::sd::serviceentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::serviceentry_impl>(e); + EXPECT_EQ(0u, its_casted_entry->get_minor_version()); + } + offer_received.set_value(); + service_offered = true; + } + } + if (service_offered && client_subscribed) { + keep_receiving = false; + } + } else { + ADD_FAILURE() << " received non-sd message"; + } + } + } + }); + + std::thread send_thread([&]() { + try { + std::promise<void> client_connected; + boost::asio::ip::tcp::socket::endpoint_type local( + boost::asio::ip::address::from_string(std::string(local_address)), + 40001); + boost::asio::ip::tcp::acceptor its_acceptor(io_); + boost::system::error_code ec; + its_acceptor.open(local.protocol(), ec); + boost::asio::detail::throw_error(ec, "acceptor open"); + its_acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + boost::asio::detail::throw_error(ec, "acceptor set_option"); + its_acceptor.bind(local, ec); + boost::asio::detail::throw_error(ec, "acceptor bind"); + its_acceptor.listen(boost::asio::socket_base::max_connections, ec); + boost::asio::detail::throw_error(ec, "acceptor listen"); + its_acceptor.async_accept(tcp_socket, [&](boost::system::error_code _error) { + if (!_error) { + // Nagle algorithm off + tcp_socket.set_option(boost::asio::ip::tcp::no_delay(true)); + client_connected.set_value(); + } else { + ADD_FAILURE() << "accept_cbk: " << _error.message(); + } + }); + + + // offer the service + std::uint8_t its_offer_service_message[] = { + 0xff, 0xff, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x30, // length + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x02, 0x00, + 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, // length entries array + 0x01, 0x00, 0x00, 0x20, + 0x33, 0x44, 0x00, 0x01, // service / instance + 0x00, 0xff, 0xff, 0xff, // major / ttl + 0x00, 0x00, 0x00, 0x00, // minor + 0x00, 0x00, 0x00, 0x0c, // length options array + 0x00, 0x09, 0x04, 0x00, + 0xff, 0xff, 0xff, 0xff, // slave address + 0x00, 0x06, 0x9c, 0x41, + }; + boost::asio::ip::address its_local_address = + boost::asio::ip::address::from_string(std::string(local_address)); + std::memcpy(&its_offer_service_message[48], &its_local_address.to_v4().to_bytes()[0], 4); + + boost::asio::ip::udp::socket::endpoint_type target_sd( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30490); + udp_socket.send_to(boost::asio::buffer(its_offer_service_message), target_sd); + + // wait until client established TCP connection + if (std::future_status::timeout == client_connected.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't connect within time"; + } + + // wait until client subscribed + if (std::future_status::timeout == remote_client_subscribed.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't subscribe within time"; + } + + // wait until a offer was received + if (std::future_status::timeout == offer_received.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Didn't receive offer within time"; + } + + std::atomic<std::uint32_t> fin_as_service_received(0); + std::promise<void> client_reconnected1; + + std::thread tcp_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + + while (keep_receiving) { + boost::system::error_code error; + tcp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (error == boost::asio::error::eof) { + EXPECT_EQ(boost::asio::error::eof, error); + fin_as_service_received++; + keep_receiving = false; + } else { + keep_receiving = false; + ADD_FAILURE() << __func__ << ":" << __LINE__ << " error: " << error.message(); + return; + } + } + }); + + boost::asio::ip::tcp::socket tcp_socket3(io_); + its_acceptor.async_accept(tcp_socket3, [&](boost::system::error_code _error) { + if (!_error) { + // Nagle algorithm off + tcp_socket3.set_option(boost::asio::ip::tcp::no_delay(true)); + client_reconnected1.set_value(); + } else { + ADD_FAILURE() << "accept_cbk2: " << _error.message(); + } + }); + // send malicious data as server (too long length and wrong protocol version) + std::uint8_t its_malicious_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x07, 0x7f, + 0xBB, 0xBB, 0xCA, 0xFE, + 0xAA, 0x00, 0x00, 0x00 // protocol version set to 0xAA + }; + tcp_socket.send(boost::asio::buffer(its_malicious_data)); + + // wait until client reestablished TCP connection + if (std::future_status::timeout == client_reconnected1.get_future().wait_for(std::chrono::seconds(10))) { + tcp_socket3.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket3.close(ec); + ADD_FAILURE() << "Client didn't reconnect within time 1"; + } + + tcp_receive_thread.join(); + + EXPECT_EQ(1u, fin_as_service_received); + + std::thread tcp_receive_thread2([&]() { + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + while (keep_receiving) { + boost::system::error_code error; + tcp_socket3.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (error == boost::asio::error::eof) { + EXPECT_EQ(boost::asio::error::eof, error); + fin_as_service_received++; + // client must sent back error response before closing the connection + EXPECT_EQ(2u, fin_as_service_received); + if (fin_as_service_received == 2) { + keep_receiving = false; + } + } else { + keep_receiving = false; + return; + } + } + }); + + // send malicious data as server (wrong protocol version) + std::uint8_t its_malicious_data_correct_length[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xBB, 0xBB, 0xCA, 0xFE, + 0xAA, 0x00, 0x00, 0x00 // protocol version set to 0xAA + }; + tcp_socket3.send(boost::asio::buffer(its_malicious_data_correct_length)); + + tcp_socket3.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket3.close(ec); + + + tcp_receive_thread2.join(); + EXPECT_EQ(2u, fin_as_service_received); + + // establish second tcp connection as client and send malicious data as well + std::atomic<std::uint32_t> error_response_as_client_received(0); + std::atomic<std::uint32_t> fin_as_client_received(0); + + boost::asio::ip::tcp::socket tcp_socket2(io_); + boost::asio::ip::tcp::socket::endpoint_type remote( + boost::asio::ip::address::from_string(std::string(remote_address)), + 40001); + tcp_socket2.open(remote.protocol()); + tcp_socket2.connect(remote); + + + std::thread tcp_service_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + while (keep_receiving) { + boost::system::error_code error; + std::size_t bytes_transferred = tcp_socket2.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (!error) { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + std::shared_ptr<vsomeip::message> its_message(its_deserializer.deserialize_message()); + EXPECT_EQ(0x3345, its_message->get_service()); + EXPECT_EQ(0x1, its_message->get_method()); + EXPECT_EQ(0xCCCC, its_message->get_client()); + EXPECT_EQ(0xDDDD, its_message->get_session()); + EXPECT_EQ(vsomeip::return_code_e::E_WRONG_PROTOCOL_VERSION, its_message->get_return_code()); + EXPECT_EQ(vsomeip::message_type_e::MT_ERROR, its_message->get_message_type()); + error_response_as_client_received++; + // service must sent back error response before closing the connection + EXPECT_EQ(error_response_as_client_received - 1u, fin_as_client_received); + } else { + EXPECT_EQ(boost::asio::error::eof, error); + fin_as_client_received++; + // service must sent back error response before closing the connection + EXPECT_EQ(error_response_as_client_received, fin_as_client_received); + if (fin_as_client_received == 1) { + keep_receiving = false; + } + } + } + }); + + // send malicious data as client (too long length and wrong protocol version) + std::uint8_t its_malicious_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x07, 0x7f, + 0xCC, 0xCC, 0xDD, 0xDD, + 0xAA, 0x00, 0x00, 0x00 // protocol version set to 0xAA + }; + tcp_socket2.send(boost::asio::buffer(its_malicious_client_data)); + tcp_service_receive_thread.join(); + EXPECT_EQ(1u, error_response_as_client_received); + EXPECT_EQ(1u, fin_as_client_received); + + tcp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket2.close(ec); + + // establish a tcp connection as client again and send wrong protocol + // version in message with a correct length field + boost::asio::ip::tcp::socket tcp_socket5(io_); + tcp_socket5.open(remote.protocol()); + tcp_socket5.connect(remote); + + std::thread tcp_service_receive_thread2([&]() { + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + while (keep_receiving) { + boost::system::error_code error; + std::size_t bytes_transferred = tcp_socket5.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (!error) { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + std::shared_ptr<vsomeip::message> its_message(its_deserializer.deserialize_message()); + EXPECT_EQ(0x3345, its_message->get_service()); + EXPECT_EQ(0x1, its_message->get_method()); + EXPECT_EQ(0xCCCC, its_message->get_client()); + EXPECT_EQ(0xDDDD, its_message->get_session()); + EXPECT_EQ(vsomeip::return_code_e::E_WRONG_PROTOCOL_VERSION, its_message->get_return_code()); + EXPECT_EQ(vsomeip::message_type_e::MT_ERROR, its_message->get_message_type()); + error_response_as_client_received++; + // service must sent back error response before closing the connection + EXPECT_EQ(error_response_as_client_received - 1u, fin_as_client_received); + if (error_response_as_client_received == 2) { + keep_receiving = false; + } + } + } + }); + + // send malicious data as client (wrong protocol version) + std::uint8_t its_malicious_client_data_correct_length[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xCC, 0xCC, 0xDD, 0xDD, + 0xAA, 0x00, 0x00, 0x00 // protocol version set to 0xAA + }; + tcp_socket5.send(boost::asio::buffer(its_malicious_client_data_correct_length)); + tcp_service_receive_thread2.join(); + EXPECT_EQ(2u, error_response_as_client_received); + EXPECT_EQ(1u, fin_as_client_received); + + tcp_socket5.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket5.close(ec); + + // call shutdown method + std::uint8_t shutdown_call[] = { + 0x33, 0x45, 0x14, 0x04, + 0x00, 0x00, 0x00, 0x08, + 0x22, 0x22, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00 }; + boost::asio::ip::udp::socket::endpoint_type target_service( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30001); + boost::asio::ip::udp::socket udp_socket2(io_, boost::asio::ip::udp::v4()); + udp_socket2.send_to(boost::asio::buffer(shutdown_call), target_service); + udp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket2.close(ec); + } catch (const std::exception& _e) { + ADD_FAILURE() << "catched exception: " << _e.what(); + } + + }); + + send_thread.join(); + sd_receive_thread.join(); + boost::system::error_code ec; + udp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket.close(ec); + tcp_socket.close(ec); + +} + +/* + * @test Send a message with an invalid message type to the test master + * one time as client and one time as service. Ensure that the client + * reestablishes the TCP connection after the service sent an message with an + * invalid protocol version back + */ +TEST_F(malicious_data, send_wrong_message_type) +{ + std::promise<void> remote_client_subscribed; + std::promise<void> offer_received; + + boost::asio::ip::tcp::socket tcp_socket(io_); + boost::asio::ip::udp::socket udp_socket(io_, + boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); + + std::thread sd_receive_thread([&](){ + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + std::vector<vsomeip::event_t> its_received_events; + std::atomic<bool> service_offered(false); + std::atomic<bool> client_subscribed(false); + + // join the sd multicast group 224.0.24.1 + udp_socket.set_option(boost::asio::ip::multicast::join_group( + boost::asio::ip::address::from_string("224.0.24.1").to_v4())); + + while (keep_receiving) { + boost::system::error_code error; + std::size_t bytes_transferred = udp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (error) { + keep_receiving = false; + ADD_FAILURE() << __func__ << " error: " << error.message(); + return; + } else { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + vsomeip::service_t its_service = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_SERVICE_POS_MIN], + receive_buffer[VSOMEIP_SERVICE_POS_MAX]); + vsomeip::method_t its_method = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_METHOD_POS_MIN], + receive_buffer[VSOMEIP_METHOD_POS_MAX]); + if (its_service == vsomeip::sd::service && its_method == vsomeip::sd::method) { + vsomeip::sd::message_impl sd_msg; + EXPECT_TRUE(sd_msg.deserialize(&its_deserializer)); + EXPECT_EQ(1u, sd_msg.get_entries().size()); + for (auto e : sd_msg.get_entries()) { + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP && !client_subscribed) { + EXPECT_TRUE(e->is_eventgroup_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP, e->get_type()); + EXPECT_EQ(1,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(1u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP) { + std::shared_ptr<vsomeip::sd::eventgroupentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::eventgroupentry_impl>(e); + EXPECT_EQ(1u, its_casted_entry->get_eventgroup()); + } + remote_client_subscribed.set_value(); + client_subscribed = true; + } else if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE && !service_offered) { + EXPECT_TRUE(e->is_service_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::OFFER_SERVICE, e->get_type()); + EXPECT_EQ(2,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id + 1u, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(2u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE) { + std::shared_ptr<vsomeip::sd::serviceentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::serviceentry_impl>(e); + EXPECT_EQ(0u, its_casted_entry->get_minor_version()); + } + offer_received.set_value(); + service_offered = true; + } + } + if (service_offered && client_subscribed) { + keep_receiving = false; + } + } else { + ADD_FAILURE() << " received non-sd message"; + } + } + } + }); + + std::thread send_thread([&]() { + try { + std::promise<void> client_connected; + boost::asio::ip::tcp::socket::endpoint_type local( + boost::asio::ip::address::from_string(std::string(local_address)), + 40001); + boost::asio::ip::tcp::acceptor its_acceptor(io_); + boost::system::error_code ec; + its_acceptor.open(local.protocol(), ec); + boost::asio::detail::throw_error(ec, "acceptor open"); + its_acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + boost::asio::detail::throw_error(ec, "acceptor set_option"); + its_acceptor.bind(local, ec); + boost::asio::detail::throw_error(ec, "acceptor bind"); + its_acceptor.listen(boost::asio::socket_base::max_connections, ec); + boost::asio::detail::throw_error(ec, "acceptor listen"); + its_acceptor.async_accept(tcp_socket, [&](boost::system::error_code _error) { + if (!_error) { + // Nagle algorithm off + tcp_socket.set_option(boost::asio::ip::tcp::no_delay(true)); + client_connected.set_value(); + } else { + ADD_FAILURE() << "accept_cbk: " << _error.message(); + } + }); + + + // offer the service + std::uint8_t its_offer_service_message[] = { + 0xff, 0xff, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x30, // length + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x02, 0x00, + 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, // length entries array + 0x01, 0x00, 0x00, 0x20, + 0x33, 0x44, 0x00, 0x01, // service / instance + 0x00, 0xff, 0xff, 0xff, // major / ttl + 0x00, 0x00, 0x00, 0x00, // minor + 0x00, 0x00, 0x00, 0x0c, // length options array + 0x00, 0x09, 0x04, 0x00, + 0xff, 0xff, 0xff, 0xff, // slave address + 0x00, 0x06, 0x9c, 0x41, + }; + boost::asio::ip::address its_local_address = + boost::asio::ip::address::from_string(std::string(local_address)); + std::memcpy(&its_offer_service_message[48], &its_local_address.to_v4().to_bytes()[0], 4); + + boost::asio::ip::udp::socket::endpoint_type target_sd( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30490); + udp_socket.send_to(boost::asio::buffer(its_offer_service_message), target_sd); + + // wait until client established TCP connection + if (std::future_status::timeout == client_connected.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't connect within time"; + } + + // wait until client subscribed + if (std::future_status::timeout == remote_client_subscribed.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't subscribe within time"; + } + + // wait until a offer was received + if (std::future_status::timeout == offer_received.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Didn't receive offer within time"; + } + + std::atomic<bool> fin_as_service_received(false); + std::promise<void> client_reconnected; + + std::thread tcp_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + + while (keep_receiving) { + boost::system::error_code error; + tcp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (!error) { + ADD_FAILURE() << __func__ << ":" << __LINE__ + << " received a non-error:" << error.message(); + } else if (error == boost::asio::error::eof) { + EXPECT_EQ(boost::asio::error::eof, error); + fin_as_service_received = true; + keep_receiving = false; + } else { + keep_receiving = false; + ADD_FAILURE() << __func__ << ":" << __LINE__ << " error: " << error.message(); + return; + } + } + }); + + boost::asio::ip::tcp::socket tcp_socket3(io_); + its_acceptor.async_accept(tcp_socket3, [&](boost::system::error_code _error) { + if (!_error) { + // Nagle algorithm off + tcp_socket.set_option(boost::asio::ip::tcp::no_delay(true)); + client_reconnected.set_value(); + } else { + ADD_FAILURE() << "accept_cbk2: " << _error.message(); + } + }); + // send malicious data as server (too long length and wrong message type) + std::uint8_t its_malicious_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x07, 0x7f, + 0xBB, 0xBB, 0xCA, 0xFE, + 0x01, 0x00, 0xAA, 0x00 // message type set to 0xAA + }; + tcp_socket.send(boost::asio::buffer(its_malicious_data)); + + // wait until client reestablished TCP connection + if (std::future_status::timeout == client_reconnected.get_future().wait_for(std::chrono::seconds(10))) { + tcp_socket3.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket3.close(ec); + ADD_FAILURE() << "Client didn't reconnect within time"; + } else { + tcp_socket3.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket3.close(ec); + } + + tcp_receive_thread.join(); + + EXPECT_TRUE(fin_as_service_received); + + + // establish second tcp connection as client and send malicious data as well + std::atomic<bool> fin_as_client_received(false); + + boost::asio::ip::tcp::socket tcp_socket2(io_); + boost::asio::ip::tcp::socket::endpoint_type remote( + boost::asio::ip::address::from_string(std::string(remote_address)), + 40001); + tcp_socket2.open(remote.protocol()); + tcp_socket2.connect(remote); + + + std::thread tcp_service_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::function<void()> receive; + std::vector<std::uint8_t> receive_buffer(4096); + + auto receive_cbk = [&](const boost::system::error_code& _error, + std::size_t bytes_transferred) { + (void)bytes_transferred; + if (!_error) { + ADD_FAILURE() << __func__ << ":" << __LINE__ + << " received a non-error:" << _error.message(); + } else { + EXPECT_EQ(boost::asio::error::eof, _error); + fin_as_client_received = true; + keep_receiving = false; + + } + }; + + receive = [&]() { + tcp_socket2.async_receive(boost::asio::buffer(receive_buffer, receive_buffer.capacity()), + receive_cbk); + }; + + while (keep_receiving) { + receive(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }); + + // send malicious data as client (too long length and wrong message type) + std::uint8_t its_malicious_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x07, 0x7f, + 0xCC, 0xCC, 0xDD, 0xDD, + 0x01, 0x00, 0xAA, 0x00 // protocol version set to 0xAA + }; + tcp_socket2.send(boost::asio::buffer(its_malicious_client_data)); + tcp_service_receive_thread.join(); + EXPECT_TRUE(fin_as_client_received); + + tcp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket2.close(ec); + + // call shutdown method + std::uint8_t shutdown_call[] = { + 0x33, 0x45, 0x14, 0x04, + 0x00, 0x00, 0x00, 0x08, + 0x22, 0x22, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00 }; + boost::asio::ip::udp::socket::endpoint_type target_service( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30001); + boost::asio::ip::udp::socket udp_socket2(io_, boost::asio::ip::udp::v4()); + udp_socket2.send_to(boost::asio::buffer(shutdown_call), target_service); + udp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket2.close(ec); + } catch (const std::exception& _e) { + ADD_FAILURE() << "catched exception: " << _e.what(); + } + + }); + + send_thread.join(); + sd_receive_thread.join(); + boost::system::error_code ec; + udp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket.close(ec); + tcp_socket.close(ec); + +} + +/* + * @test Send a message with an invalid return code to the test master + * one time as client and one time as service. Ensure that the client + * reestablishes the TCP connection after the service sent an message with an + * invalid protocol version back + */ +TEST_F(malicious_data, send_wrong_return_code) +{ + std::promise<void> remote_client_subscribed; + std::promise<void> offer_received; + + boost::asio::ip::tcp::socket tcp_socket(io_); + boost::asio::ip::udp::socket udp_socket(io_, + boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); + + std::thread sd_receive_thread([&](){ + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + std::vector<vsomeip::event_t> its_received_events; + std::atomic<bool> service_offered(false); + std::atomic<bool> client_subscribed(false); + + // join the sd multicast group 224.0.24.1 + udp_socket.set_option(boost::asio::ip::multicast::join_group( + boost::asio::ip::address::from_string("224.0.24.1").to_v4())); + + while (keep_receiving) { + boost::system::error_code error; + std::size_t bytes_transferred = udp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (error) { + keep_receiving = false; + ADD_FAILURE() << __func__ << " error: " << error.message(); + return; + } else { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + vsomeip::service_t its_service = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_SERVICE_POS_MIN], + receive_buffer[VSOMEIP_SERVICE_POS_MAX]); + vsomeip::method_t its_method = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_METHOD_POS_MIN], + receive_buffer[VSOMEIP_METHOD_POS_MAX]); + if (its_service == vsomeip::sd::service && its_method == vsomeip::sd::method) { + vsomeip::sd::message_impl sd_msg; + EXPECT_TRUE(sd_msg.deserialize(&its_deserializer)); + EXPECT_EQ(1u, sd_msg.get_entries().size()); + for (auto e : sd_msg.get_entries()) { + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP && !client_subscribed) { + EXPECT_TRUE(e->is_eventgroup_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP, e->get_type()); + EXPECT_EQ(1,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(1u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP) { + std::shared_ptr<vsomeip::sd::eventgroupentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::eventgroupentry_impl>(e); + EXPECT_EQ(1u, its_casted_entry->get_eventgroup()); + } + remote_client_subscribed.set_value(); + client_subscribed = true; + } else if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE && !service_offered) { + EXPECT_TRUE(e->is_service_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::OFFER_SERVICE, e->get_type()); + EXPECT_EQ(2,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id + 1u, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(2u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE) { + std::shared_ptr<vsomeip::sd::serviceentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::serviceentry_impl>(e); + EXPECT_EQ(0u, its_casted_entry->get_minor_version()); + } + offer_received.set_value(); + service_offered = true; + } + } + if (service_offered && client_subscribed) { + keep_receiving = false; + } + } else { + ADD_FAILURE() << " received non-sd message"; + } + } + } + }); + + std::thread send_thread([&]() { + try { + std::promise<void> client_connected; + boost::asio::ip::tcp::socket::endpoint_type local( + boost::asio::ip::address::from_string(std::string(local_address)), + 40001); + boost::asio::ip::tcp::acceptor its_acceptor(io_); + boost::system::error_code ec; + its_acceptor.open(local.protocol(), ec); + boost::asio::detail::throw_error(ec, "acceptor open"); + its_acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + boost::asio::detail::throw_error(ec, "acceptor set_option"); + its_acceptor.bind(local, ec); + boost::asio::detail::throw_error(ec, "acceptor bind"); + its_acceptor.listen(boost::asio::socket_base::max_connections, ec); + boost::asio::detail::throw_error(ec, "acceptor listen"); + its_acceptor.async_accept(tcp_socket, [&](boost::system::error_code _error) { + if (!_error) { + // Nagle algorithm off + tcp_socket.set_option(boost::asio::ip::tcp::no_delay(true)); + client_connected.set_value(); + } else { + ADD_FAILURE() << "accept_cbk: " << _error.message(); + } + }); + + + // offer the service + std::uint8_t its_offer_service_message[] = { + 0xff, 0xff, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x30, // length + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x02, 0x00, + 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, // length entries array + 0x01, 0x00, 0x00, 0x20, + 0x33, 0x44, 0x00, 0x01, // service / instance + 0x00, 0xff, 0xff, 0xff, // major / ttl + 0x00, 0x00, 0x00, 0x00, // minor + 0x00, 0x00, 0x00, 0x0c, // length options array + 0x00, 0x09, 0x04, 0x00, + 0xff, 0xff, 0xff, 0xff, // slave address + 0x00, 0x06, 0x9c, 0x41, + }; + boost::asio::ip::address its_local_address = + boost::asio::ip::address::from_string(std::string(local_address)); + std::memcpy(&its_offer_service_message[48], &its_local_address.to_v4().to_bytes()[0], 4); + + boost::asio::ip::udp::socket::endpoint_type target_sd( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30490); + udp_socket.send_to(boost::asio::buffer(its_offer_service_message), target_sd); + + // wait until client established TCP connection + if (std::future_status::timeout == client_connected.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't connect within time"; + } + + // wait until client subscribed + if (std::future_status::timeout == remote_client_subscribed.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't subscribe within time"; + } + + // wait until a offer was received + if (std::future_status::timeout == offer_received.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Didn't receive offer within time"; + } + + std::atomic<bool> fin_as_service_received(false); + std::promise<void> client_reconnected; + + std::thread tcp_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + + while (keep_receiving) { + boost::system::error_code error; + tcp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (!error) { + ADD_FAILURE() << __func__ << ":" << __LINE__ + << " received a non-error:" << error.message(); + } else if (error == boost::asio::error::eof) { + EXPECT_EQ(boost::asio::error::eof, error); + fin_as_service_received = true; + keep_receiving = false; + } else { + keep_receiving = false; + ADD_FAILURE() << __func__ << ":" << __LINE__ << " error: " << error.message(); + return; + } + } + }); + + boost::asio::ip::tcp::socket tcp_socket3(io_); + its_acceptor.async_accept(tcp_socket3, [&](boost::system::error_code _error) { + if (!_error) { + // Nagle algorithm off + tcp_socket.set_option(boost::asio::ip::tcp::no_delay(true)); + client_reconnected.set_value(); + } else { + ADD_FAILURE() << "accept_cbk2: " << _error.message(); + } + }); + // send malicious data as server (too long length and wrong return code) + std::uint8_t its_malicious_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x07, 0x7f, + 0xBB, 0xBB, 0xCA, 0xFE, + 0x01, 0x00, 0x00, 0xAA // return code set to 0xAA + }; + tcp_socket.send(boost::asio::buffer(its_malicious_data)); + + // wait until client reestablished TCP connection + if (std::future_status::timeout == client_reconnected.get_future().wait_for(std::chrono::seconds(10))) { + tcp_socket3.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket3.close(ec); + ADD_FAILURE() << "Client didn't reconnect within time"; + } else { + tcp_socket3.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket3.close(ec); + } + + tcp_receive_thread.join(); + + EXPECT_TRUE(fin_as_service_received); + + + // establish second tcp connection as client and send malicious data as well + std::atomic<bool> fin_as_client_received(false); + + boost::asio::ip::tcp::socket tcp_socket2(io_); + boost::asio::ip::tcp::socket::endpoint_type remote( + boost::asio::ip::address::from_string(std::string(remote_address)), + 40001); + tcp_socket2.open(remote.protocol()); + tcp_socket2.connect(remote); + + + std::thread tcp_service_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::function<void()> receive; + std::vector<std::uint8_t> receive_buffer(4096); + + auto receive_cbk = [&](const boost::system::error_code& _error, + std::size_t bytes_transferred) { + (void)bytes_transferred; + if (!_error) { + ADD_FAILURE() << __func__ << ":" << __LINE__ + << " received a non-error:" << _error.message(); + } else { + EXPECT_EQ(boost::asio::error::eof, _error); + fin_as_client_received = true; + keep_receiving = false; + + } + }; + + receive = [&]() { + tcp_socket2.async_receive(boost::asio::buffer(receive_buffer, receive_buffer.capacity()), + receive_cbk); + }; + + while (keep_receiving) { + receive(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }); + + // send malicious data as client (too long length and wrong return code) + std::uint8_t its_malicious_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x07, 0x7f, + 0xCC, 0xCC, 0xDD, 0xDD, + 0x01, 0x00, 0x00, 0xAA // return version set to 0xAA + }; + tcp_socket2.send(boost::asio::buffer(its_malicious_client_data)); + tcp_service_receive_thread.join(); + EXPECT_TRUE(fin_as_client_received); + + tcp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket2.close(ec); + + // call shutdown method + std::uint8_t shutdown_call[] = { + 0x33, 0x45, 0x14, 0x04, + 0x00, 0x00, 0x00, 0x08, + 0x22, 0x22, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00 }; + boost::asio::ip::udp::socket::endpoint_type target_service( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30001); + boost::asio::ip::udp::socket udp_socket2(io_, boost::asio::ip::udp::v4()); + udp_socket2.send_to(boost::asio::buffer(shutdown_call), target_service); + udp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket2.close(ec); + } catch (const std::exception& _e) { + ADD_FAILURE() << "catched exception: " << _e.what(); + } + + }); + + send_thread.join(); + sd_receive_thread.join(); + boost::system::error_code ec; + udp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket.close(ec); + tcp_socket.close(ec); + +} + +/* + * @test Send a message with an invalid protocol version, invalid message type + * and invalid return code via UDP to the test master + * one time as client and one time as service. Ensure that message with the + * WRONG_PROTOCOL_VERSION is sent back in both cases + */ +TEST_F(malicious_data, wrong_header_fields_udp) +{ + std::promise<void> remote_client_subscribed; + std::promise<void> offer_received; + + boost::asio::ip::udp::socket udp_socket(io_, + boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); + boost::asio::ip::udp::socket udp_socket_service(io_, + boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 40001)); + + boost::asio::ip::udp::endpoint udp_client_info; + boost::asio::ip::udp::endpoint udp_service_info; + + + std::thread sd_receive_thread([&](){ + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + std::vector<vsomeip::event_t> its_received_events; + std::atomic<bool> service_offered(false); + std::atomic<bool> client_subscribed(false); + + // join the sd multicast group 224.0.24.1 + udp_socket.set_option(boost::asio::ip::multicast::join_group( + boost::asio::ip::address::from_string("224.0.24.1").to_v4())); + + while (keep_receiving) { + boost::system::error_code error; + std::size_t bytes_transferred = udp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (error) { + keep_receiving = false; + ADD_FAILURE() << __func__ << " error: " << error.message(); + return; + } else { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + vsomeip::service_t its_service = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_SERVICE_POS_MIN], + receive_buffer[VSOMEIP_SERVICE_POS_MAX]); + vsomeip::method_t its_method = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_METHOD_POS_MIN], + receive_buffer[VSOMEIP_METHOD_POS_MAX]); + if (its_service == vsomeip::sd::service && its_method == vsomeip::sd::method) { + vsomeip::sd::message_impl sd_msg; + EXPECT_TRUE(sd_msg.deserialize(&its_deserializer)); + EXPECT_EQ(1u, sd_msg.get_entries().size()); + for (auto e : sd_msg.get_entries()) { + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP && !client_subscribed) { + EXPECT_TRUE(e->is_eventgroup_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP, e->get_type()); + EXPECT_EQ(1,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(1u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP) { + std::shared_ptr<vsomeip::sd::eventgroupentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::eventgroupentry_impl>(e); + EXPECT_EQ(1u, its_casted_entry->get_eventgroup()); + } + std::shared_ptr<vsomeip::sd::option_impl> op = sd_msg.get_options().front(); + EXPECT_EQ(op->get_type(), vsomeip::sd::option_type_e::IP4_ENDPOINT); + EXPECT_EQ(op->get_length(), 9u); + if (op->get_type() == vsomeip::sd::option_type_e::IP4_ENDPOINT) { + std::shared_ptr<vsomeip::sd::ipv4_option_impl> ip_op + = std::static_pointer_cast<vsomeip::sd::ipv4_option_impl>(op); + EXPECT_EQ(vsomeip::sd::layer_four_protocol_e::UDP, ip_op->get_layer_four_protocol()); + udp_client_info = boost::asio::ip::udp::endpoint( + boost::asio::ip::address_v4(ip_op->get_address()), + ip_op->get_port()); + } + + remote_client_subscribed.set_value(); + client_subscribed = true; + } else if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE && !service_offered) { + EXPECT_TRUE(e->is_service_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::OFFER_SERVICE, e->get_type()); + EXPECT_EQ(2,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id + 1u, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(2u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE) { + std::shared_ptr<vsomeip::sd::serviceentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::serviceentry_impl>(e); + EXPECT_EQ(0u, its_casted_entry->get_minor_version()); + } + for (const auto op : sd_msg.get_options()) { + EXPECT_EQ(op->get_type(), vsomeip::sd::option_type_e::IP4_ENDPOINT); + EXPECT_EQ(op->get_length(), 9u); + if (op->get_type() == vsomeip::sd::option_type_e::IP4_ENDPOINT) { + std::shared_ptr<vsomeip::sd::ipv4_option_impl> ip_op + = std::static_pointer_cast<vsomeip::sd::ipv4_option_impl>(op); + if (ip_op->get_layer_four_protocol() == vsomeip::sd::layer_four_protocol_e::UDP) { + EXPECT_EQ(vsomeip::sd::layer_four_protocol_e::UDP, ip_op->get_layer_four_protocol()); + udp_service_info = boost::asio::ip::udp::endpoint( + boost::asio::ip::address_v4(ip_op->get_address()), + ip_op->get_port()); + } + } + } + offer_received.set_value(); + service_offered = true; + } + } + if (service_offered && client_subscribed) { + keep_receiving = false; + } + } else { + ADD_FAILURE() << " received non-sd message"; + } + } + } + }); + + std::thread send_thread([&]() { + try { + boost::system::error_code ec; + udp_socket_service.set_option(boost::asio::socket_base::reuse_address(true), ec); + boost::asio::detail::throw_error(ec, "udp_socket_service set_option"); + + // offer the service + std::uint8_t its_offer_service_message[] = { + 0xff, 0xff, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x30, // length + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x02, 0x00, + 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, // length entries array + 0x01, 0x00, 0x00, 0x20, + 0x33, 0x44, 0x00, 0x01, // service / instance + 0x00, 0xff, 0xff, 0xff, // major / ttl + 0x00, 0x00, 0x00, 0x00, // minor + 0x00, 0x00, 0x00, 0x0c, // length options array + 0x00, 0x09, 0x04, 0x00, + 0xff, 0xff, 0xff, 0xff, // slave address + 0x00, 0x11, 0x9c, 0x41, // offer via udp + }; + boost::asio::ip::address its_local_address = + boost::asio::ip::address::from_string(std::string(local_address)); + std::memcpy(&its_offer_service_message[48], &its_local_address.to_v4().to_bytes()[0], 4); + + boost::asio::ip::udp::socket::endpoint_type target_sd( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30490); + udp_socket.send_to(boost::asio::buffer(its_offer_service_message), target_sd); + + // wait until client subscribed + if (std::future_status::timeout == remote_client_subscribed.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't subscribe within time"; + } + + // wait until a offer was received + if (std::future_status::timeout == offer_received.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Didn't receive offer within time"; + } + + // send malicious data as server (wrong protocol version) + std::uint8_t wrong_protocol_data[] = { + 0x33, 0x44, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xAA, 0xAA, 0xCA, 0xFE, + 0xAA, 0x00, 0x00, 0x00 // protocol version set to 0xAA + }; + udp_socket_service.send_to(boost::asio::buffer(wrong_protocol_data), udp_client_info); + + std::uint8_t wrong_message_type_data[] = { + 0x33, 0x44, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xBB, 0xBB, 0xCA, 0xFE, + 0x01, 0x00, 0xBB, 0x00 // message type set to 0xBB + }; + udp_socket_service.send_to(boost::asio::buffer(wrong_message_type_data), udp_client_info); + + std::uint8_t wrong_return_code_data[] = { + 0x33, 0x44, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xCC, 0xCC, 0xCA, 0xFE, + 0x01, 0x00, 0x00, 0xCC // return code set to 0xCC + }; + udp_socket_service.send_to(boost::asio::buffer(wrong_return_code_data), udp_client_info); + + std::uint8_t all_wrong_data[] = { + 0x33, 0x44, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xAA, 0xAA, 0xCA, 0xFE, + 0xAA, 0x00, 0xBB, 0xCC + }; + udp_socket_service.send_to(boost::asio::buffer(all_wrong_data), udp_client_info); + + udp_socket_service.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket_service.close(ec); + + // establish second UDP connection as client and send malicious data as well + std::atomic<bool> error_response_as_client_received(false); + + boost::asio::ip::udp::socket udp_socket_client(io_); + boost::asio::ip::udp::socket::endpoint_type remote( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30001); + udp_socket_client.open(remote.protocol()); + udp_socket_client.connect(remote); + + + std::thread udp_client_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::function<void()> receive; + std::vector<std::uint8_t> receive_buffer(4096); + + auto receive_cbk = [&](const boost::system::error_code& _error, + std::size_t bytes_transferred) { + if (!_error) { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + std::shared_ptr<vsomeip::message> its_message(its_deserializer.deserialize_message()); + EXPECT_EQ(0x3345, its_message->get_service()); + EXPECT_EQ(0x1, its_message->get_method()); + EXPECT_EQ(0xCCCC, its_message->get_client()); + EXPECT_EQ(0xDDDD, its_message->get_session()); + EXPECT_EQ(vsomeip::return_code_e::E_WRONG_PROTOCOL_VERSION, its_message->get_return_code()); + EXPECT_EQ(vsomeip::message_type_e::MT_ERROR, its_message->get_message_type()); + error_response_as_client_received = true; + keep_receiving = false; + } else { + ADD_FAILURE() << __func__ << ":" << __LINE__ << " error: " << _error.message(); + return; + } + }; + + receive = [&]() { + udp_socket_client.async_receive(boost::asio::buffer(receive_buffer, receive_buffer.capacity()), + receive_cbk); + }; + + while (keep_receiving) { + receive(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }); + + // send malicious data as client (too long length and wrong protocol version) + std::uint8_t wrong_protocol_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xCC, 0xCC, 0xDD, 0xDD, + 0xAA, 0x00, 0x00, 0x00 // protocol version set to 0xAA + }; + udp_socket_client.send_to(boost::asio::buffer(wrong_protocol_client_data), udp_service_info); + + std::uint8_t wrong_message_type_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xCC, 0xCC, 0xDD, 0xDD, + 0x01, 0x00, 0xBB, 0x00 // message type set to 0xBB + }; + udp_socket_client.send_to(boost::asio::buffer(wrong_message_type_client_data), udp_service_info); + + std::uint8_t wrong_return_code_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xCC, 0xCC, 0xDD, 0xDD, + 0x01, 0x00, 0x00, 0xCC // return code set to 0xCC + }; + udp_socket_client.send_to(boost::asio::buffer(wrong_return_code_client_data), udp_service_info); + + std::uint8_t all_wrong_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xCC, 0xCC, 0xDD, 0xDD, + 0xAA, 0x00, 0xBB, 0xCC + }; + udp_socket_client.send_to(boost::asio::buffer(all_wrong_client_data), udp_service_info); + + + udp_client_receive_thread.join(); + EXPECT_TRUE(error_response_as_client_received); + + udp_socket_client.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket_client.close(ec); + + // call shutdown method + std::uint8_t shutdown_call[] = { + 0x33, 0x45, 0x14, 0x04, + 0x00, 0x00, 0x00, 0x08, + 0x22, 0x22, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00 }; + boost::asio::ip::udp::socket::endpoint_type target_service( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30001); + boost::asio::ip::udp::socket udp_socket2(io_, boost::asio::ip::udp::v4()); + udp_socket2.send_to(boost::asio::buffer(shutdown_call), target_service); + udp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket2.close(ec); + } catch (const std::exception& _e) { + ADD_FAILURE() << "catched exception: " << _e.what(); + } + + }); + + send_thread.join(); + sd_receive_thread.join(); + boost::system::error_code ec; + udp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket.close(ec); +} + #ifndef _WIN32 int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); if(argc < 3) { - std::cerr << "Please pass an target and local IP address to this binary like: " - << argv[0] << " 10.0.3.1 10.0.3.202" << std::endl; + std::cerr << "Please pass an target, local IP address and test mode to this binary like: " + << argv[0] << " 10.0.3.1 10.0.3.202 EVENTS" << std::endl; exit(1); } remote_address = argv[1]; local_address = argv[2]; + std::string its_testmode = argv[3]; + if (its_testmode == std::string("MALICIOUS_EVENTS")) { + ::testing::GTEST_FLAG(filter) = "*send_malicious_events"; + } else if (its_testmode == std::string("PROTOCOL_VERSION")) { + ::testing::GTEST_FLAG(filter) = "*send_wrong_protocol_version"; + } else if (its_testmode == std::string("MESSAGE_TYPE")) { + ::testing::GTEST_FLAG(filter) = "*send_wrong_message_type"; + } else if (its_testmode == std::string("RETURN_CODE")) { + ::testing::GTEST_FLAG(filter) = "*send_wrong_return_code"; + } else if (its_testmode == std::string("WRONG_HEADER_FIELDS_UDP")) { + ::testing::GTEST_FLAG(filter) = "*wrong_header_fields_udp"; + } return RUN_ALL_TESTS(); } #endif diff --git a/test/malicious_data_tests/malicious_data_test_service.cpp b/test/malicious_data_tests/malicious_data_test_service.cpp index 17a4bb8..cc4999e 100644 --- a/test/malicious_data_tests/malicious_data_test_service.cpp +++ b/test/malicious_data_tests/malicious_data_test_service.cpp @@ -61,15 +61,20 @@ public: // request service of client app_->request_service(service_info_.service_id, service_info_.instance_id); app_->subscribe(service_info_.service_id, service_info_.instance_id, - service_info_.eventgroup_id, 0, vsomeip::subscription_type_e::SU_RELIABLE, + service_info_.eventgroup_id, 0, + (testmode_ == malicious_data_test::test_mode_e::WRONG_HEADER_FIELDS_UDP) ? + vsomeip::subscription_type_e::SU_RELIABLE_AND_UNRELIABLE : + vsomeip::subscription_type_e::SU_RELIABLE, service_info_.event_id); app_->start(); } ~malicious_data_test_service() { - EXPECT_EQ(9u, received_events_); - EXPECT_EQ(9u, received_methodcalls_); + if (testmode_ == malicious_data_test::test_mode_e::MALICIOUS_EVENTS) { + EXPECT_EQ(9u, received_events_); + EXPECT_EQ(9u, received_methodcalls_); + } offer_thread_.join(); } @@ -153,7 +158,7 @@ private: std::thread offer_thread_; }; -malicious_data_test::test_mode_e its_testmode(malicious_data_test::test_mode_e::SUBSCRIBE); +malicious_data_test::test_mode_e its_testmode(malicious_data_test::test_mode_e::MALICIOUS_EVENTS); TEST(someip_malicious_data_test, block_subscription_handler) { @@ -165,6 +170,18 @@ TEST(someip_malicious_data_test, block_subscription_handler) int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + std::string its_passed_testmode = argv[1]; + if (its_passed_testmode == std::string("MALICIOUS_EVENTS")) { + its_testmode = malicious_data_test::test_mode_e::MALICIOUS_EVENTS; + } else if (its_passed_testmode == std::string("PROTOCOL_VERSION")) { + its_testmode = malicious_data_test::test_mode_e::PROTOCOL_VERSION; + } else if (its_passed_testmode == std::string("MESSAGE_TYPE")) { + its_testmode = malicious_data_test::test_mode_e::MESSAGE_TYPE; + } else if (its_passed_testmode == std::string("RETURN_CODE")) { + its_testmode = malicious_data_test::test_mode_e::RETURN_CODE; + } else if (its_passed_testmode == std::string("WRONG_HEADER_FIELDS_UDP")) { + its_testmode = malicious_data_test::test_mode_e::WRONG_HEADER_FIELDS_UDP; + } return RUN_ALL_TESTS(); } #endif |