Ejemplo n.º 1
0
        /// <param name="identity">Isolates streams by identity.</param>
        public async Task <TorSocks5Client> EstablishTcpConnectionAsync(IPEndPoint destination, string identity, CancellationToken cancel = default)
        {
            identity = Guard.NotNullOrEmptyOrWhitespace(nameof(identity), identity, trim: true);
            Guard.NotNull(nameof(destination), destination);

            var client = new TorSocks5Client(TorSocks5EndPoint);

            try
            {
                await client.ConnectAsync().ConfigureAwait(false);

                cancel.ThrowIfCancellationRequested();
                await client.HandshakeAsync(identity).ConfigureAwait(false);

                cancel.ThrowIfCancellationRequested();
                await client.ConnectToDestinationAsync(destination).ConfigureAwait(false);

                cancel.ThrowIfCancellationRequested();
                return(client);
            }
            catch (OperationCanceledException)
            {
                client?.Dispose();
                throw;
            }
        }
Ejemplo n.º 2
0
        public async Task <TorSocks5Client> EstablishTcpConnectionAsync(string host, int port, bool isolateStream = true, CancellationToken cancel = default)
        {
            host = Guard.NotNullOrEmptyOrWhitespace(nameof(host), host, true);
            Guard.MinimumAndNotNull(nameof(port), port, 0);

            var client = new TorSocks5Client(TorSocks5EndPoint);

            try
            {
                await client.ConnectAsync().ConfigureAwait(false);

                cancel.ThrowIfCancellationRequested();
                await client.HandshakeAsync(isolateStream).ConfigureAwait(false);

                cancel.ThrowIfCancellationRequested();
                await client.ConnectToDestinationAsync(host, port).ConfigureAwait(false);

                cancel.ThrowIfCancellationRequested();
                return(client);
            }
            catch (OperationCanceledException)
            {
                client?.Dispose();
                throw;
            }
        }
Ejemplo n.º 3
0
        protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancel)
        {
            Guard.NotNull(nameof(request), request);
            if (cancel == null)
            {
                cancel = CancellationToken.None;
            }
            // https://tools.ietf.org/html/rfc7230#section-2.7.1
            // A sender MUST NOT generate an "http" URI with an empty host identifier.
            var host = Guard.NotNullOrEmptyOrWhitespace($"{nameof(request)}.{nameof(request.RequestUri)}.{nameof(request.RequestUri.DnsSafeHost)}", request.RequestUri.DnsSafeHost, trim: true);

            using (await DisposeRequestLock.ReaderLockAsync(cancel).ConfigureAwait(false))
                using (var connectLockTask = await ConnectLock.LockAsync(cancel).ConfigureAwait(false))         // this makes sure clients with the same host don't try to connect concurrently, it gets released after connection established
                {
                    KeyValuePair <TorSocks5Client, AsyncLock> clientLockPair = TryFindClientLockPair(host, request.RequestUri.Port);
                    AsyncLock clientLock = clientLockPair.Value ?? new AsyncLock();             // this makes sure clients with the same host don't work concurrently
                    using (await clientLock.LockAsync(cancel).ConfigureAwait(false))
                    {
                        TorSocks5Client client = await SendAsync(request, host, connectLockTask, clientLockPair, clientLock, cancel);

                        try
                        {
                            return(await new HttpResponseMessage().CreateNewAsync(client.Stream, request.Method).ConfigureAwait(false));
                        }
                        catch (IOException)
                        {
                            // the connection is lost, reconnect
                            client = await SendAsync(request, host, connectLockTask, clientLockPair, clientLock, cancel);

                            return(await new HttpResponseMessage().CreateNewAsync(client.Stream, request.Method).ConfigureAwait(false));
                        }
                    }
                }
        }
