summaryrefslogtreecommitdiffstats
path: root/src/input_common/udp/protocol.cpp
blob: 5e50bd6129dccc8c34f99f9b8cfb9b3662b2f2be (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#include <cstddef>
#include <cstring>
#include "common/logging/log.h"
#include "input_common/udp/protocol.h"

namespace InputCommon::CemuhookUDP {

static constexpr std::size_t GetSizeOfResponseType(Type t) {
    switch (t) {
    case Type::Version:
        return sizeof(Response::Version);
    case Type::PortInfo:
        return sizeof(Response::PortInfo);
    case Type::PadData:
        return sizeof(Response::PadData);
    }
    return 0;
}

namespace Response {

/**
 * Returns Type if the packet is valid, else none
 *
 * Note: Modifies the buffer to zero out the crc (since thats the easiest way to check without
 * copying the buffer)
 */
std::optional<Type> Validate(u8* data, std::size_t size) {
    if (size < sizeof(Header)) {
        return std::nullopt;
    }
    Header header{};
    std::memcpy(&header, data, sizeof(Header));
    if (header.magic != SERVER_MAGIC) {
        LOG_ERROR(Input, "UDP Packet has an unexpected magic value");
        return std::nullopt;
    }
    if (header.protocol_version != PROTOCOL_VERSION) {
        LOG_ERROR(Input, "UDP Packet protocol mismatch");
        return std::nullopt;
    }
    if (header.type < Type::Version || header.type > Type::PadData) {
        LOG_ERROR(Input, "UDP Packet is an unknown type");
        return std::nullopt;
    }

    // Packet size must equal sizeof(Header) + sizeof(Data)
    // and also verify that the packet info mentions the correct size. Since the spec includes the
    // type of the packet as part of the data, we need to include it in size calculations here
    // ie: payload_length == sizeof(T) + sizeof(Type)
    const std::size_t data_len = GetSizeOfResponseType(header.type);
    if (header.payload_length != data_len + sizeof(Type) || size < data_len + sizeof(Header)) {
        LOG_ERROR(
            Input,
            "UDP Packet payload length doesn't match. Received: {} PayloadLength: {} Expected: {}",
            size, header.payload_length, data_len + sizeof(Type));
        return std::nullopt;
    }

    const u32 crc32 = header.crc;
    boost::crc_32_type result;
    // zero out the crc in the buffer and then run the crc against it
    std::memset(&data[offsetof(Header, crc)], 0, sizeof(u32_le));

    result.process_bytes(data, data_len + sizeof(Header));
    if (crc32 != result.checksum()) {
        LOG_ERROR(Input, "UDP Packet CRC check failed. Offset: {}", offsetof(Header, crc));
        return std::nullopt;
    }
    return header.type;
}
} // namespace Response

} // namespace InputCommon::CemuhookUDP