Beispiel #1
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>
        internal async Task <IPAddress> ResolveAsync(string host)
        {
            // https://gitweb.torproject.org/torspec.git/tree/socks-extensions.txt#n44

            host = Guard.NotNullOrEmptyOrWhitespace(nameof(host), host, true);

            if (TorSocks5EndPoint is null)
            {
                var hostAddresses = await Dns.GetHostAddressesAsync(host);

                return(hostAddresses.First());
            }

            var cmd = CmdField.Resolve;

            var dstAddr = new AddrField(host);

            var dstPort = new PortField(0);

            var resolveRequest = new TorSocks5Request(cmd, dstAddr, dstPort);
            var sendBuffer     = resolveRequest.ToBytes();

            var receiveBuffer = await SendAsync(sendBuffer);

            var resolveResponse = new TorSocks5Response();

            resolveResponse.FromBytes(receiveBuffer);

            if (resolveResponse.Rep != RepField.Succeeded)
            {
                throw new TorSocks5FailureResponseException(resolveResponse.Rep);
            }
            return(IPAddress.Parse(resolveResponse.BndAddr.DomainOrIPv4));
        }
Beispiel #2
0
        /// <param name="host">IPv4 or domain</param>
        public async Task ConnectToDestinationAsync(string host, int port, CancellationToken cancellationToken = default)
        {
            Logger.LogDebug($"> {nameof(host)}={host}, {nameof(port)}={port}");

            host = Guard.NotNullOrEmptyOrWhitespace(nameof(host), host, true);
            Guard.MinimumAndNotNull(nameof(port), port, 0);

            try
            {
                var connectionRequest = new TorSocks5Request(cmd: CmdField.Connect, new AddrField(host), new PortField(port));
                var sendBuffer        = connectionRequest.ToBytes();

                var receiveBuffer = await SendAsync(sendBuffer, receiveBufferSize : null, cancellationToken).ConfigureAwait(false);

                var connectionResponse = new TorSocks5Response();
                connectionResponse.FromBytes(receiveBuffer);

                if (connectionResponse.Rep != RepField.Succeeded)
                {
                    // https://www.ietf.org/rfc/rfc1928.txt
                    // When a reply(REP value other than X'00') indicates a failure, the
                    // SOCKS server MUST terminate the TCP connection shortly after sending
                    // the reply.This must be no more than 10 seconds after detecting the
                    // condition that caused a failure.
                    DisposeTcpClient();
                    Logger.LogWarning($"Connection response indicates a failure. Actual response is: '{connectionResponse.Rep}'. Request: {host}:{port}.");
                    throw new TorSocks5FailureResponseException(connectionResponse.Rep);
                }

                // Do not check the Bnd. Address and Bnd. Port. because Tor does not seem to return any, ever. It returns zeros instead.
                // Generally also do not check anything but the success response, according to Socks5 RFC

                // If the reply code(REP value of X'00') indicates a success, and the
                // request was either a BIND or a CONNECT, the client may now start
                // passing data. If the selected authentication method supports
                // encapsulation for the purposes of integrity, authentication and / or
                // confidentiality, the data are encapsulated using the method-dependent
                // encapsulation.Similarly, when data arrives at the SOCKS server for
                // the client, the server MUST encapsulate the data as appropriate for
                // the authentication method in use.
            }
            catch (OperationCanceledException)
            {
                Logger.LogTrace($"Connecting to destination {host}:{port} was canceled.");
                throw;
            }
            catch (Exception e)
            {
                Logger.LogError($"Exception was thrown when connecting to destination ({host}:{port})", e);
                throw;
            }
            finally
            {
                Logger.LogDebug("<");
            }
        }
Beispiel #3
0
        /// <summary>
        /// Tor attempts to find the canonical hostname for that IPv4 record
        /// </summary>
        internal async Task <string> ReverseResolveAsync(IPAddress iPv4)
        {
            // https://gitweb.torproject.org/torspec.git/tree/socks-extensions.txt#n55

            Guard.NotNull(nameof(iPv4), iPv4);

            if (TorSocks5EndPoint is null)             // Only Tor is iPv4 dependent
            {
                var host = await Dns.GetHostEntryAsync(iPv4);

                return(host.HostName);
            }

            Guard.Same($"{nameof(iPv4)}.{nameof(iPv4.AddressFamily)}", AddressFamily.InterNetwork, iPv4.AddressFamily);

            var cmd = CmdField.ResolvePtr;

            var dstAddr = new AddrField(iPv4.ToString());

            var dstPort = new PortField(0);

            var resolveRequest = new TorSocks5Request(cmd, dstAddr, dstPort);
            var sendBuffer     = resolveRequest.ToBytes();

            var receiveBuffer = await SendAsync(sendBuffer);

            var resolveResponse = new TorSocks5Response();

            resolveResponse.FromBytes(receiveBuffer);

            if (resolveResponse.Rep != RepField.Succeeded)
            {
                throw new TorSocks5FailureResponseException(resolveResponse.Rep);
            }
            return(resolveResponse.BndAddr.DomainOrIPv4);
        }
        /// <param name="host">IPv4 or domain</param>
        internal async Task ConnectToDestinationAsync(string host, int port, bool isRecursiveCall = false)
        {
            host = Guard.NotNullOrEmptyOrWhitespace(nameof(host), host, true);
            Guard.MinimumAndNotNull(nameof(port), port, 0);

            if (TorSocks5EndPoint is null)
            {
                using (await AsyncLock.LockAsync().ConfigureAwait(false))
                {
                    TcpClient?.Dispose();
                    TcpClient = IPAddress.TryParse(host, out IPAddress ip) ? new TcpClient(ip.AddressFamily) : new TcpClient();
                    await TcpClient.ConnectAsync(host, port).ConfigureAwait(false);

                    Stream         = TcpClient.GetStream();
                    RemoteEndPoint = TcpClient.Client.RemoteEndPoint;
                }

                return;
            }

            var cmd = CmdField.Connect;

            var dstAddr = new AddrField(host);

            DestinationHost = dstAddr.DomainOrIPv4;

            var dstPort = new PortField(port);

            DestinationPort = dstPort.DstPort;

            var connectionRequest = new TorSocks5Request(cmd, dstAddr, dstPort);
            var sendBuffer        = connectionRequest.ToBytes();

            var receiveBuffer = await SendAsync(sendBuffer, isRecursiveCall : isRecursiveCall).ConfigureAwait(false);

            var connectionResponse = new TorSocks5Response();

            connectionResponse.FromBytes(receiveBuffer);

            if (connectionResponse.Rep != RepField.Succeeded)
            {
                // https://www.ietf.org/rfc/rfc1928.txt
                // When a reply(REP value other than X'00') indicates a failure, the
                // SOCKS server MUST terminate the TCP connection shortly after sending
                // the reply.This must be no more than 10 seconds after detecting the
                // condition that caused a failure.
                DisposeTcpClient();
                throw new TorSocks5FailureResponseException(connectionResponse.Rep);
            }

            // Do not check the Bnd. Address and Bnd. Port. because Tor does not seem to return any, ever. It returns zeros instead.
            // Generally also do not check anything but the success response, according to Socks5 RFC

            // If the reply code(REP value of X'00') indicates a success, and the
            // request was either a BIND or a CONNECT, the client may now start
            // passing data. If the selected authentication method supports
            // encapsulation for the purposes of integrity, authentication and / or
            // confidentiality, the data are encapsulated using the method-dependent
            // encapsulation.Similarly, when data arrives at the SOCKS server for
            // the client, the server MUST encapsulate the data as appropriate for
            // the authentication method in use.
        }