Ejemplo n.º 4
0
        public async Task <TorSocks5Client> EstablishTcpConnectionAsync(IPEndPoint destination, bool isolateStream = true, CancellationToken cancel = default)
        {
            Guard.NotNull(nameof(destination), destination);

            var client = new TorSocks5Client(TorSocks5EndPoint);

            try
            {
                await client.ConnectAsync().ConfigureAwait(false);

                cancel.ThrowIfCancellationRequested();
                await client.HandshakeAsync(isolateStream).ConfigureAwait(false);

                cancel.ThrowIfCancellationRequested();
                await client.ConnectToDestinationAsync(destination).ConfigureAwait(false);

                cancel.ThrowIfCancellationRequested();
                return(client);
            }
            catch (OperationCanceledException)
            {
                client?.Dispose();
                throw;
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Tor attempts to find the canonical hostname for that IPv4 record
        /// </summary>
        public async Task <string> ReverseResolveAsync(IPAddress iPv4, bool isolateStream = true)
        {
            Guard.NotNull(nameof(iPv4), iPv4);

            using (var client = new TorSocks5Client(TorSocks5EndPoint))
            {
                await client.ConnectAsync().ConfigureAwait(false);

                await client.HandshakeAsync(isolateStream).ConfigureAwait(false);

                return(await client.ReverseResolveAsync(iPv4).ConfigureAwait(false));
            }
        }
Ejemplo n.º 6
0
        /// <summary>
        /// When Tor receives a "RESOLVE" SOCKS command, it initiates
        /// a remote lookup of the hostname provided as the target address in the SOCKS
        /// request.
        /// </summary>
        public async Task <IPAddress> ResolveAsync(string host, bool isolateStream = true)
        {
            host = Guard.NotNullOrEmptyOrWhitespace(nameof(host), host, true);

            using (var client = new TorSocks5Client(TorSocks5EndPoint))
            {
                await client.ConnectAsync().ConfigureAwait(false);

                await client.HandshakeAsync(isolateStream).ConfigureAwait(false);

                return(await client.ResolveAsync(host).ConfigureAwait(false));
            }
        }
Ejemplo n.º 7
0
        public async Task <TorSocks5Client> EstablishTcpConnectionAsync(IPEndPoint destination, bool isolateStream = true)
        {
            Guard.NotNull(nameof(destination), destination);

            var client = new TorSocks5Client(TorSocks5EndPoint);
            await client.ConnectAsync();

            await client.HandshakeAsync(isolateStream);

            await client.ConnectToDestinationAsync(destination);

            return(client);
        }
Ejemplo n.º 8
0
        public async Task <TorSocks5Client> EstablishTcpConnectionAsync(string host, int port, bool isolateStream = true)
        {
            host = Guard.NotNullOrEmptyOrWhitespace(nameof(host), host, true);
            Guard.MinimumAndNotNull(nameof(port), port, 0);

            var client = new TorSocks5Client(TorSocks5EndPoint);
            await client.ConnectAsync();

            await client.HandshakeAsync(isolateStream);

            await client.ConnectToDestinationAsync(host, port);

            return(client);
        }
Ejemplo n.º 9
0
        /// <param name="identity">Isolates streams by identity.</param>
        public async Task <TorSocks5Client> EstablishTcpConnectionAsync(IPEndPoint destination, string identity)
        {
            identity = Guard.NotNullOrEmptyOrWhitespace(nameof(identity), identity, trim: true);
            Guard.NotNull(nameof(destination), destination);

            var client = new TorSocks5Client(TorSocks5EndPoint);
            await client.ConnectAsync();

            await client.HandshakeAsync(identity);

            await client.ConnectToDestinationAsync(destination);

            return(client);
        }
Ejemplo n.º 10
0
        protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancel)
        {
            Guard.NotNull(nameof(request), request);
            if (cancel == null)
            {
                cancel = CancellationToken.None;
            }
            // https://tools.ietf.org/html/rfc7230#section-2.7.1
            // A sender MUST NOT generate an "http" URI with an empty host identifier.
            var host = Guard.NotNullOrEmptyOrWhitespace($"{nameof(request)}.{nameof(request.RequestUri)}.{nameof(request.RequestUri.DnsSafeHost)}", request.RequestUri.DnsSafeHost, trim: true);

            using (var linuxOsxLock = await LinuxOsxLock.LockAsync(cancel).ConfigureAwait(false))             // Linux and OSX is terrible, rather do everything in sync, if windows, it'll be released right away, this must be in effect to bypass this linuxosxbug: if (client != null && (!client.IsConnected || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)))
                using (await DisposeRequestLock.ReaderLockAsync(cancel).ConfigureAwait(false))
                    using (var connectLockTask = await ConnectLock.LockAsync(cancel).ConfigureAwait(false))   // this makes sure clients with the same host don't try to connect concurrently, it gets released after connection established
                    {
                        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                        {
                            linuxOsxLock.Dispose();
                        }

                        KeyValuePair <TorSocks5Client, AsyncLock> clientLockPair = TryFindClientLockPair(host, request.RequestUri.Port);
                        AsyncLock clientLock = clientLockPair.Value ?? new AsyncLock();         // this makes sure clients with the same host don't work concurrently
                        using (await clientLock.LockAsync(cancel).ConfigureAwait(false))
                        {
                            TorSocks5Client client = await SendAsync(request, host, connectLockTask, clientLockPair, clientLock, cancel);

                            try
                            {
                                return(await new HttpResponseMessage().CreateNewAsync(client.Stream, request.Method).ConfigureAwait(false));
                            }
                            catch (IOException)
                            {
                                // the connection is lost, reconnect
                                client = await SendAsync(request, host, connectLockTask, clientLockPair, clientLock, cancel);

                                return(await new HttpResponseMessage().CreateNewAsync(client.Stream, request.Method).ConfigureAwait(false));
                            }
                        }
                    }
        }
Ejemplo n.º 11
0
        private async Task <TorSocks5Client> SendAsync(HttpRequestMessage request, string host, IDisposable connectLockTask, KeyValuePair <TorSocks5Client, AsyncLock> clientLockPair, AsyncLock clientLock, CancellationToken cancel)
        {
            TorSocks5Client client = null;

            try
            {
                // https://tools.ietf.org/html/rfc7230#section-2.6
                // Intermediaries that process HTTP messages (i.e., all intermediaries
                // other than those acting as tunnels) MUST send their own HTTP - version
                // in forwarded messages.
                request.Version = HttpProtocol.HTTP11.Version;

                client = clientLockPair.Key;

                if (client != null && (!client.IsConnected || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)))                 // Linux and OSX bug, this line only works if LinuxOsxLock is in effect
                {
                    Connections.TryRemove(client, out AsyncLock al);
                    client?.Dispose();
                }

                if (client == null || !client.IsConnected)
                {
                    cancel.ThrowIfCancellationRequested();
                    client = await TorSocks5Manager.EstablishTcpConnectionAsync(host, request.RequestUri.Port, isolateStream : true, cancel : cancel).ConfigureAwait(false);

                    cancel.ThrowIfCancellationRequested();

                    Stream stream = client.TcpClient.GetStream();
                    if (request.RequestUri.Scheme.Equals("https", StringComparison.Ordinal))
                    {
                        SslStream sslStream;
                        // On Linux and OSX ignore certificate, because of a .NET Core bug
                        // This is a security vulnerability, has to be fixed as soon as the bug get fixed
                        // Details:
                        // https://github.com/dotnet/corefx/issues/21761
                        // https://github.com/nopara73/DotNetTor/issues/4
                        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                        {
                            sslStream = new SslStream(
                                stream,
                                leaveInnerStreamOpen: true);
                        }
                        else
                        {
                            sslStream = new SslStream(
                                stream,
                                leaveInnerStreamOpen: true,
                                userCertificateValidationCallback: (a, b, c, d) => true);
                        }

                        await sslStream
                        .AuthenticateAsClientAsync(
                            host,
                            new X509CertificateCollection(),
                            SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12,
                            checkCertificateRevocation : true)
                        .ConfigureAwait(false);

                        stream = sslStream;
                    }

                    client.Stream = stream;

                    Connections.TryAdd(client, clientLock);
                }
                connectLockTask?.Dispose();

                cancel.ThrowIfCancellationRequested();

                // https://tools.ietf.org/html/rfc7230#section-3.3.2
                // A user agent SHOULD send a Content - Length in a request message when
                // no Transfer-Encoding is sent and the request method defines a meaning
                // for an enclosed payload body.For example, a Content - Length header
                // field is normally sent in a POST request even when the value is 0
                // (indicating an empty payload body).A user agent SHOULD NOT send a
                // Content - Length header field when the request message does not contain
                // a payload body and the method semantics do not anticipate such a
                // body.
                // TODO implement it fully (altough probably .NET already ensures it)
                if (request.Method == HttpMethod.Post)
                {
                    if (request.Headers.TransferEncoding.Count == 0)
                    {
                        if (request.Content == null)
                        {
                            request.Content = new ByteArrayContent(new byte[] { });                             // dummy empty content
                            request.Content.Headers.ContentLength = 0;
                        }
                        else
                        {
                            if (request.Content.Headers.ContentLength == null)
                            {
                                request.Content.Headers.ContentLength = (await request.Content.ReadAsStringAsync().ConfigureAwait(false)).Length;
                                cancel.ThrowIfCancellationRequested();
                            }
                        }
                    }
                }

                var requestString = await request.ToHttpStringAsync().ConfigureAwait(false);

                cancel.ThrowIfCancellationRequested();

                var bytes = Encoding.UTF8.GetBytes(requestString);

                try
                {
                    await client.Stream.WriteAsync(bytes, 0, bytes.Length, cancel).ConfigureAwait(false);

                    cancel.ThrowIfCancellationRequested();
                }
                catch (NullReferenceException ex)                 // dotnet brainfart
                {
                    Logger.LogTrace <TorSocks5Handler>(ex);
                    throw new OperationCanceledException();
                }

                await client.Stream.FlushAsync(cancel).ConfigureAwait(false);

                cancel.ThrowIfCancellationRequested();
                return(client);
            }
            catch (OperationCanceledException)
            {
                client?.Dispose();
                throw;
            }
            catch (Exception ex)
            {
                Logger.LogTrace <TorSocks5Handler>(ex);
                client?.Dispose();
                cancel.ThrowIfCancellationRequested();
                throw;
            }
        }