// Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <cstring>
#include <future>

#include <gtest/gtest.h>

#include <boost/asio.hpp>

#include <vsomeip/vsomeip.hpp>

#include "../../implementation/utility/include/byteorder.hpp"
#include "../../implementation/message/include/deserializer.hpp"
#include "../../implementation/service_discovery/include/service_discovery.hpp"
#include "../../implementation/service_discovery/include/message_impl.hpp"
#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;
static char* local_address;

class malicious_data : public ::testing::Test {
public:
    malicious_data() :
        work_(std::make_shared<boost::asio::io_service::work>(io_)),
        io_thread_(std::bind(&malicious_data::io_run, this)) {}
protected:

    void TearDown() {
        work_.reset();
        io_thread_.join();
        io_.stop();
    }

    void io_run() {
        io_.run();
    }

    boost::asio::io_service io_;
    std::shared_ptr<boost::asio::io_service::work> work_;
    std::thread io_thread_;
};

TEST_F(malicious_data, send_malicious_events)
{
    std::promise<bool> client_subscribed;

    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 receive_thread([&](){
        std::atomic<bool> keep_receiving(true);
        std::function<void()> receive;
        std::vector<std::uint8_t> receive_buffer(4096);
        std::vector<vsomeip::event_t> its_received_events;

        const std::function<void(const boost::system::error_code&, std::size_t)> receive_cbk = [&](
                const boost::system::error_code& error, std::size_t bytes_transferred) {
            if (error) {
                keep_receiving = false;
                ADD_FAILURE() << __func__ << " error: " << error.message();
                return;
            }
            #if 0
            std::stringstream str;
            for (size_t i = 0; i < bytes_transferred; i++) {
                str << std::hex << std::setw(2) << std::setfill('0') << std::uint32_t(receive_buffer[i]) << " ";
            }
            std::cout << __func__ << " received: " << std::dec << bytes_transferred << " bytes: " << str.str() << std::endl;
            #endif

            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 (const auto& e : sd_msg.get_entries()) {
                    if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP) {
                        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());
                        }
                        client_subscribed.set_value(true);
                        keep_receiving = false;
                    }
                }
            }


        };

        receive = [&]() {
            udp_socket.async_receive(boost::asio::buffer(receive_buffer, receive_buffer.capacity()),
                    receive_cbk);
        };

        receive();
        while(keep_receiving) {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
    });

    std::thread send_thread([&]() {
        try {
            std::promise<bool> 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(true);
                } 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 == client_subscribed.get_future().wait_for(std::chrono::seconds(10))) {
                ADD_FAILURE() << "Client didn't subscribe within time";
            }

            // send malicious data as server
            std::uint8_t its_malicious_data[] = {
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x11, 0x3f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x38, 0x4f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x5f, 0x5f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x86, 0x6f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xad, 0x7f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xd4, 0x8f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xfb, 0x9f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x22, 0xaf, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, // payload missing
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0xbf, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x70, 0xcf, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x43, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x6d, 0x80, 0x00, 0x45, 0x22, 0x80, 0x00, 0x45, 0x3b, 0x80, 0x00, 0x43, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x97, 0xdf, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x23, 0x99, 0x00, 0x4e, 0xb3, 0xe4, 0x4e, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x48, 0x00, 0x00, 0x45, 0x96, 0x00, 0x00, 0x45, 0x16, 0x00, 0x00, 0x45, 0x22, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x22, 0x91, 0x00, 0x4e, 0xb3, 0xe3, 0xd7, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x83, 0x40, 0x00, 0x44, 0x48, 0x00, 0x00, 0x45, 0x9c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xe5, 0xff, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x21, 0x6c, 0x00, 0x4e, 0xb3, 0xe3, 0x55, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x20, 0x00, 0x44, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x0d, 0x0f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x20, 0xa9, 0x00, 0x4e, 0xb3, 0xe3, 0x56, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x83, 0x40, 0x00, 0x45, 0x6d, 0x80, 0x00, 0x44, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x34, 0x1f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1f, 0xc6, 0x00, 0x4e, 0xb3, 0xe3, 0x87, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x96, 0x00, 0x00, 0x45, 0x83, 0x40, 0x00, 0x44, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x5b, 0x2f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1e, 0xf1, 0x00, 0x4e, 0xb3, 0xe3, 0x5e, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x16, 0x00, 0x00, 0x45, 0x9c, 0x40, 0x00, 0x45, 0x22, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x82, 0x3f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1d, 0xad, 0x00, 0x4e, 0xb3, 0xe3, 0xa8, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x48, 0x00, 0x00, 0x45, 0x16, 0x00, 0x00, 0x44, 0xaf, 0x00, 0x00, 0x45, 0x3b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xa9, 0x4f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1d, 0xec, 0x00, 0x4e, 0xb3, 0xe3, 0xd8, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xa8, 0xc0, 0x00, 0x44, 0xaf, 0x00, 0x00, 0x45, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xd0, 0x5f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1d, 0xf9, 0x00, 0x4e, 0xb3, 0xe3, 0xdd, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xfa, 0x00, 0x00, 0x44, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xf7, 0x6f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1d, 0xf9, 0x00, 0x4e, 0xb3, 0xe3, 0xdd, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x19, 0x20, 0x00, 0x43, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x44, 0x80, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1e, 0x7f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1d, 0xf9, 0x00, 0x4e, 0xb3, 0xe3, 0xdd, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x09, 0x80, 0x00, 0x44, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            };
            tcp_socket.send(boost::asio::buffer(its_malicious_data));

            // establish second tcp connection as client and send malicious data as well
            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::uint8_t its_malicious_client_data[] = {
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x11, 0x3f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x38, 0x4f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x5f, 0x5f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x86, 0x6f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xad, 0x7f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xd4, 0x8f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xfb, 0x9f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x22, 0xaf, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, // payload missing
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0xbf, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x24, 0x02, 0x00, 0x4e, 0xb3, 0xe4, 0x7a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x70, 0xcf, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x43, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x6d, 0x80, 0x00, 0x45, 0x22, 0x80, 0x00, 0x45, 0x3b, 0x80, 0x00, 0x43, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x97, 0xdf, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x23, 0x99, 0x00, 0x4e, 0xb3, 0xe4, 0x4e, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x48, 0x00, 0x00, 0x45, 0x96, 0x00, 0x00, 0x45, 0x16, 0x00, 0x00, 0x45, 0x22, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x22, 0x91, 0x00, 0x4e, 0xb3, 0xe3, 0xd7, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x83, 0x40, 0x00, 0x44, 0x48, 0x00, 0x00, 0x45, 0x9c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xe5, 0xff, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x21, 0x6c, 0x00, 0x4e, 0xb3, 0xe3, 0x55, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x20, 0x00, 0x44, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x0d, 0x0f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x20, 0xa9, 0x00, 0x4e, 0xb3, 0xe3, 0x56, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x83, 0x40, 0x00, 0x45, 0x6d, 0x80, 0x00, 0x44, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x34, 0x1f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1f, 0xc6, 0x00, 0x4e, 0xb3, 0xe3, 0x87, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x96, 0x00, 0x00, 0x45, 0x83, 0x40, 0x00, 0x44, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x5b, 0x2f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1e, 0xf1, 0x00, 0x4e, 0xb3, 0xe3, 0x5e, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x16, 0x00, 0x00, 0x45, 0x9c, 0x40, 0x00, 0x45, 0x22, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x82, 0x3f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1d, 0xad, 0x00, 0x4e, 0xb3, 0xe3, 0xa8, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x48, 0x00, 0x00, 0x45, 0x16, 0x00, 0x00, 0x44, 0xaf, 0x00, 0x00, 0x45, 0x3b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xa9, 0x4f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1d, 0xec, 0x00, 0x4e, 0xb3, 0xe3, 0xd8, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xa8, 0xc0, 0x00, 0x44, 0xaf, 0x00, 0x00, 0x45, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xd0, 0x5f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1d, 0xf9, 0x00, 0x4e, 0xb3, 0xe3, 0xdd, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xfa, 0x00, 0x00, 0x44, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xf7, 0x6f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1d, 0xf9, 0x00, 0x4e, 0xb3, 0xe3, 0xdd, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x19, 0x20, 0x00, 0x43, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x33, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1e, 0x7f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x05, 0x26, 0x5c, 0x00, 0x04, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x0e, 0x47, 0x36, 0x37, 0x38, 0x35, 0x37, 0x35, 0x00, 0x4d, 0xd5, 0x1d, 0xf9, 0x00, 0x4e, 0xb3, 0xe3, 0xdd, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x09, 0x80, 0x00, 0x44, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            };
            tcp_socket2.send(boost::asio::buffer(its_malicious_client_data));

            std::this_thread::sleep_for(std::chrono::milliseconds(1500));
            // 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);
            udp_socket.send_to(boost::asio::buffer(shutdown_call), target_service);
        } catch (const std::exception& _e) {
            ADD_FAILURE() << "catched exception: " << _e.what();
        }

    });

    send_thread.join();
    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 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 (const 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) {
                        #if 0
                        std::stringstream str;
                        for (size_t i = 0; i < bytes_transferred; i++) {
                            str << std::hex << std::setw(2) << std::setfill('0') << std::uint32_t(receive_buffer[i]) << " ";
                        }
                        std::cout << __func__ << " received: " << std::dec << bytes_transferred << " bytes: " << str.str() << std::endl;
                        #endif
                        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 (const 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 (const 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 (const 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, 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
