예제 #1
0
        public TorSocks5Handler(IPEndPoint torSocks5EndPoint)
        {
            Guard.NotNull(nameof(torSocks5EndPoint), torSocks5EndPoint);

            TorSocks5Manager = new TorSocks5Manager(torSocks5EndPoint);

            Connections = new ConcurrentDictionary <TorSocks5Client, AsyncLock>();

            ConnectLock = new AsyncLock();

            DisposeRequestLock = new AsyncReaderWriterLock();

            TorControlClient.CircuitChangeRequested += Client_CircuitChangeRequestedAsync;
        }
예제 #2
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;
            }
        }