Use WebClient with socks proxy

烂漫一生 提交于 2019-11-28 17:56:33

SOCKS is not supported directly by the WebRequest/WebResponse classes and by extension, the WebClient class (it relies on WebRequest to do its work).

It really can't, as it works on the transport layer (TCP/IP) and not through a simple redirect to a server that forwards HTTP requests (which is the level that the WebRequest/WebResponse classes work on).

You can create a specialized derivation of WebRequest/WebResponse (that uses ProxySocket to handle the low-level handshaking and then) and then create a specialized WebClient class which overrides the GetWebRequest and GetWebResponse methods.

Once you have that class substituted for your WebClient instances, it should work as normal (you might have to set up the proxy in each case where you use it though).

Dan Parsonson

Here is how I ended up doing it, thank you casperOne for the answer

public class SocksWebClient : WebClient
    {
        public IProxyDetails ProxyDetails { get; set; }
        public string UserAgent { get; set; }

        protected override WebRequest GetWebRequest(Uri address)
        {
            WebRequest result = null;

            if (ProxyDetails != null)
            {
                if (ProxyDetails.ProxyType == ProxyType.Proxy)
                {
                    result = (HttpWebRequest)WebRequest.Create(address);
                    result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress);
                    if (!string.IsNullOrEmpty(UserAgent))
                        ((HttpWebRequest)result).UserAgent = UserAgent;
                }
                else if (ProxyDetails.ProxyType == ProxyType.Socks)
                {
                    result = SocksHttpWebRequest.Create(address);
                    result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress);
                    //TODO: implement user and password

                }
                else if (ProxyDetails.ProxyType == ProxyType.None)
                {
                    result = (HttpWebRequest)WebRequest.Create(address);
                    if (!string.IsNullOrEmpty(UserAgent))
                        ((HttpWebRequest)result).UserAgent = UserAgent;
                }
            }
            else
            {
                result = (HttpWebRequest)WebRequest.Create(address);
                if (!string.IsNullOrEmpty(UserAgent))
                    ((HttpWebRequest)result).UserAgent = UserAgent;
            }


            return result;
        }

    }

The SocksHttpWebRequest class is taken from the blog linked to by @casperOne, the code for which is as follows:

using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Org.Mentalis.Network.ProxySocket;

namespace Ditrans
{
    public class SocksHttpWebRequest : WebRequest
    {

        #region Member Variables

        private readonly Uri _requestUri;
        private WebHeaderCollection _requestHeaders;
        private string _method;
        private SocksHttpWebResponse _response;
        private string _requestMessage;
        private byte[] _requestContentBuffer;

