Esempio n. 1
0
#pragma warning disable VSTHRD100 // Avoid async void methods
        /// <summary>
        ///   Called when a remote peer is connecting to the local peer.
        /// </summary>
        /// <param name="stream">
        ///   The stream to the remote peer.
        /// </param>
        /// <param name="local">
        ///   The local peer's address.
        /// </param>
        /// <param name="remote">
        ///   The remote peer's address.
        /// </param>
        /// <remarks>
        ///   Establishes the protocols of the connection.  Any exception is simply
        ///   logged as warning.
        /// </remarks>
        async void OnRemoteConnect(Stream stream, MultiAddress local, MultiAddress remote)
#pragma warning restore VSTHRD100 // Avoid async void methods
        {
            if (!IsRunning)
            {
                try
                {
                    stream.Dispose();
                }
                catch (Exception)
                {
                    // eat it.
                }
                return;
            }

            // If the remote is already trying to establish a connection, then we
            // can just refuse this one.
            if (!pendingRemoteConnections.TryAdd(remote, null))
            {
                log.Debug($"Duplicate remote connection from {remote}");
                try
                {
                    stream.Dispose();
                }
                catch (Exception)
                {
                    // eat it.
                }
                return;
            }

            try
            {
                if (log.IsDebugEnabled)
                {
                    log.Debug($"remote connect from {remote}");
                }

                // TODO: Check the policies

                var connection = new PeerConnection
                {
                    IsIncoming    = true,
                    LocalPeer     = LocalPeer,
                    LocalAddress  = local,
                    LocalPeerKey  = LocalPeerKey,
                    RemoteAddress = remote,
                    Stream        = stream
                };

                // Are we communicating to a private network?
                if (NetworkProtector != null)
                {
                    connection.Stream = await NetworkProtector.ProtectAsync(connection).ConfigureAwait(false);
                }

                // Mount the protocols.
                MountProtocols(connection);

                // Start the handshake
                // TODO: Isn't connection cancel token required.
                _ = connection.ReadMessagesAsync(default(CancellationToken));

                // Wait for security to be established.
                await connection.SecurityEstablished.Task.ConfigureAwait(false);

                // TODO: Maybe connection.LocalPeerKey = null;

                // Wait for the handshake to complete.
                var muxer = await connection.MuxerEstablished.Task;

                // Need details on the remote peer.
                Identify1 identify = null;
                lock (protocols)
                {
                    identify = protocols.OfType <Identify1>().First();
                }
                connection.RemotePeer = await identify.GetRemotePeerAsync(connection, default(CancellationToken)).ConfigureAwait(false);

                connection.RemotePeer    = RegisterPeer(connection.RemotePeer);
                connection.RemoteAddress = new MultiAddress($"{remote}/ipfs/{connection.RemotePeer.Id}");
                var actual = Manager.Add(connection);
                if (actual == connection)
                {
                    ConnectionEstablished?.Invoke(this, connection);
                }
            }
            catch (Exception e)
            {
                if (log.IsDebugEnabled)
                {
                    log.Debug($"remote connect from {remote} failed: {e.Message}");
                }
                try
                {
                    stream.Dispose();
                }
                catch (Exception)
                {
                    // eat it.
                }
            }
            finally
            {
                pendingRemoteConnections.TryRemove(remote, out object _);
            }
        }
Esempio n. 2
0
        /// <summary>
        ///   Establish a duplex stream between the local and remote peer.
        /// </summary>
        /// <param name="remote"></param>
        /// <param name="addrs"></param>
        /// <param name="cancel"></param>
        /// <returns></returns>
        async Task <PeerConnection> DialAsync(Peer remote, IEnumerable <MultiAddress> addrs, CancellationToken cancel)
        {
            log.Debug($"Dialing {remote}");

            if (remote == LocalPeer)
            {
                throw new Exception("Cannot dial self.");
            }

            // If no addresses, then ask peer routing.
            if (Router != null && addrs.Count() == 0)
            {
                var found = await Router.FindPeerAsync(remote.Id, cancel).ConfigureAwait(false);

                addrs            = found.Addresses;
                remote.Addresses = addrs;
            }

            // Get the addresses we can use to dial the remote.  Filter
            // out any addresses (ip and port) we are listening on.
            var blackList = listeners.Keys
                            .Select(a => a.WithoutPeerId())
                            .ToArray();
            var possibleAddresses = (await Task.WhenAll(addrs.Select(a => a.ResolveAsync(cancel))).ConfigureAwait(false))
                                    .SelectMany(a => a)
                                    .Where(a => !blackList.Contains(a.WithoutPeerId()))
                                    .Select(a => a.WithPeerId(remote.Id))
                                    .Distinct()
                                    .ToArray();

            if (possibleAddresses.Length == 0)
            {
                throw new Exception($"{remote} has no known or reachable address.");
            }

            // Try the various addresses in parallel.  The first one to complete wins.
            PeerConnection connection = null;

            try
            {
                using (var timeout = new CancellationTokenSource(TransportConnectionTimeout))
                    using (var cts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancel))
                    {
                        var attempts = possibleAddresses
                                       .Select(a => DialAsync(remote, a, cts.Token));
                        connection = await TaskHelper.WhenAnyResultAsync(attempts, cts.Token).ConfigureAwait(false);

                        cts.Cancel(); // stop other dialing tasks.
                    }
            }
            catch (Exception e)
            {
                var attemped = string.Join(", ", possibleAddresses.Select(a => a.ToString()));
                log.Trace($"Cannot dial {attemped}");
                throw new Exception($"Cannot dial {remote}.", e);
            }

            // Do the connection handshake.
            try
            {
                MountProtocols(connection);
                IEncryptionProtocol[] security = null;
                lock (protocols)
                {
                    security = protocols.OfType <IEncryptionProtocol>().ToArray();
                }
                await connection.InitiateAsync(security, cancel).ConfigureAwait(false);

                await connection.MuxerEstablished.Task.ConfigureAwait(false);

                Identify1 identify = null;
                lock (protocols)
                {
                    identify = protocols.OfType <Identify1>().First();
                }
                await identify.GetRemotePeerAsync(connection, cancel).ConfigureAwait(false);
            }
            catch (Exception)
            {
                connection.Dispose();
                throw;
            }

            var actual = Manager.Add(connection);

            if (actual == connection)
            {
                ConnectionEstablished?.Invoke(this, connection);
            }

            return(actual);
        }