| 1 |
#include "catch.hpp" |
| 2 |
|
| 3 |
#include "UnapiNetWire.hh" |
| 4 |
|
| 5 |
#include <array> |
| 6 |
#include <cstdint> |
| 7 |
#include <span> |
| 8 |
#include <vector> |
| 9 |
|
| 10 |
using namespace openmsx; |
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
static_assert(wire_layout<DetectResult>); |
| 15 |
static_assert(wire_layout<TcpStateResult>); |
| 16 |
static_assert(wire_layout<TcpOpenParams>); |
| 17 |
static_assert(wire_layout<UdpRecvResultHeader>); |
| 18 |
static_assert(wire_layout<IcmpRecvResult>); |
| 19 |
namespace { |
| 20 |
struct Padded { uint8_t a; uint32_t b; }; |
| 21 |
} |
| 22 |
static_assert(!wire_layout<Padded>); |
| 23 |
static_assert(!wire_layout<float>); |
| 24 |
static_assert(!wire_layout<std::vector<uint8_t>>); |
| 25 |
|
| 26 |
[[nodiscard]] static std::vector<uint8_t> vec(std::span<const uint8_t> s) |
| 27 |
{ |
| 28 |
return {s.begin(), s.end()}; |
| 29 |
} |
| 30 |
|
| 31 |
TEST_CASE("UnapiNetWire: default-constructed structs are valid success frames") |
| 32 |
{ |
| 33 |
|
| 34 |
|
| 35 |
CHECK(vec(toBytes(DetectResult{})) == |
| 36 |
std::vector<uint8_t>{0x00, 0x55, 0x02, 0x00, 0x00}); |
| 37 |
CHECK(toBytes(TcpStateResult{})[0] == 0x00); |
| 38 |
CHECK(toBytes(TcpRecvResultHeader{})[0] == 0x00); |
| 39 |
CHECK(toBytes(UdpRecvResultHeader{})[0] == 0x00); |
| 40 |
CHECK(toBytes(IcmpRecvResult{})[0] == 0x00); |
| 41 |
CHECK(toBytes(OpenResult{})[0] == 0x00); |
| 42 |
CHECK(toBytes(GetLocalIpResult{})[0] == 0x00); |
| 43 |
CHECK(toBytes(UdpStateResult{})[0] == 0x00); |
| 44 |
} |
| 45 |
|
| 46 |
TEST_CASE("UnapiNetWire: DETECT result is byte-identical") |
| 47 |
{ |
| 48 |
DetectResult r{}; |
| 49 |
r.caps = 0x1F; |
| 50 |
CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0x55, 0x02, 0x1F, 0x00}); |
| 51 |
} |
| 52 |
|
| 53 |
TEST_CASE("UnapiNetWire: TCP_STATE result is byte-identical (mixed endian)") |
| 54 |
{ |
| 55 |
TcpStateResult r{}; |
| 56 |
r.state = 0x04; |
| 57 |
r.avail = 0x0102; |
| 58 |
r.closeReason = 0x01; |
| 59 |
r.remoteIp = 0xC0A80101; |
| 60 |
r.remotePort = 0x1F90; |
| 61 |
r.localPort = 0x0050; |
| 62 |
|
| 63 |
CHECK(vec(toBytes(r)) == std::vector<uint8_t>{ |
| 64 |
0x00, 0x04, 0x02, 0x01, 0x01, 0xC0, 0xA8, 0x01, 0x01, 0x90, 0x1F, 0x50, 0x00}); |
| 65 |
} |
| 66 |
|
| 67 |
TEST_CASE("UnapiNetWire: OPEN result is status + 1-based handle") |
| 68 |
{ |
| 69 |
OpenResult r{}; |
| 70 |
r.handle = 0x03; |
| 71 |
CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0x03}); |
| 72 |
} |
| 73 |
|
| 74 |
TEST_CASE("UnapiNetWire: TCP_RECV header is status + little-endian length") |
| 75 |
{ |
| 76 |
TcpRecvResultHeader r{}; |
| 77 |
r.actualLen = 0x0102; |
| 78 |
CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0x02, 0x01}); |
| 79 |
} |
| 80 |
|
| 81 |
TEST_CASE("UnapiNetWire: GET_LOCALIP result is status + 4-byte big-endian") |
| 82 |
{ |
| 83 |
GetLocalIpResult r{}; |
| 84 |
r.ip = 0xC0A80101; |
| 85 |
CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0xC0, 0xA8, 0x01, 0x01}); |
| 86 |
} |
| 87 |
|
| 88 |
TEST_CASE("UnapiNetWire: UDP_STATE result is status + little-endian size") |
| 89 |
{ |
| 90 |
UdpStateResult r{}; |
| 91 |
r.firstDgramSize = 0x0004; |
| 92 |
CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0x04, 0x00}); |
| 93 |
} |
| 94 |
|
| 95 |
TEST_CASE("UnapiNetWire: DNS_QUERY fast path = status + marker + big-endian IP") |
| 96 |
{ |
| 97 |
DnsQueryResult r{}; |
| 98 |
r.ip = 0xC0A80101; |
| 99 |
CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0x01, 0xC0, 0xA8, 0x01, 0x01}); |
| 100 |
} |
| 101 |
|
| 102 |
TEST_CASE("UnapiNetWire: DNS_STATUS complete = status + state + big-endian IP") |
| 103 |
{ |
| 104 |
DnsStatusResult r{}; |
| 105 |
r.ip = 0x08080808; |
| 106 |
CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0x02, 0x08, 0x08, 0x08, 0x08}); |
| 107 |
} |
| 108 |
|
| 109 |
TEST_CASE("UnapiNetWire: DNS_STATUS failed lookup = success carrying bad news") |
| 110 |
{ |
| 111 |
DnsStatusFailed r{}; |
| 112 |
r.sub = 0x03; |
| 113 |
CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0xFF, 0x03}); |
| 114 |
} |
| 115 |
|
| 116 |
TEST_CASE("UnapiNetWire: UDP_RECV header (status, IP big, port/len little)") |
| 117 |
{ |
| 118 |
UdpRecvResultHeader r{}; |
| 119 |
r.srcIp = 0xC0A80101; |
| 120 |
r.srcPort = 0x0035; |
| 121 |
r.actualLen = 0x0004; |
| 122 |
CHECK(vec(toBytes(r)) == std::vector<uint8_t>{ |
| 123 |
0x00, 0xC0, 0xA8, 0x01, 0x01, 0x35, 0x00, 0x04, 0x00}); |
| 124 |
} |
| 125 |
|
| 126 |
TEST_CASE("UnapiNetWire: ICMP_RECV record (12 bytes, status replaces hasData)") |
| 127 |
{ |
| 128 |
IcmpRecvResult r{}; |
| 129 |
r.srcIp = 0x08080808; |
| 130 |
r.ttl = 0x40; |
| 131 |
r.identifier = 0x1234; |
| 132 |
r.sequence = 0x0001; |
| 133 |
r.dataLen = 0x0020; |
| 134 |
CHECK(vec(toBytes(r)) == std::vector<uint8_t>{ |
| 135 |
0x00, 0x08, 0x08, 0x08, 0x08, 0x40, 0x34, 0x12, 0x01, 0x00, 0x20, 0x00}); |
| 136 |
} |
| 137 |
|
| 138 |
TEST_CASE("UnapiNetWire: TCP_OPEN params round-trip (IP big, ports little)") |
| 139 |
{ |
| 140 |
std::array<uint8_t, 11> bytes{ |
| 141 |
0xC0, 0xA8, 0x01, 0x01, 0x90, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x02}; |
| 142 |
auto p = fromBytes<TcpOpenParams>(bytes); |
| 143 |
CHECK(uint32_t(p.remoteIp) == 0xC0A80101); |
| 144 |
CHECK(uint16_t(p.remotePort) == 0x1F90); |
| 145 |
CHECK(uint16_t(p.localPort) == 0x0000); |
| 146 |
CHECK(uint16_t(p.timeout) == 0x0000); |
| 147 |
CHECK(p.flags == 0x02); |
| 148 |
} |
| 149 |
|
| 150 |
TEST_CASE("UnapiNetWire: UDP_SEND param header round-trip") |
| 151 |
{ |
| 152 |
std::array<uint8_t, 9> bytes{ |
| 153 |
0x01, 0xC0, 0xA8, 0x01, 0xFF, 0x99, 0x1F, 0x03, 0x00}; |
| 154 |
auto p = fromBytes<UdpSendParamHeader>(bytes); |
| 155 |
CHECK(p.handle == 0x01); |
| 156 |
CHECK(uint32_t(p.destIp) == 0xC0A801FF); |
| 157 |
CHECK(uint16_t(p.destPort) == 0x1F99); |
| 158 |
CHECK(uint16_t(p.len) == 0x0003); |
| 159 |
} |