コード例 #1
0
        /// <summary>
        /// Sends "version" message to the peer and waits for the response in form of "verack" or "reject" message.
        /// </summary>
        /// <param name="cancellationToken">Cancellation that allows aborting the operation at any stage.</param>
        /// <exception cref="ProtocolException">Thrown when the peer rejected our "version" message.</exception>
        public async Task RespondToHandShakeAsync(CancellationToken cancellation = default(CancellationToken))
        {
            this.logger.LogTrace("()");

            using (var listener = new NetworkPeerListener(this).Where(m => (m.Message.Payload is VerAckPayload) || (m.Message.Payload is RejectPayload)))
            {
                this.logger.LogTrace("Responding to handshake.");
                await this.SendMessageAsync(this.MyVersion);

                IncomingMessage message = listener.ReceiveMessage(cancellation);

                if (message.Message.Payload is RejectPayload reject)
                {
                    this.logger.LogTrace("Version rejected: code {0}, reason {1}.", reject.Code, reject.Reason);
                    this.logger.LogTrace("(-)[VERSION_REJECTED]");
                    throw new ProtocolException("Version rejected " + reject.Code + " : " + reject.Reason);
                }

                await this.SendMessageAsync(new VerAckPayload());

                this.State = NetworkPeerState.HandShaked;
            }

            this.logger.LogTrace("(-)");
        }
コード例 #2
0
        /// <inheritdoc/>
        public async Task RespondToHandShakeAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            this.logger.LogTrace("()");

            using (var listener = new NetworkPeerListener(this))
            {
                this.logger.LogTrace("Responding to handshake with my version.");
                await this.SendMessageAsync(this.MyVersion, cancellationToken).ConfigureAwait(false);

                this.logger.LogTrace("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.LogTrace("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.LogTrace("Sending version acknowledgement.");
                        await this.SendMessageAsync(new VerAckPayload(), cancellationToken).ConfigureAwait(false);

                        await this.SetStateAsync(NetworkPeerState.HandShaked).ConfigureAwait(false);

                        break;
                    }
                }
            }

            this.logger.LogTrace("(-)");
        }
コード例 #3
0
        /// <summary>
        /// Exchanges "version" and "verack" messages with the peer.
        /// <para>Both parties have to send their "version" messages to the other party
        /// as well as to acknowledge that they are happy with the other party's "version" information.</para>
        /// </summary>
        /// <param name="requirements">Protocol requirement for network peers the node wants to be connected to.</param>
        /// <param name="cancellationToken">Cancellation that allows aborting the operation at any stage.</param>
        public async Task VersionHandshakeAsync(NetworkPeerRequirement requirements, CancellationToken cancellationToken = default(CancellationToken))
        {
            this.logger.LogTrace("({0}.{1}:{2})", nameof(requirements), nameof(requirements.RequiredServices), requirements?.RequiredServices);

            requirements = requirements ?? new NetworkPeerRequirement();
            using (var listener = new NetworkPeerListener(this).Where(p => (p.Message.Payload is VersionPayload) ||
                                                                      (p.Message.Payload is RejectPayload) ||
                                                                      (p.Message.Payload is VerAckPayload)))
            {
                await this.SendMessageAsync(this.MyVersion).ConfigureAwait(false);

                Payload payload = listener.ReceivePayload <Payload>(cancellationToken);
                if (payload is RejectPayload)
                {
                    this.logger.LogTrace("(-)[HANDSHAKE_REJECTED]");
                    throw new ProtocolException("Handshake rejected: " + ((RejectPayload)payload).Reason);
                }

                var version = (VersionPayload)payload;
                this.PeerVersion = version;
                if (!version.AddressReceiver.Address.Equals(this.MyVersion.AddressFrom.Address))
                {
                    this.logger.LogDebug("Different external address detected by the node '{0}' instead of '{1}'.", version.AddressReceiver.Address, this.MyVersion.AddressFrom.Address);
                }

                if (version.Version < ProtocolVersion.MIN_PEER_PROTO_VERSION)
                {
                    this.logger.LogDebug("Outdated version {0} received, disconnecting peer.", version.Version);

                    this.Disconnect("Outdated version");
                    this.logger.LogTrace("(-)[OUTDATED]");
                    return;
                }

                if (!requirements.Check(version))
                {
                    this.logger.LogTrace("(-)[UNSUPPORTED_REQUIREMENTS]");
                    this.Disconnect("The peer does not support the required services requirement");
                    return;
                }

                await this.SendMessageAsync(new VerAckPayload()).ConfigureAwait(false);

                listener.ReceivePayload <VerAckPayload>(cancellationToken);
                this.State = NetworkPeerState.HandShaked;

                if (this.Advertize && this.MyVersion.AddressFrom.Address.IsRoutable(true))
                {
                    this.SendMessageVoidAsync(new AddrPayload(new NetworkAddress(this.MyVersion.AddressFrom)
                    {
                        Time = this.dateTimeProvider.GetTimeOffset()
                    }));
                }
            }

            this.logger.LogTrace("(-)");
        }
