/// <inheritdoc/> public async Task RespondToHandShakeAsync(CancellationToken cancellationToken = default(CancellationToken)) { using (var listener = new NetworkPeerListener(this, this.asyncProvider)) { this.logger.LogDebug("Responding to handshake with my version."); await this.SendMessageAsync(this.MyVersion, cancellationToken).ConfigureAwait(false); this.logger.LogDebug("Waiting for version acknowledgement or rejection message."); while (this.State != NetworkPeerState.HandShaked) { Payload payload = await listener.ReceivePayloadAsync <Payload>(cancellationToken).ConfigureAwait(false); switch (payload) { case RejectPayload rejectPayload: this.logger.LogDebug("Version rejected: code {0}, reason '{1}'.", rejectPayload.Code, rejectPayload.Reason); this.logger.LogTrace("(-)[VERSION_REJECTED]"); throw new ProtocolException("Version rejected " + rejectPayload.Code + ": " + rejectPayload.Reason); case VerAckPayload verAckPayload: this.logger.LogDebug("Sending version acknowledgement."); await this.SendMessageAsync(new VerAckPayload(), cancellationToken).ConfigureAwait(false); await this.SetStateAsync(NetworkPeerState.HandShaked).ConfigureAwait(false); break; } } } }
/// <inheritdoc/> public async Task VersionHandshakeAsync(NetworkPeerRequirement requirements, CancellationToken cancellationToken) { // Note that this method gets called for outbound peers. When our peer is inbound we receive the initial version handshake from the initiating peer, and it is handled via this.ProcessMessageAsync() only. // In stratisX, the equivalent functionality is contained in main.cpp, method ProcessMessage() requirements = requirements ?? new NetworkPeerRequirement(); NetworkPeerListener listener = null; try { listener = new NetworkPeerListener(this, this.asyncProvider); this.logger.LogDebug("Sending my version."); await this.SendMessageAsync(this.MyVersion, cancellationToken).ConfigureAwait(false); this.logger.LogDebug("Waiting for version or rejection message."); bool versionReceived = false; bool verAckReceived = false; while (!versionReceived || !verAckReceived) { Payload payload = await listener.ReceivePayloadAsync <Payload>(cancellationToken).ConfigureAwait(false); switch (payload) { case RejectPayload rejectPayload: this.logger.LogTrace("(-)[HANDSHAKE_REJECTED]"); throw new ProtocolException("Handshake rejected: " + rejectPayload.Reason); case VersionPayload versionPayload: versionReceived = true; this.PeerVersion = versionPayload; if (!versionPayload.AddressReceiver.Address.MapToIPv6().Equals(this.MyVersion.AddressFrom.Address.MapToIPv6())) { this.logger.LogDebug("Different external address detected by the node '{0}' instead of '{1}'.", versionPayload.AddressReceiver.Address, this.MyVersion.AddressFrom.Address); } if (versionPayload.Version < ProtocolVersion.MIN_PEER_PROTO_VERSION) { this.logger.LogDebug("Outdated version {0} received, disconnecting peer.", versionPayload.Version); this.Disconnect("Outdated version"); this.logger.LogTrace("(-)[OUTDATED]"); return; } if (!requirements.Check(versionPayload, this.Inbound, out string reason)) { this.logger.LogTrace("(-)[UNSUPPORTED_REQUIREMENTS]"); this.Disconnect("The peer does not support the required services requirement, reason: " + reason); return; } this.logger.LogDebug("Sending version acknowledgement."); await this.SendMessageAsync(new VerAckPayload(), cancellationToken).ConfigureAwait(false); // Note that we only update our external address data from information returned by outbound peers. // TODO: Is this due to a security assumption or is it an oversight? There is a higher risk the inbounds could be spoofing what they claim our external IP is. We would then use it in future version payloads, so that could be considered an attack. // For outbounds: AddressFrom is our current external endpoint from our perspective, and could easily be incorrect if it has been automatically detected from local NICs. // Whereas AddressReceiver is the endpoint from the peer's perspective, so we update our view using that. this.selfEndpointTracker.UpdateAndAssignMyExternalAddress(versionPayload.AddressReceiver, false); break; case VerAckPayload verAckPayload: verAckReceived = true; break; } } await this.SetStateAsync(NetworkPeerState.HandShaked).ConfigureAwait(false); if (this.advertize && this.MyVersion.AddressFrom.Address.IsRoutable(true)) { var addrPayload = new AddrPayload ( new NetworkAddress(this.MyVersion.AddressFrom) { Time = this.dateTimeProvider.GetTimeOffset() } ); await this.SendMessageAsync(addrPayload, cancellationToken).ConfigureAwait(false); } // Ask the just-handshaked peer for the peers they know about to aid in our own peer discovery. await this.SendMessageAsync(new GetAddrPayload(), cancellationToken).ConfigureAwait(false); } catch { throw; } finally { listener?.Dispose(); } }