peer_out_of_band.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. #include <Windows.h>
  2. #include <array>
  3. #include "../../../middleware/crypto/random_bytes.h"
  4. #include "../../../middleware/encoding/bit_raw.h"
  5. #include "../../../middleware/encoding/bit_reader.h"
  6. #include "../../../middleware/encoding/bit_writer.h"
  7. #include "../../../middleware/gameplay/descriptor/join_descriptor.h"
  8. #include "../../../middleware/gameplay/peer/connect_messages.h"
  9. #include "../../../middleware/gameplay/peer/established_packet.h"
  10. #include "../../../middleware/gameplay/peer/join_messages.h"
  11. #include "../../../middleware/gameplay/peer/peer_container.h"
  12. #include "../endpoint/gameplay_endpoint.h"
  13. #include "../gameplay_log.h"
  14. #include "../group/group_host.h"
  15. #include "peer_transport_internal.h"
  16. namespace sunrise::server::gameplay::peer {
  17. namespace {
  18. namespace gp = state::gameplay;
  19. namespace wire = middleware::gameplay::peer;
  20. namespace bits = middleware::encoding::bits;
  21. /** The random connect sequence is folded low byte first. */
  22. constexpr unsigned kByteBits = 8;
  23. /** Sequence the first packet to a peer carries, because the head advances before it is written. */
  24. constexpr std::uint16_t kFirstPacketSequence = 1;
  25. /**
  26. * Fills the address blob that names this host on the direct path.
  27. * @param receivingPort Host pool port the request arrived on. Zero names the primary port, as it
  28. * does on the transport's send path.
  29. * @param output Receives the direct-path address blob.
  30. */
  31. void local_address(std::uint16_t receivingPort,
  32. std::array<std::byte, wire::kAddressBlobSize>& output) noexcept {
  33. const gp::Endpoint advertised = endpoint::advertised();
  34. middleware::gameplay::descriptor::write_direct_net_addr(
  35. advertised.address, receivingPort != 0 ? receivingPort : advertised.port, output);
  36. }
  37. /** @return A random 32-bit sequence, or zero when Windows refused. */
  38. [[nodiscard]] std::uint32_t random_sequence() noexcept {
  39. std::array<std::byte, sizeof(std::uint32_t)> bytes{};
  40. if (!middleware::crypto::random::fill(bytes)) {
  41. return 0;
  42. }
  43. std::uint32_t value = 0;
  44. for (std::size_t index = 0; index < bytes.size(); ++index) {
  45. value |= std::to_integer<std::uint32_t>(bytes[index]) << (index * kByteBits);
  46. }
  47. return value;
  48. }
  49. /**
  50. * Answers the peer's connect establish with this host's own.
  51. * It goes on the reliable queue because that is where the peer sends its own.
  52. * @param to Peer endpoint.
  53. * @param body Both channel ids.
  54. */
  55. void answer_establish(const gp::Endpoint& to, const wire::ConnectEstablish& body) noexcept {
  56. std::array<std::byte, kReplyCapacity> buffer{};
  57. bits::Writer writer(buffer);
  58. std::size_t size = 0;
  59. if (!wire::write_establish(writer, body) || !writer.finish(size)) {
  60. report(core::log::Level::warn, "ev=gameplay stage=establish result=fail reason=encode");
  61. return;
  62. }
  63. AcquireSRWLockExclusive(&g_lock);
  64. // The endpoint's link. A channel the peer has retired has no link of its own to answer on.
  65. gp::PeerLink* peer = find_locked(to);
  66. const bool queued =
  67. peer != nullptr
  68. && wire::enqueue_message(peer->outbound,
  69. static_cast<std::uint8_t>(wire::ConnectId::establish),
  70. wire::kEstablishSize,
  71. {buffer.data(), size},
  72. writer.bit_count());
  73. if (queued) {
  74. peer->acknowledgementOwed = true;
  75. peer->outbound.awaitingAcknowledgement = false;
  76. }
  77. ReleaseSRWLockExclusive(&g_lock);
  78. report(core::log::Level::info,
  79. "ev=gameplay stage=establish result=%s local=0x%08X remote=0x%08X",
  80. queued ? "queued" : "fail",
  81. body.channelId,
  82. body.remoteChannelId);
  83. }
  84. /**
  85. * Answers one connect request with a connect response.
  86. * @param from Requesting endpoint.
  87. * @param request Decoded request body.
  88. * @param now Monotonic tick count.
  89. */
  90. void answer_connect(const gp::Endpoint& from,
  91. const wire::ConnectRequest& request,
  92. std::uint64_t now) noexcept {
  93. wire::ConnectResponse response{};
  94. // The peer checks both echoed fields and closes the connection on a wrong sequence.
  95. response.remoteChannelId = request.channelId;
  96. response.remoteSequence = request.sequence;
  97. // The client locates its connecting channel by this address, so it must name the host pool
  98. // port the request reached rather than always the primary port.
  99. local_address(from.localPort, response.address);
  100. DisplacedExternals displaced{};
  101. std::size_t displacedCount = 0;
  102. std::array<std::uint64_t, gp::kSessionsPerLink> resetSessions{};
  103. std::size_t resetSessionCount = 0;
  104. gp::entity_identity::Source resetSource{};
  105. AcquireSRWLockExclusive(&g_lock);
  106. // Keyed by endpoint. The client holds one channel per host peer, so a second link would stamp
  107. // packets with a channel id the client has already retired.
  108. gp::PeerLink* peer = find_locked(from);
  109. // A repeat of the same request is a retransmission and leaves the link alone. A different
  110. // channel or sequence is a new incarnation the peer built without announcing the teardown.
  111. const bool rebuilt = peer != nullptr
  112. && (peer->remoteConnectionSequence != request.channelId
  113. || peer->remoteTransportSequence != request.sequence);
  114. if (peer == nullptr) {
  115. peer = allocate_locked();
  116. }
  117. const bool fresh = peer != nullptr && (peer->stage == gp::PeerStage::absent || rebuilt);
  118. if (fresh) {
  119. resetSource = entity_source(*peer);
  120. invalidate_entity_identity_locked(resetSource);
  121. // The sessions outlive the channel. The client rebuilds one channel under every group
  122. // session it holds and rejoins none of them, so dropping them here strands each one.
  123. const std::array<std::uint64_t, gp::kSessionsPerLink> held =
  124. peer->stage == gp::PeerStage::absent ? std::array<std::uint64_t, gp::kSessionsPerLink>{}
  125. : peer->sessions;
  126. for (const std::uint64_t sessionId : held) {
  127. if (sessionId != 0) {
  128. resetSessions[resetSessionCount++] = sessionId;
  129. }
  130. }
  131. displacedCount = collect_displaced_locked(*peer, displaced);
  132. *peer = {};
  133. ++g_peerGeneration;
  134. if (g_peerGeneration == 0) {
  135. ++g_peerGeneration;
  136. }
  137. peer->peerGeneration = g_peerGeneration;
  138. peer->channelGeneration = g_peerGeneration;
  139. peer->sessions = held;
  140. peer->endpoint = from;
  141. // The channel id is an incarnation counter: the peer refuses one that does not
  142. // increase, and reads all ones as unset.
  143. peer->localConnectionSequence = ++g_channelId;
  144. // The peer builds its receive window from the announced sequence and expects the first
  145. // packet one past it. This announces the sequence before the first packet, not the first
  146. // packet itself.
  147. peer->localTransportSequence =
  148. (random_sequence() & ~static_cast<std::uint32_t>(gp::kPacketSequenceModulus - 1))
  149. | static_cast<std::uint32_t>(kFirstPacketSequence - 1);
  150. }
  151. if (peer != nullptr) {
  152. peer->remoteConnectionSequence = request.channelId;
  153. peer->remoteTransportSequence = request.sequence;
  154. // The membership update must name the peer's own address, so its own blob is kept.
  155. peer->remoteAddress = request.address;
  156. peer->remoteAddressPresent = true;
  157. // A retransmission must not move an established link back a stage.
  158. if (fresh) {
  159. peer->stage = gp::PeerStage::connecting;
  160. }
  161. peer->lastTick = now;
  162. response.channelId = peer->localConnectionSequence;
  163. response.sequence = peer->localTransportSequence;
  164. }
  165. ReleaseSRWLockExclusive(&g_lock);
  166. notify_external_outcomes(displaced, displacedCount);
  167. reset_transports(resetSessions.data(), resetSessionCount);
  168. reset_entity_source(resetSource);
  169. if (peer == nullptr) {
  170. report(core::log::Level::warn, "ev=gameplay stage=connect result=fail reason=capacity");
  171. return;
  172. }
  173. std::array<std::byte, kReplyCapacity> buffer{};
  174. bits::Writer writer(buffer);
  175. wire::MessageHeader header{static_cast<std::uint8_t>(wire::ConnectId::response),
  176. wire::kResponseSize};
  177. std::size_t size = 0;
  178. if (!wire::open_container(writer) || !wire::write_header(writer, header)
  179. || !wire::write_response(writer, response) || !wire::close_container(writer)
  180. || !writer.finish(size) || !send_transport(from, {buffer.data(), size})) {
  181. report(core::log::Level::warn, "ev=gameplay stage=connect result=fail reason=send");
  182. return;
  183. }
  184. // A rebuilt link is invisible otherwise: the peer closes the old one silently.
  185. report(core::log::Level::info,
  186. "ev=gameplay stage=connect result=ok peer=%u local=0x%08X remote=0x%08X rebuilt=%u",
  187. from.port,
  188. response.channelId,
  189. request.channelId,
  190. rebuilt ? 1U : 0U);
  191. // The peer refuses any first reliable record that is not a connect establish, so this must be
  192. // enqueued before anything else the join produces.
  193. wire::ConnectEstablish establish{};
  194. establish.remoteChannelId = response.remoteChannelId;
  195. establish.channelId = response.channelId;
  196. answer_establish(from, establish);
  197. }
  198. /**
  199. * Binds one group session to the link the peer opened for it.
  200. * @param from Peer endpoint.
  201. * @param sessionId Session the join request named.
  202. * @return True when a link now carries that session.
  203. */
  204. [[nodiscard]] bool bind_session(const gp::Endpoint& from, std::uint64_t sessionId) noexcept {
  205. if (sessionId == 0) {
  206. return false;
  207. }
  208. AcquireSRWLockExclusive(&g_lock);
  209. // The endpoint's link, whatever it already carries. A join for a second region arrives on the
  210. // same channel as the first, and out of band when that channel is still being rebuilt.
  211. gp::PeerLink* const peer = find_locked(from);
  212. const char* result = "nolink";
  213. bool bound = false;
  214. std::uint32_t channel = 0;
  215. if (peer != nullptr) {
  216. channel = peer->localConnectionSequence;
  217. result = "full";
  218. for (std::uint64_t& slot : peer->sessions) {
  219. if (slot == sessionId) {
  220. result = "held";
  221. bound = true;
  222. break;
  223. }
  224. if (slot == 0) {
  225. slot = sessionId;
  226. result = "bound";
  227. bound = true;
  228. break;
  229. }
  230. }
  231. }
  232. ReleaseSRWLockExclusive(&g_lock);
  233. report(bound ? core::log::Level::info : core::log::Level::warn,
  234. "ev=gameplay stage=link result=%s session=0x%016llX peer=%u local=0x%08X",
  235. result,
  236. static_cast<unsigned long long>(sessionId),
  237. from.port,
  238. channel);
  239. return bound;
  240. }
  241. /**
  242. * Picks the joining peer's own machine id out of its request's peer table.
  243. * The row is the one whose address is the NetAddr this link's connect request carried. A single
  244. * row needs no match.
  245. * @param from Peer endpoint.
  246. * @param request Decoded join request.
  247. * @return The machine id, or zero when no row names this link.
  248. */
  249. [[nodiscard]] std::uint64_t joining_machine_id(const gp::Endpoint& from,
  250. const wire::JoinRequest& request) noexcept {
  251. std::array<std::byte, gp::kNetAddrBlobSize> address{};
  252. bool present = false;
  253. AcquireSRWLockShared(&g_lock);
  254. const gp::PeerLink* peer = find_locked(from);
  255. if (peer != nullptr && peer->remoteAddressPresent) {
  256. address = peer->remoteAddress;
  257. present = true;
  258. }
  259. ReleaseSRWLockShared(&g_lock);
  260. for (std::size_t index = 0; present && index < request.peerCount; ++index) {
  261. if (request.peers[index].address == address) {
  262. return request.peers[index].machineId;
  263. }
  264. }
  265. return request.peerCount == 1 ? request.peers[0].machineId : 0;
  266. }
  267. /**
  268. * Admits or refuses one join request. An admitted join binds the session to the link, then
  269. * publishes the membership snapshot and the join parameters the peer waits on.
  270. * @param from Peer endpoint.
  271. * @param request Decoded join request.
  272. */
  273. void answer_join(const gp::Endpoint& from, const wire::JoinRequest& request) noexcept {
  274. const std::uint64_t hostSession = endpoint::identity().onlineSessionId;
  275. wire::RefuseReason reason = wire::RefuseReason::notFound;
  276. if (wire::admit(request, hostSession, reason)) {
  277. // The join is the first thing on this link that names the session. A link already
  278. // carrying it is a retry.
  279. const bool bound = bind_session(from, request.sessionId);
  280. const std::uint64_t machineId = joining_machine_id(from, request);
  281. const bool published =
  282. bound && group::publish_membership(from, request.joinId, machineId, request.sessionId);
  283. // The peer needs both before it finishes: the snapshot names it, and the parameter update
  284. // releases the latch its own tick waits on.
  285. const bool parameters = bound && group::publish_join_parameters(request.sessionId);
  286. // Nothing else names what the peer thinks it is joining.
  287. report(core::log::Level::info,
  288. "ev=gameplay stage=join result=admit build=%u..%u exe=%u session=0x%016llX "
  289. "host=0x%016llX join=0x%016llX machine=0x%016llX peers=%u membership=%s "
  290. "parameters=%s",
  291. request.minimumBuild,
  292. request.maximumBuild,
  293. static_cast<unsigned>(request.executableType),
  294. static_cast<unsigned long long>(request.sessionId),
  295. static_cast<unsigned long long>(hostSession),
  296. static_cast<unsigned long long>(request.joinId),
  297. static_cast<unsigned long long>(machineId),
  298. request.peerCount,
  299. published ? "queued" : "fail",
  300. parameters ? "queued" : "fail");
  301. return;
  302. }
  303. if (!wire::answerable(request)) {
  304. report(core::log::Level::warn,
  305. "ev=gameplay stage=join result=drop reason=protocol value=0x%04X",
  306. static_cast<unsigned>(request.protocolVersion));
  307. return;
  308. }
  309. report(core::log::Level::warn,
  310. "ev=gameplay stage=join result=refuse reason=%u build=%u..%u exe=%u session=0x%016llX "
  311. "host=0x%016llX",
  312. static_cast<unsigned>(reason),
  313. request.minimumBuild,
  314. request.maximumBuild,
  315. static_cast<unsigned>(request.executableType),
  316. static_cast<unsigned long long>(request.sessionId),
  317. static_cast<unsigned long long>(hostSession));
  318. wire::JoinRefuse refusal{};
  319. refusal.sessionId = request.sessionId;
  320. refusal.joinId = request.joinId;
  321. refusal.reason = reason;
  322. std::array<std::byte, kReplyCapacity> buffer{};
  323. bits::Writer writer(buffer);
  324. const wire::MessageHeader header{static_cast<std::uint8_t>(wire::JoinId::refuse),
  325. wire::kJoinRefuseSize};
  326. std::size_t size = 0;
  327. if (!wire::open_container(writer) || !wire::write_header(writer, header)
  328. || !wire::write_join_refuse(writer, refusal) || !wire::close_container(writer)
  329. || !writer.finish(size) || !send_transport(from, {buffer.data(), size})) {
  330. report(core::log::Level::warn, "ev=gameplay stage=join result=fail reason=send");
  331. return;
  332. }
  333. report(core::log::Level::info,
  334. "ev=gameplay stage=join result=refuse reason=%u",
  335. static_cast<unsigned>(refusal.reason));
  336. }
  337. /**
  338. * Answers one ping with the pong that echoes it.
  339. * The pair is mandatory: a peer that pings and is never answered treats the link as unreachable.
  340. * @param from Peer endpoint.
  341. * @param reader Reader positioned at the ping body.
  342. * @return True when the body read, whether or not the reply left the endpoint.
  343. */
  344. [[nodiscard]] bool answer_ping(const gp::Endpoint& from, bits::Reader& reader) noexcept {
  345. wire::PingBody ping{};
  346. if (!wire::read_ping(reader, ping)) {
  347. return false;
  348. }
  349. wire::PongBody pong{};
  350. pong.sequence = ping.sequence;
  351. pong.timestamp = ping.timestamp;
  352. const bool sent = send_out_of_band(
  353. from,
  354. static_cast<std::uint8_t>(wire::ConnectId::pong),
  355. wire::kPongSize,
  356. [&pong](bits::Writer& writer) noexcept { return wire::write_pong(writer, pong); });
  357. report(sent ? core::log::Level::debug : core::log::Level::warn,
  358. "ev=gameplay stage=ping result=%s sequence=%u",
  359. sent ? "answered" : "fail",
  360. static_cast<unsigned>(ping.sequence));
  361. return true;
  362. }
  363. } // namespace
  364. /** Sends one already-encoded out-of-band body in its own container. */
  365. bool send_container(const gp::Endpoint& to,
  366. std::uint8_t id,
  367. std::uint32_t declaredSize,
  368. std::span<const std::byte> body,
  369. std::size_t bodyBits) noexcept {
  370. std::array<std::byte, kReplyCapacity> buffer{};
  371. bits::Writer writer(buffer);
  372. const wire::MessageHeader header{id, declaredSize};
  373. if (!wire::open_container(writer) || !wire::write_header(writer, header)) {
  374. return false;
  375. }
  376. bits::Reader reader(body);
  377. if (!bits::copy(reader, writer, bodyBits)) {
  378. return false;
  379. }
  380. std::size_t size = 0;
  381. if (!wire::close_container(writer) || !writer.finish(size)) {
  382. return false;
  383. }
  384. return send_transport(to, {buffer.data(), size});
  385. }
  386. /** Consumes one out-of-band message container. */
  387. void consume_container(const gp::Endpoint& from,
  388. std::span<const std::byte> payload,
  389. std::uint64_t now) noexcept {
  390. bits::Reader reader(payload);
  391. if (!wire::read_marker(reader)) {
  392. return;
  393. }
  394. for (;;) {
  395. wire::MessageHeader header{};
  396. bool present = false;
  397. if (!wire::read_header(reader, header, present)) {
  398. report(core::log::Level::debug, "ev=gameplay stage=oob result=drop reason=header");
  399. return;
  400. }
  401. if (!present) {
  402. return;
  403. }
  404. if (header.id == static_cast<std::uint8_t>(wire::ConnectId::ping)) {
  405. if (!answer_ping(from, reader)) {
  406. return;
  407. }
  408. continue;
  409. }
  410. if (header.id == static_cast<std::uint8_t>(wire::ConnectId::packetsDiscarded)) {
  411. std::uint8_t discarded = 0;
  412. if (!wire::read_packets_discarded(reader, discarded)) {
  413. return;
  414. }
  415. report(core::log::Level::debug,
  416. "ev=gameplay stage=discarded result=read packets=%u",
  417. static_cast<unsigned>(discarded));
  418. continue;
  419. }
  420. if (header.id == static_cast<std::uint8_t>(wire::ConnectId::mayday)) {
  421. wire::MaydayBody mayday{};
  422. if (!wire::read_mayday(reader, mayday)) {
  423. return;
  424. }
  425. report(core::log::Level::warn,
  426. "ev=gameplay stage=mayday result=read session=0x%016llX code=%d",
  427. static_cast<unsigned long long>(mayday.sessionId),
  428. static_cast<int>(mayday.code));
  429. continue;
  430. }
  431. if (header.id == static_cast<std::uint8_t>(wire::ConnectId::request)) {
  432. wire::ConnectRequest request{};
  433. if (!wire::read_request(reader, request)) {
  434. return;
  435. }
  436. answer_connect(from, request, now);
  437. continue;
  438. }
  439. if (header.id == static_cast<std::uint8_t>(wire::JoinId::request)) {
  440. wire::JoinRequest request{};
  441. if (wire::read_join_request(reader, request)) {
  442. answer_join(from, request);
  443. }
  444. // The member table and tail behind the peer table are not decoded, so no later
  445. // message in this container can be located.
  446. return;
  447. }
  448. if (header.id == static_cast<std::uint8_t>(wire::ConnectId::closed)) {
  449. wire::ConnectEnd closed{};
  450. if (!wire::read_closed(reader, closed)) {
  451. return;
  452. }
  453. // The link goes, the sessions stay. The client rebuilds the channel and rejoins none
  454. // of them, so releasing their activity host sessions here strands every one.
  455. drop_endpoint(from);
  456. report(core::log::Level::info,
  457. "ev=gameplay stage=peer result=closed reason=%u",
  458. static_cast<unsigned>(closed.reason));
  459. return;
  460. }
  461. if (group::consume(from, header.id, reader, now)) {
  462. continue;
  463. }
  464. // A message this host does not decode ends the chain: its body width is unknown, so
  465. // every message behind it would be read at the wrong offset.
  466. report(core::log::Level::debug, "ev=gameplay stage=oob result=stop id=%u", header.id);
  467. return;
  468. }
  469. }
  470. } // namespace sunrise::server::gameplay::peer