Commit 8323f79f authored by Honfika's avatar Honfika

socks5 refactored + small fies

parent 270c57c9
......@@ -395,40 +395,41 @@ retry:
tcpServerSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
}
var connectTask = socks
? ProxySocketConnectionTaskFactory.CreateTask((ProxySocket.ProxySocket)tcpServerSocket, ipAddress, port)
: SocketConnectionTaskFactory.CreateTask(tcpServerSocket, ipAddress, port);
await Task.WhenAny(connectTask, Task.Delay(proxyServer.ConnectTimeOutSeconds * 1000, cancellationToken));
if (!connectTask.IsCompleted || !tcpServerSocket.Connected)
{
// here we can just do some cleanup and let the loop continue since
// we will either get a connection or wind up with a null tcpClient
// which will throw
try
{
connectTask.Dispose();
}
catch
{
// ignore
}
try
{
#if NET45
tcpServerSocket?.Close();
#else
tcpServerSocket?.Dispose();
#endif
tcpServerSocket = null;
}
catch
{
// ignore
}
continue;
}
((ProxySocket.ProxySocket)tcpServerSocket).Connect(ipAddress, port);
// var connectTask = socks
// ? ProxySocketConnectionTaskFactory.CreateTask((ProxySocket.ProxySocket)tcpServerSocket, ipAddress, port)
// : SocketConnectionTaskFactory.CreateTask(tcpServerSocket, ipAddress, port);
// await Task.WhenAny(connectTask, Task.Delay(proxyServer.ConnectTimeOutSeconds * 1000, cancellationToken));
// if (!connectTask.IsCompleted || !tcpServerSocket.Connected)
// {
// // here we can just do some cleanup and let the loop continue since
// // we will either get a connection or wind up with a null tcpClient
// // which will throw
// try
// {
// connectTask.Dispose();
// }
// catch
// {
// // ignore
// }
// try
// {
//#if NET45
// tcpServerSocket?.Close();
//#else
// tcpServerSocket?.Dispose();
//#endif
// tcpServerSocket = null;
// }
// catch
// {
// // ignore
// }
// continue;
// }
break;
}
......
......@@ -90,45 +90,20 @@ namespace Titanium.Web.Proxy.ProxySocket.Authentication
/// Gets or sets a byt array that can be used to store data.
/// </summary>
/// <value>A byte array to store data.</value>
protected byte[] Buffer
{
get
{
return _buffer;
}
set
{
_buffer = value;
}
}
protected byte[] Buffer { get; set; }
/// <summary>
/// Gets or sets the number of bytes that have been received from the remote proxy server.
/// </summary>
/// <value>An integer that holds the number of bytes that have been received from the remote proxy server.</value>
protected int Received
{
get
{
return _received;
}
set
{
_received = value;
}
}
protected int Received { get; set; }
// private variables
/// <summary>Holds the value of the Buffer property.</summary>
private byte[] _buffer;
/// <summary>Holds the value of the Server property.</summary>
private Socket _server;
/// <summary>Holds the address of the method to call when the proxy has authenticated the client.</summary>
protected HandShakeComplete CallBack;
/// <summary>Holds the value of the Received property.</summary>
private int _received;
}
}
......@@ -73,7 +73,7 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <returns>An array of bytes that has to be sent when the user wants to connect to a specific IPEndPoint.</returns>
private byte[] GetConnectBytes(string host, int port)
{
StringBuilder sb = new StringBuilder();
var sb = new StringBuilder();
sb.AppendLine(string.Format("CONNECT {0}:{1} HTTP/1.1", host, port));
sb.AppendLine(string.Format("Host: {0}:{1}", host, port));
if (!string.IsNullOrEmpty(Username))
......@@ -92,12 +92,14 @@ namespace Titanium.Web.Proxy.ProxySocket
/// Verifies that proxy server successfully connected to requested host
/// </summary>
/// <param name="buffer">Input data array</param>
private void VerifyConnectHeader(byte[] buffer)
/// <param name="length">The data count in the buffer</param>
private void VerifyConnectHeader(byte[] buffer, int length)
{
string header = Encoding.ASCII.GetString(buffer);
string header = Encoding.ASCII.GetString(buffer, 0, length);
if ((!header.StartsWith("HTTP/1.1 ", StringComparison.OrdinalIgnoreCase) &&
!header.StartsWith("HTTP/1.0 ", StringComparison.OrdinalIgnoreCase)) || !header.EndsWith(" "))
throw new ProtocolViolationException();
string code = header.Substring(9, 3);
if (code != "200")
throw new ProxyException("Invalid HTTP status. Code: " + code);
......@@ -134,20 +136,21 @@ namespace Titanium.Web.Proxy.ProxySocket
{
if (host == null)
throw new ArgumentNullException();
if (port <= 0 || port > 65535 || host.Length > 255)
throw new ArgumentException();
byte[] buffer = GetConnectBytes(host, port);
if (Server.Send(buffer, 0, buffer.Length, SocketFlags.None) < buffer.Length)
{
throw new SocketException(10054);
}
buffer = ReadBytes(13);
VerifyConnectHeader(buffer);
ReadBytes(buffer, 13); // buffer is always longer than 13 bytes. Check the code in GetConnectBytes
VerifyConnectHeader(buffer, 13);
// Read bytes 1 by 1 until we reach "\r\n\r\n"
int receivedNewlineChars = 0;
buffer = new byte[1];
while (receivedNewlineChars < 4)
{
int recv = Server.Receive(buffer, 0, 1, SocketFlags.None);
......@@ -209,7 +212,7 @@ namespace Titanium.Web.Proxy.ProxySocket
}
catch (Exception e)
{
ProtocolComplete(e);
OnProtocolComplete(e);
return;
}
......@@ -220,7 +223,7 @@ namespace Titanium.Web.Proxy.ProxySocket
}
catch (Exception e)
{
ProtocolComplete(e);
OnProtocolComplete(e);
}
}
......@@ -239,7 +242,7 @@ namespace Titanium.Web.Proxy.ProxySocket
}
catch (Exception e)
{
ProtocolComplete(e);
OnProtocolComplete(e);
}
}
......@@ -255,7 +258,7 @@ namespace Titanium.Web.Proxy.ProxySocket
}
catch (Exception e)
{
ProtocolComplete(e);
OnProtocolComplete(e);
return;
}
......@@ -268,13 +271,13 @@ namespace Titanium.Web.Proxy.ProxySocket
}
else
{
VerifyConnectHeader(Buffer);
VerifyConnectHeader(Buffer, 13);
ReadUntilHeadersEnd(true);
}
}
catch (Exception e)
{
ProtocolComplete(e);
OnProtocolComplete(e);
}
}
......@@ -303,7 +306,7 @@ namespace Titanium.Web.Proxy.ProxySocket
if (_receivedNewlineChars == 4)
{
ProtocolComplete(null);
OnProtocolComplete(null);
}
else
{
......@@ -327,10 +330,16 @@ namespace Titanium.Web.Proxy.ProxySocket
}
catch (Exception e)
{
ProtocolComplete(e);
OnProtocolComplete(e);
}
}
protected override void OnProtocolComplete(Exception? exception)
{
// do not return the base Buffer
ProtocolComplete(exception);
}
/// <summary>
/// Gets or sets the password to use when authenticating with the HTTPS server.
/// </summary>
......
......@@ -42,73 +42,40 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <param name="stateObject">An object that contains state information for this request.</param>
internal IAsyncProxyResult(object stateObject = null)
{
_stateObject = stateObject;
_completed = false;
if (_waitHandle != null)
_waitHandle.Reset();
AsyncState = stateObject;
IsCompleted = false;
_waitHandle?.Reset();
}
/// <summary>Initializes the internal variables of this object</summary>
internal void Reset()
{
_stateObject = null;
_completed = true;
if (_waitHandle != null)
_waitHandle.Set();
//AsyncState = null;
IsCompleted = true;
_waitHandle?.Set();
}
/// <summary>Gets a value that indicates whether the server has completed processing the call. It is illegal for the server to use any client supplied resources outside of the agreed upon sharing semantics after it sets the IsCompleted property to "true". Thus, it is safe for the client to destroy the resources after IsCompleted property returns "true".</summary>
/// <value>A boolean that indicates whether the server has completed processing the call.</value>
public bool IsCompleted
{
get
{
return _completed;
}
}
public bool IsCompleted { get; private set; }
/// <summary>Gets a value that indicates whether the BeginXXXX call has been completed synchronously. If this is detected in the AsyncCallback delegate, it is probable that the thread that called BeginInvoke is the current thread.</summary>
/// <value>Returns false.</value>
public bool CompletedSynchronously
{
get
{
return false;
}
}
public bool CompletedSynchronously => false;
/// <summary>Gets an object that was passed as the state parameter of the BeginXXXX method call.</summary>
/// <value>The object that was passed as the state parameter of the BeginXXXX method call.</value>
public object AsyncState
{
get
{
return _stateObject;
}
}
public object AsyncState { get; private set; }
/// <summary>
/// The AsyncWaitHandle property returns the WaitHandle that can use to perform a WaitHandle.WaitOne or WaitAny or WaitAll. The object which implements IAsyncResult need not derive from the System.WaitHandle classes directly. The WaitHandle wraps its underlying synchronization primitive and should be signaled after the call is completed. This enables the client to wait for the call to complete instead polling. The Runtime supplies a number of waitable objects that mirror Win32 synchronization primitives e.g. ManualResetEvent, AutoResetEvent and Mutex.
/// WaitHandle supplies methods that support waiting for such synchronization objects to become signaled with "any" or "all" semantics i.e. WaitHandle.WaitOne, WaitAny and WaitAll. Such methods are context aware to avoid deadlocks. The AsyncWaitHandle can be allocated eagerly or on demand. It is the choice of the IAsyncResult implementer.
///</summary>
/// <value>The WaitHandle associated with this asynchronous result.</value>
public WaitHandle AsyncWaitHandle
{
get
{
if (_waitHandle == null)
_waitHandle = new ManualResetEvent(false);
return _waitHandle;
}
}
public WaitHandle AsyncWaitHandle => _waitHandle ??= new ManualResetEvent(false);
// private variables
/// <summary>Used internally to represent the state of the asynchronous request</summary>
private bool _completed;
/// <summary>Holds the value of the StateObject property.</summary>
private object _stateObject;
/// <summary>Holds the value of the WaitHandle property.</summary>
private ManualResetEvent _waitHandle;
......
......@@ -31,7 +31,6 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Security.AccessControl;
// Implements a number of classes to allow Sockets to connect trough a firewall.
namespace Titanium.Web.Proxy.ProxySocket
......@@ -104,6 +103,21 @@ namespace Titanium.Web.Proxy.ProxySocket
ToThrow = new InvalidOperationException();
}
/// <summary>
/// Establishes a connection to a remote device.
/// </summary>
/// <param name="address">An EndPoint address that represents the remote device.</param>
/// <param name="port">An EndPoint port that represents the remote device.</param>
/// <exception cref="ArgumentNullException">The remoteEP parameter is a null reference (Nothing in Visual Basic).</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
/// <exception cref="ProxyException">An error occurred while talking to the proxy server.</exception>
public new void Connect(IPAddress address, int port)
{
var remoteEP = new IPEndPoint(address, port);
Connect(remoteEP);
}
/// <summary>
/// Establishes a connection to a remote device.
/// </summary>
......@@ -144,20 +158,22 @@ namespace Titanium.Web.Proxy.ProxySocket
public new void Connect(string host, int port)
{
if (host == null)
throw new ArgumentNullException("<host> cannot be null.");
throw new ArgumentNullException(nameof(host));
if (port <= 0 || port > 65535)
throw new ArgumentException("Invalid port.");
throw new ArgumentException(nameof(port));
if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null)
base.Connect(new IPEndPoint(Dns.GetHostEntry(host).AddressList[0], port));
else
{
base.Connect(ProxyEndPoint);
if (ProxyType == ProxyTypes.Https)
(new HttpsHandler(this, ProxyUser, ProxyPass)).Negotiate(host, port);
new HttpsHandler(this, ProxyUser, ProxyPass).Negotiate(host, port);
else if (ProxyType == ProxyTypes.Socks4)
(new Socks4Handler(this, ProxyUser)).Negotiate(host, port);
new Socks4Handler(this, ProxyUser).Negotiate(host, port);
else if (ProxyType == ProxyTypes.Socks5)
(new Socks5Handler(this, ProxyUser, ProxyPass)).Negotiate(host, port);
new Socks5Handler(this, ProxyUser, ProxyPass).Negotiate(host, port);
}
}
......@@ -248,8 +264,7 @@ namespace Titanium.Web.Proxy.ProxySocket
AsyncResult = BeginDns(host, this.OnHandShakeComplete, state);
return AsyncResult;
}
else
{
if (ProxyType == ProxyTypes.Https)
{
AsyncResult = (new HttpsHandler(this, ProxyUser, ProxyPass)).BeginNegotiate(host, port,
......@@ -273,7 +288,6 @@ namespace Titanium.Web.Proxy.ProxySocket
return null;
}
}
/// <summary>
/// Ends a pending asynchronous connection request.
......@@ -369,8 +383,8 @@ namespace Titanium.Web.Proxy.ProxySocket
this.Close();
ToThrow = error;
CallBack?.Invoke(AsyncResult);
AsyncResult.Reset();
CallBack?.Invoke(AsyncResult);
}
/// <summary>
......@@ -389,17 +403,7 @@ namespace Titanium.Web.Proxy.ProxySocket
/// Gets or sets a user-defined object.
/// </summary>
/// <value>The user-defined object.</value>
private object State
{
get
{
return _state;
}
set
{
_state = value;
}
}
private object State { get; set; }
/// <summary>
/// Gets or sets the username to use when authenticating with the proxy.
......@@ -442,8 +446,6 @@ namespace Titanium.Web.Proxy.ProxySocket
private int RemotePort { get; set; }
// private variables
/// <summary>Holds the value of the State property.</summary>
private object _state;
/// <summary>Holds the value of the ProxyUser property.</summary>
private string _proxyUser = string.Empty;
......
......@@ -169,14 +169,17 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
private void Negotiate(byte[] connect, int length)
{
if (connect.Length < 2)
throw new ArgumentException();
if (connect == null)
throw new ArgumentNullException(nameof(connect));
if (Server.Send(connect, 0, length, SocketFlags.None) < connect.Length)
if (length < 2)
throw new ArgumentException(nameof(length));
if (Server.Send(connect, 0, length, SocketFlags.None) < length)
throw new SocketException(10054);
byte[] buffer = ReadBytes(8);
if (buffer[1] != 90)
ReadBytes(connect, 8);
if (connect[1] != 90)
{
Server.Close();
throw new ProxyException("Negotiation failed.");
......@@ -266,8 +269,9 @@ namespace Titanium.Web.Proxy.ProxySocket
try
{
BufferCount = 8;
Received = 0;
Server.BeginReceive(Buffer, 0, 8, SocketFlags.None, OnReceive, Server);
Server.BeginReceive(Buffer, 0, BufferCount, SocketFlags.None, OnReceive, Server);
}
catch (Exception e)
{
......@@ -296,7 +300,7 @@ namespace Titanium.Web.Proxy.ProxySocket
}
else
{
Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, OnReceive,
Server.BeginReceive(Buffer, Received, BufferCount - Received, SocketFlags.None, OnReceive,
Server);
}
}
......@@ -305,11 +309,5 @@ namespace Titanium.Web.Proxy.ProxySocket
OnProtocolComplete(e);
}
}
private void OnProtocolComplete(Exception? exception)
{
ArrayPool<byte>.Shared.Return(Buffer);
ProtocolComplete(exception);
}
}
}
......@@ -29,6 +29,7 @@
*/
using System;
using System.Buffers;
using System.Net;
using System.Net.Sockets;
......@@ -68,19 +69,6 @@ namespace Titanium.Web.Proxy.ProxySocket
buffer[1] = (byte)(port % 256);
}
/// <summary>
/// Converts a port number to an array of bytes.
/// </summary>
/// <param name="port">The port to convert.</param>
/// <returns>An array of two bytes that represents the specified port.</returns>
protected byte[] PortToBytes(int port)
{
byte[] ret = new byte[2];
ret[0] = (byte)(port / 256);
ret[1] = (byte)(port % 256);
return ret;
}
/// <summary>
/// Converts an IP address to an array of bytes.
/// </summary>
......@@ -99,16 +87,17 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <summary>
/// Reads a specified number of bytes from the Server socket.
/// </summary>
/// <param name="buffer">The result buffer.</param>
/// <param name="count">The number of bytes to return.</param>
/// <returns>An array of bytes.</returns>
/// <exception cref="ArgumentException">The number of bytes to read is invalid.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
protected byte[] ReadBytes(int count)
protected void ReadBytes(byte[] buffer, int count)
{
if (count <= 0)
throw new ArgumentException();
byte[] buffer = new byte[count];
int received = 0;
while (received != count)
{
......@@ -120,8 +109,6 @@ namespace Titanium.Web.Proxy.ProxySocket
received += recv;
}
return buffer;
}
/// <summary>
......@@ -134,6 +121,7 @@ namespace Titanium.Web.Proxy.ProxySocket
int recv = Server.EndReceive(ar);
if (recv <= 0)
throw new SocketException(10054);
Received += recv;
}
......@@ -149,6 +137,16 @@ namespace Titanium.Web.Proxy.ProxySocket
throw new SocketException(10054);
}
protected virtual void OnProtocolComplete(Exception? exception)
{
if (Buffer != null)
{
ArrayPool<byte>.Shared.Return(Buffer);
}
ProtocolComplete(exception);
}
/// <summary>
/// Gets or sets the socket connection with the proxy server.
/// </summary>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment