join_descriptor.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. #include "join_descriptor.h"
  2. #include <algorithm>
  3. #include <span>
  4. namespace sunrise::middleware::gameplay::descriptor {
  5. namespace {
  6. /** The machine identity occupies the first eight bytes. */
  7. constexpr std::size_t kMachineOffset = 0;
  8. /** The NetAddr follows the machine identity. */
  9. constexpr std::size_t kNetAddrOffset = 8;
  10. /** The IPv4 address opens the first local entry, in network byte order. */
  11. constexpr std::size_t kAddressOffset = 0;
  12. /** The UDP port follows the address, low byte first. The IPv4 beside it stays network order. */
  13. constexpr std::size_t kPortOffset = 4;
  14. /** The public entry follows five 6-byte local entries, and carries the same two fields. */
  15. constexpr std::size_t kPublicAddressOffset = 30;
  16. /** The public UDP port follows the public address, low byte first. */
  17. constexpr std::size_t kPublicPortOffset = 34;
  18. /** The NAT type closes the address body at offset 40. */
  19. constexpr std::size_t kNatTypeOffset = 40;
  20. /** NAT type 1 reads as open. Zero reads as unknown and leaves the address unroutable. */
  21. constexpr std::byte kNatTypeOpen{1};
  22. /** The transport method is the last NetAddr byte. */
  23. constexpr std::size_t kMethodOffset = kNetAddrSize - 1;
  24. /** Method 0 selects the direct path. Methods 6 and 7 would select the relay instead. */
  25. constexpr std::byte kDirectMethod{0};
  26. /** The online session id occupies the tail, low half first. */
  27. constexpr std::size_t kSessionOffset = 110;
  28. /** Bits in one byte. */
  29. constexpr unsigned kByteBits = 8;
  30. /** Mask of one byte. */
  31. constexpr std::uint64_t kByteMask = 0xFF;
  32. /** The advertised port must be even. */
  33. constexpr std::uint16_t kPortAlignment = 2;
  34. /**
  35. * Writes one unsigned value in network byte order.
  36. * @param output Field storage.
  37. * @param offset First byte of the field.
  38. * @param value Host-order value.
  39. * @param width Field width in bytes.
  40. */
  41. void write_network_order(std::span<std::byte> output,
  42. std::size_t offset,
  43. std::uint64_t value,
  44. std::size_t width) noexcept {
  45. for (std::size_t index = 0; index < width; ++index) {
  46. const unsigned shift = static_cast<unsigned>(width - 1 - index) * kByteBits;
  47. output[offset + index] = static_cast<std::byte>((value >> shift) & kByteMask);
  48. }
  49. }
  50. /**
  51. * Writes one unsigned value low byte first.
  52. * @param output Field storage.
  53. * @param offset First byte of the field.
  54. * @param value Host-order value.
  55. * @param width Field width in bytes.
  56. */
  57. void write_memory_order(std::span<std::byte> output,
  58. std::size_t offset,
  59. std::uint64_t value,
  60. std::size_t width) noexcept {
  61. for (std::size_t index = 0; index < width; ++index) {
  62. const unsigned shift = static_cast<unsigned>(index) * kByteBits;
  63. output[offset + index] = static_cast<std::byte>((value >> shift) & kByteMask);
  64. }
  65. }
  66. } // namespace
  67. /** Builds one NetAddr for the direct method-0 path. */
  68. void write_net_addr(std::uint32_t address,
  69. std::uint16_t port,
  70. std::array<std::byte, kNetAddrSize>& output) noexcept {
  71. output = {};
  72. write_network_order(output, kAddressOffset, address, sizeof(std::uint32_t));
  73. write_memory_order(output, kPortOffset, port, sizeof(std::uint16_t));
  74. write_network_order(output, kPublicAddressOffset, address, sizeof(std::uint32_t));
  75. write_memory_order(output, kPublicPortOffset, port, sizeof(std::uint16_t));
  76. output[kNatTypeOffset] = kNatTypeOpen;
  77. output[kMethodOffset] = kDirectMethod;
  78. }
  79. /** Builds one join descriptor for the direct method-0 path. */
  80. bool build(const JoinEndpoint& endpoint, std::array<std::byte, kDescriptorSize>& output) noexcept {
  81. if (endpoint.machineId == 0 || endpoint.address == 0 || endpoint.port == 0
  82. || endpoint.port % kPortAlignment != 0 || endpoint.onlineSessionId == 0) {
  83. return false;
  84. }
  85. std::array<std::byte, kNetAddrSize> netAddr{};
  86. write_net_addr(endpoint.address, endpoint.port, netAddr);
  87. std::array<std::byte, kDescriptorSize> candidate{};
  88. write_memory_order(candidate, kMachineOffset, endpoint.machineId, sizeof(std::uint64_t));
  89. std::copy(netAddr.begin(), netAddr.end(), candidate.begin() + kNetAddrOffset);
  90. // The join key stays zero. The direct path accepts it and carries no key material.
  91. write_memory_order(candidate, kSessionOffset, endpoint.onlineSessionId, sizeof(std::uint64_t));
  92. output = candidate;
  93. return true;
  94. }
  95. } // namespace sunrise::middleware::gameplay::descriptor