/// <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)); }
public TorSocks5Request(CmdField cmd, AddrField dstAddr, PortField dstPort) { Cmd = Guard.NotNull(nameof(cmd), cmd); DstAddr = Guard.NotNull(nameof(dstAddr), dstAddr); DstPort = Guard.NotNull(nameof(dstPort), dstPort); Ver = VerField.Socks5; Rsv = RsvField.X00; Atyp = dstAddr.Atyp; }
public TorSocks5Response(RepField rep, AddrField bndAddr, PortField bndPort) { Rep = Guard.NotNull(nameof(rep), rep); BndAddr = Guard.NotNull(nameof(bndAddr), bndAddr); BndPort = Guard.NotNull(nameof(bndPort), bndPort); Ver = VerField.Socks5; Rsv = RsvField.X00; Atyp = bndAddr.Atyp; }
public override void FromBytes(byte[] bytes) { Guard.NotNullOrEmpty(nameof(bytes), bytes); Guard.MinimumAndNotNull($"{nameof(bytes)}.{nameof(bytes.Length)}", bytes.Length, 6); Ver = new VerField(bytes[0]); Rep = new RepField(); Rep.FromByte(bytes[1]); Rsv = new RsvField(); Rsv.FromByte(bytes[2]); Atyp = new AtypField(bytes[3]); BndAddr = new AddrField(); BndAddr.FromBytes(bytes[4..^ 2]);
public override void FromBytes(byte[] bytes) { Guard.NotNullOrEmpty(nameof(bytes), bytes); Guard.MinimumAndNotNull($"{nameof(bytes)}.{nameof(bytes.Length)}", bytes.Length, 6); Ver = new VerField(); Ver.FromByte(bytes[0]); Cmd = new CmdField(); Cmd.FromByte(bytes[1]); Rsv = new RsvField(); Rsv.FromByte(bytes[2]); Atyp = new AtypField(); Atyp.FromByte(bytes[3]); DstAddr = new AddrField(); DstAddr.FromBytes(bytes.Skip(4).Take(bytes.Length - 4 - 2).ToArray()); DstPort = new PortField(); DstPort.FromBytes(bytes.Skip(bytes.Length - 2).ToArray()); }
/// <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. }