Socks4Connector
Establishes a tunnel via a SOCKS4 proxy server.
            
                using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Renci.SshNet.Connection
{
    internal class Socks4Connector : ConnectorBase
    {
        public Socks4Connector(ISocketFactory socketFactory)
            : base(socketFactory)
        {
        }
        public override Socket Connect(IConnectionInfo connectionInfo)
        {
            Socket socket = SocketConnect(connectionInfo.ProxyHost, connectionInfo.ProxyPort, connectionInfo.Timeout);
            try {
                HandleProxyConnect(connectionInfo, socket);
                return socket;
            } catch (Exception) {
                socket.Shutdown(SocketShutdown.Both);
                socket.Dispose();
                throw;
            }
        }
        private void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket)
        {
            byte[] data = CreateSocks4ConnectionRequest(connectionInfo.Host, (ushort)connectionInfo.Port, connectionInfo.ProxyUsername);
            SocketAbstraction.Send(socket, data);
            if (ConnectorBase.SocketReadByte(socket, connectionInfo.Timeout) != 0)
                throw new ProxyException("SOCKS4: Null is expected.");
            switch (ConnectorBase.SocketReadByte(socket, connectionInfo.Timeout)) {
            case 91:
                throw new ProxyException("SOCKS4: Connection rejected.");
            case 92:
                throw new ProxyException("SOCKS4: Client is not running identd or not reachable from the server.");
            case 93:
                throw new ProxyException("SOCKS4: Client's identd could not confirm the user ID string in the request.");
            default:
                throw new ProxyException("SOCKS4: Not valid response.");
            case 90: {
                byte[] array = new byte[6];
                ConnectorBase.SocketRead(socket, array, 0, array.Length, connectionInfo.Timeout);
                break;
            }
            }
        }
        private static byte[] CreateSocks4ConnectionRequest(string hostname, ushort port, string username)
        {
            byte[] socks4DestinationAddress = GetSocks4DestinationAddress(hostname);
            byte[] proxyUserBytes = GetProxyUserBytes(username);
            byte[] array = new byte[4 + socks4DestinationAddress.Length + proxyUserBytes.Length + 1];
            int num = 0;
            array[num++] = 4;
            array[num++] = 1;
            Pack.UInt16ToBigEndian(port, array, num);
            num += 2;
            Buffer.BlockCopy(socks4DestinationAddress, 0, array, num, socks4DestinationAddress.Length);
            num += socks4DestinationAddress.Length;
            Buffer.BlockCopy(proxyUserBytes, 0, array, num, proxyUserBytes.Length);
            num += proxyUserBytes.Length;
            array[num] = 0;
            return array;
        }
        private static byte[] GetSocks4DestinationAddress(string hostname)
        {
            IPAddress[] hostAddresses = DnsAbstraction.GetHostAddresses(hostname);
            foreach (IPAddress iPAddress in hostAddresses) {
                if (iPAddress.AddressFamily == AddressFamily.InterNetwork)
                    return iPAddress.GetAddressBytes();
            }
            throw new ProxyException($"""{hostname}""");
        }
        private static byte[] GetProxyUserBytes(string proxyUser)
        {
            if (proxyUser == null)
                return Array<byte>.Empty;
            return Encoding.ASCII.GetBytes(proxyUser);
        }
    }
}