コード例 #4
0
        /// <summary>
        /// Waits for a message of specific type to be received from the peer.
        /// </summary>
        /// <typeparam name="TPayload">Type of message to wait for.</typeparam>
        /// <param name="cancellationToken">Cancellation that allows aborting the operation.</param>
        /// <returns>Received message.</returns>
        /// <exception cref="OperationCanceledException">Thrown if the cancellation token was cancelled.</exception>
        public TPayload ReceiveMessage <TPayload>(CancellationToken cancellationToken = default(CancellationToken)) where TPayload : Payload
        {
            this.logger.LogTrace("()");

            using (var listener = new NetworkPeerListener(this))
            {
                TPayload res = listener.ReceivePayload <TPayload>(cancellationToken);

                this.logger.LogTrace("(-):'{0}'", res);
                return(res);
            }
        }
コード例 #5
0
        /// <inheritdoc/>
        public async Task VersionHandshakeAsync(NetworkPeerRequirement requirements, CancellationToken cancellationToken)
        {
            this.logger.LogTrace("({0}.{1}:{2})", nameof(requirements), nameof(requirements.RequiredServices), requirements?.RequiredServices);

            requirements = requirements ?? new NetworkPeerRequirement();
            using (var listener = new NetworkPeerListener(this))
            {
                this.logger.LogTrace("Sending my version.");
                await this.SendMessageAsync(this.MyVersion, cancellationToken).ConfigureAwait(false);

                this.logger.LogTrace("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.Equals(this.MyVersion.AddressFrom.Address))
                        {
                            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.logger.LogTrace("(-)[UNSUPPORTED_REQUIREMENTS]");
                            this.Disconnect("The peer does not support the required services requirement");
                            return;
                        }

                        this.logger.LogTrace("Sending version acknowledgement.");
                        await this.SendMessageAsync(new VerAckPayload(), cancellationToken).ConfigureAwait(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);
                }
            }

            this.logger.LogTrace("(-)");
        }
コード例 #6
0
        /// <inheritdoc/>
        public async Task VersionHandshakeAsync(NetworkPeerRequirement requirements, CancellationToken cancellationToken)
        {
            // In stratisX, the equivalent functionality is contained in main.cpp, method ProcessMessage()

            requirements = requirements ?? new NetworkPeerRequirement();
            using (var listener = new NetworkPeerListener(this, this.asyncProvider))
            {
                this.logger.LogTrace("Sending my version.");
                await this.SendMessageAsync(this.MyVersion, cancellationToken).ConfigureAwait(false);

                this.logger.LogTrace("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.Equals(this.MyVersion.AddressFrom.Address))
                        {
                            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.LogTrace("Sending version acknowledgement.");
                        await this.SendMessageAsync(new VerAckPayload(), cancellationToken).ConfigureAwait(false);

                        this.selfEndpointTracker.UpdateAndAssignMyExternalAddress(versionPayload.AddressFrom, 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);
            }
        }
コード例 #7
0
        /// <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();
            }
        }