        // darn MS for making everything internal (yeah, I'm talking about you, System.net.KnownHttpVerb)
        static readonly StringCollection validHttpVerbs =
            new StringCollection { "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "OPTIONS" };

        #endregion

        #region Constructor

        private SocksHttpWebRequest(Uri requestUri)
        {
            _requestUri = requestUri;
        }

        #endregion

        #region WebRequest Members

        public override WebResponse GetResponse()
        {
            if (Proxy == null)
            {
                throw new InvalidOperationException("Proxy property cannot be null.");
            }
            if (String.IsNullOrEmpty(Method))
            {
                throw new InvalidOperationException("Method has not been set.");
            }

            if (RequestSubmitted)
            {
                return _response;
            }
            _response = InternalGetResponse();
            RequestSubmitted = true;
            return _response;
        }

        public override Uri RequestUri
        {
            get { return _requestUri; }
        }

        public override IWebProxy Proxy { get; set; }

        public override WebHeaderCollection Headers
        {
            get
            {
                if (_requestHeaders == null)
                {
                    _requestHeaders = new WebHeaderCollection();
                }
                return _requestHeaders;
            }
            set
            {
                if (RequestSubmitted)
                {
                    throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
                }
                _requestHeaders = value;
            }
        }

        public bool RequestSubmitted { get; private set; }

        public override string Method
        {
            get
            {
                return _method ?? "GET";
            }
            set
            {
                if (validHttpVerbs.Contains(value))
                {
                    _method = value;
                }
                else
                {
                    throw new ArgumentOutOfRangeException("value", string.Format("'{0}' is not a known HTTP verb.", value));
                }
            }
        }

        public override long ContentLength { get; set; }

        public override string ContentType { get; set; }

        public override Stream GetRequestStream()
        {
            if (RequestSubmitted)
            {
                throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
            }

            if (_requestContentBuffer == null)
            {
                _requestContentBuffer = new byte[ContentLength];
            }
            else if (ContentLength == default(long))
            {
                _requestContentBuffer = new byte[int.MaxValue];
            }
            else if (_requestContentBuffer.Length != ContentLength)
            {
                Array.Resize(ref _requestContentBuffer, (int) ContentLength);
            }
            return new MemoryStream(_requestContentBuffer);
        }

        #endregion

        #region Methods

        public static new WebRequest Create(string requestUri)
        {
            return new SocksHttpWebRequest(new Uri(requestUri));
        }

        public static new WebRequest Create(Uri requestUri)
        {
            return new SocksHttpWebRequest(requestUri);
        }

        private string BuildHttpRequestMessage()
        {
            if (RequestSubmitted)
            {
                throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
            }

            var message = new StringBuilder();
            message.AppendFormat("{0} {1} HTTP/1.0\r\nHost: {2}\r\n", Method, RequestUri.PathAndQuery, RequestUri.Host);

            // add the headers
            foreach (var key in Headers.Keys)
            {
                message.AppendFormat("{0}: {1}\r\n", key, Headers[key.ToString()]);
            }

            if (!string.IsNullOrEmpty(ContentType))
            {
                message.AppendFormat("Content-Type: {0}\r\n", ContentType);
            }
            if (ContentLength > 0)
            {
                message.AppendFormat("Content-Length: {0}\r\n", ContentLength);
            }

            // add a blank line to indicate the end of the headers
            message.Append("\r\n");

            // add content
            if(_requestContentBuffer != null && _requestContentBuffer.Length > 0)
            {
                using (var stream = new MemoryStream(_requestContentBuffer, false))
                {
                    using (var reader = new StreamReader(stream))
                    {
                        message.Append(reader.ReadToEnd());
                    }
                }
            }

            return message.ToString();
        }

        private SocksHttpWebResponse InternalGetResponse()
        {
            var response = new StringBuilder();
            using (var _socksConnection =
                new ProxySocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
            {
                var proxyUri = Proxy.GetProxy(RequestUri);
                var ipAddress = GetProxyIpAddress(proxyUri);
                _socksConnection.ProxyEndPoint = new IPEndPoint(ipAddress, proxyUri.Port);
                _socksConnection.ProxyType = ProxyTypes.Socks5;

                // open connection
                _socksConnection.Connect(RequestUri.Host, 80);
                // send an HTTP request
                _socksConnection.Send(Encoding.ASCII.GetBytes(RequestMessage));
                // read the HTTP reply
                var buffer = new byte[1024];

                var bytesReceived = _socksConnection.Receive(buffer);
                while (bytesReceived > 0)
                {
                    response.Append(Encoding.ASCII.GetString(buffer, 0, bytesReceived));
                    bytesReceived = _socksConnection.Receive(buffer);
                }
            }
            return new SocksHttpWebResponse(response.ToString());
        }

        private static IPAddress GetProxyIpAddress(Uri proxyUri)
        {
            IPAddress ipAddress;
            if (!IPAddress.TryParse(proxyUri.Host, out ipAddress))
            {
                try
                {
                    return Dns.GetHostEntry(proxyUri.Host).AddressList[0];
                }
                catch (Exception e)
                {
                    throw new InvalidOperationException(
                        string.Format("Unable to resolve proxy hostname '{0}' to a valid IP address.", proxyUri.Host), e);
                }
            }
            return ipAddress;
        }

        #endregion

        #region Properties

        public string RequestMessage
        {
            get
            {
                if (string.IsNullOrEmpty(_requestMessage))
                {
                    _requestMessage = BuildHttpRequestMessage();
                }
                return _requestMessage;
            }
        }

        #endregion

    }
}

Note that, as @casperOne pointed out, this makes use of a (free) third party library called ProxySocket.

I came across this aswell and found the nice BetterHttpClient

It derives from WebClient and allows you to specify a socks proxy:

BetterHttpClient.HttpClient client = new BetterHttpClient.HttpClient(new BetterHttpClient.Proxy("IP address", port, BetterHttpClient.ProxyTypeEnum.Socks));
Mandy Eichmann

Here is how I ended up doing it, thank you casperOne for the answer

public class SocksWebClient : WebClient
{
    public IProxyDetails ProxyDetails { get; set; }
    public string UserAgent { get; set; }

    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest result = null;

        if (ProxyDetails != null)
        {
            if (ProxyDetails.ProxyType == ProxyType.Proxy)
            {
                result = (HttpWebRequest)WebRequest.Create(address);
                result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress);
                if (!string.IsNullOrEmpty(UserAgent))
                    ((HttpWebRequest)result).UserAgent = UserAgent;
            }
            else if (ProxyDetails.ProxyType == ProxyType.Socks)
            {
                result = SocksHttpWebRequest.Create(address);
                result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress);
                //TODO: implement user and password

            }
            else if (ProxyDetails.ProxyType == ProxyType.None)
            {
                result = (HttpWebRequest)WebRequest.Create(address);
                if (!string.IsNullOrEmpty(UserAgent))
                    ((HttpWebRequest)result).UserAgent = UserAgent;
            }
        }
        else
        {
            result = (HttpWebRequest)WebRequest.Create(address);
            if (!string.IsNullOrEmpty(UserAgent))
                ((HttpWebRequest)result).UserAgent = UserAgent;
        }

        return result;
    }
}

I'm looking for the same code but it doesnt really work for me. I wonder where you've got the IProxyDetails-Object. It seems not implemented in blog and not in the files i got from there

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!