-Use dual-mode socket & DnsEndPoint to handle all domain name connection. -Move all socket creation into one single function. So that we could treat domain name just like ipv4 & ipv6 address. This also solves the async domain solving problem because the os will hanlde it for us.tags/3.3
@@ -1,6 +1,7 @@ | |||||
using System; | using System; | ||||
using System.Net; | using System.Net; | ||||
using System.Net.Sockets; | using System.Net.Sockets; | ||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
@@ -45,14 +46,9 @@ namespace Shadowsocks.Controller | |||||
this._local = socket; | this._local = socket; | ||||
try | try | ||||
{ | { | ||||
// TODO async resolving | |||||
IPAddress ipAddress; | |||||
bool parsed = IPAddress.TryParse("127.0.0.1", out ipAddress); | |||||
IPEndPoint remoteEP = new IPEndPoint(ipAddress, targetPort); | |||||
EndPoint remoteEP = SocketUtil.GetEndPoint("localhost", targetPort); | |||||
_remote = new Socket(ipAddress.AddressFamily, | |||||
SocketType.Stream, ProtocolType.Tcp); | |||||
_remote = SocketUtil.CreateSocket(remoteEP); | |||||
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | ||||
// Connect to the remote endpoint. | // Connect to the remote endpoint. | ||||
@@ -9,6 +9,7 @@ using Shadowsocks.Controller.Strategy; | |||||
using Shadowsocks.Encryption; | using Shadowsocks.Encryption; | ||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using Shadowsocks.Proxy; | using Shadowsocks.Proxy; | ||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
@@ -363,24 +364,11 @@ namespace Shadowsocks.Controller | |||||
CreateRemote(); | CreateRemote(); | ||||
// Setting up proxy | // Setting up proxy | ||||
IPEndPoint proxyEP; | |||||
EndPoint proxyEP; | |||||
if (_config.useProxy) | if (_config.useProxy) | ||||
{ | { | ||||
IPAddress ipAddress; | |||||
bool parsed = IPAddress.TryParse(_config.proxyServer, out ipAddress); | |||||
if (!parsed) | |||||
{ | |||||
/* | |||||
* TODO really necessary to resolve a proxy's address? Maybe from local hosts? | |||||
* also we may simplify it by using dual-mode socket with | |||||
* the approach described in DirectConnect.BeginConnectDest | |||||
*/ | |||||
IPHostEntry ipHostInfo = Dns.GetHostEntry(_config.proxyServer); | |||||
ipAddress = ipHostInfo.AddressList[0]; | |||||
} | |||||
remote = new Socks5Proxy(); | remote = new Socks5Proxy(); | ||||
proxyEP = new IPEndPoint(ipAddress, _config.proxyPort); | |||||
proxyEP = SocketUtil.GetEndPoint(_config.proxyServer, _config.proxyPort); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -395,7 +383,7 @@ namespace Shadowsocks.Controller | |||||
proxyTimer.Enabled = true; | proxyTimer.Enabled = true; | ||||
proxyTimer.Proxy = remote; | proxyTimer.Proxy = remote; | ||||
proxyTimer.DestEndPoint = ProxyUtils.GetEndPoint(server.server, server.server_port); | |||||
proxyTimer.DestEndPoint = SocketUtil.GetEndPoint(server.server, server.server_port); | |||||
proxyTimer.Server = server; | proxyTimer.Server = server; | ||||
_proxyConnected = false; | _proxyConnected = false; | ||||
@@ -7,6 +7,7 @@ using System.Runtime.CompilerServices; | |||||
using Shadowsocks.Controller.Strategy; | using Shadowsocks.Controller.Strategy; | ||||
using Shadowsocks.Encryption; | using Shadowsocks.Encryption; | ||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
@@ -56,7 +57,7 @@ namespace Shadowsocks.Controller | |||||
private byte[] _buffer = new byte[1500]; | private byte[] _buffer = new byte[1500]; | ||||
private IPEndPoint _localEndPoint; | private IPEndPoint _localEndPoint; | ||||
private IPEndPoint _remoteEndPoint; | |||||
private EndPoint _remoteEndPoint; | |||||
public UDPHandler(Socket local, Server server, IPEndPoint localEndPoint) | public UDPHandler(Socket local, Server server, IPEndPoint localEndPoint) | ||||
{ | { | ||||
@@ -64,16 +65,8 @@ namespace Shadowsocks.Controller | |||||
_server = server; | _server = server; | ||||
_localEndPoint = localEndPoint; | _localEndPoint = localEndPoint; | ||||
// TODO async resolving | |||||
IPAddress ipAddress; | |||||
bool parsed = IPAddress.TryParse(server.server, out ipAddress); | |||||
if (!parsed) | |||||
{ | |||||
IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server); | |||||
ipAddress = ipHostInfo.AddressList[0]; | |||||
} | |||||
_remoteEndPoint = new IPEndPoint(ipAddress, server.server_port); | |||||
_remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); | |||||
_remoteEndPoint = SocketUtil.GetEndPoint(server.server, server.server_port); | |||||
_remote = SocketUtil.CreateSocket(_remoteEndPoint, ProtocolType.Udp); | |||||
} | } | ||||
public void Send(byte[] data, int length) | public void Send(byte[] data, int length) | ||||
@@ -2,6 +2,7 @@ | |||||
using System.Net; | using System.Net; | ||||
using System.Net.Sockets; | using System.Net.Sockets; | ||||
using System.Threading; | using System.Threading; | ||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Proxy | namespace Shadowsocks.Proxy | ||||
{ | { | ||||
@@ -52,39 +53,14 @@ namespace Shadowsocks.Proxy | |||||
public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state) | public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state) | ||||
{ | { | ||||
EndPoint realEndPoint = DestEndPoint = destEndPoint; | |||||
/* | |||||
* On windows vista or later, dual-mode socket is supported, so that | |||||
* we don't need to resolve a DnsEndPoint manually. | |||||
* We could just create a dual-mode socket and pass the DnsEndPoint | |||||
* directly to it's BeginConnect and the system will handle it correctlly | |||||
* so that we won't worry about async resolving any more. | |||||
* | |||||
* see: https://blogs.msdn.microsoft.com/webdev/2013/01/08/dual-mode-sockets-never-create-an-ipv4-socket-again/ | |||||
* | |||||
* But it seems that we can't use this feature because DnsEndPoint | |||||
* doesn't have a specific AddressFamily before it has been | |||||
* resolved (we don't know whether it's ipv4 or ipv6) and we don't have | |||||
* a dual-mode socket to use on windows xp :( | |||||
*/ | |||||
var dep = realEndPoint as DnsEndPoint; | |||||
if (dep != null) | |||||
{ | |||||
// need to resolve manually | |||||
// TODO async resolving | |||||
IPHostEntry ipHostInfo = Dns.GetHostEntry(dep.Host); | |||||
IPAddress ipAddress = ipHostInfo.AddressList[0]; | |||||
realEndPoint = new IPEndPoint(ipAddress, dep.Port); | |||||
} | |||||
DestEndPoint = destEndPoint; | |||||
if (_remote == null) | if (_remote == null) | ||||
{ | { | ||||
_remote = new Socket(realEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); | |||||
_remote = SocketUtil.CreateSocket(destEndPoint); | |||||
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | ||||
} | } | ||||
_remote.BeginConnect(realEndPoint, callback, state); | |||||
_remote.BeginConnect(destEndPoint, callback, state); | |||||
} | } | ||||
public void EndConnectDest(IAsyncResult asyncResult) | public void EndConnectDest(IAsyncResult asyncResult) | ||||
@@ -35,20 +35,4 @@ namespace Shadowsocks.Proxy | |||||
void Close(); | void Close(); | ||||
} | } | ||||
public static class ProxyUtils | |||||
{ | |||||
public static EndPoint GetEndPoint(string host, int port) | |||||
{ | |||||
IPAddress ipAddress; | |||||
bool parsed = IPAddress.TryParse(host, out ipAddress); | |||||
if (parsed) | |||||
{ | |||||
return new IPEndPoint(ipAddress, port); | |||||
} | |||||
// maybe is a domain name | |||||
return new DnsEndPoint(host, port); | |||||
} | |||||
} | |||||
} | } |
@@ -6,6 +6,7 @@ using System.Net.Sockets; | |||||
using System.Text; | using System.Text; | ||||
using System.Threading; | using System.Threading; | ||||
using Shadowsocks.Controller; | using Shadowsocks.Controller; | ||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Proxy | namespace Shadowsocks.Proxy | ||||
{ | { | ||||
@@ -51,7 +52,7 @@ namespace Shadowsocks.Proxy | |||||
public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state) | public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state) | ||||
{ | { | ||||
_remote = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp); | |||||
_remote = SocketUtil.CreateSocket(remoteEP); | |||||
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | ||||
var st = new Socks5State(); | var st = new Socks5State(); | ||||
@@ -0,0 +1,66 @@ | |||||
using System; | |||||
using System.Net; | |||||
using System.Net.Sockets; | |||||
namespace Shadowsocks.Util | |||||
{ | |||||
public static class SocketUtil | |||||
{ | |||||
private class DnsEndPoint2 : DnsEndPoint | |||||
{ | |||||
public DnsEndPoint2(string host, int port) : base(host, port) | |||||
{ | |||||
} | |||||
public DnsEndPoint2(string host, int port, AddressFamily addressFamily) : base(host, port, addressFamily) | |||||
{ | |||||
} | |||||
public override string ToString() | |||||
{ | |||||
return this.Host + ":" + this.Port; | |||||
} | |||||
} | |||||
public static EndPoint GetEndPoint(string host, int port) | |||||
{ | |||||
IPAddress ipAddress; | |||||
bool parsed = IPAddress.TryParse(host, out ipAddress); | |||||
if (parsed) | |||||
{ | |||||
return new IPEndPoint(ipAddress, port); | |||||
} | |||||
// maybe is a domain name | |||||
return new DnsEndPoint2(host, port); | |||||
} | |||||
public static Socket CreateSocket(EndPoint endPoint, ProtocolType protocolType = ProtocolType.Tcp) | |||||
{ | |||||
SocketType socketType; | |||||
switch (protocolType) | |||||
{ | |||||
case ProtocolType.Tcp: | |||||
socketType = SocketType.Stream; | |||||
break; | |||||
case ProtocolType.Udp: | |||||
socketType = SocketType.Dgram; | |||||
break; | |||||
default: | |||||
throw new NotSupportedException("Protocol " + protocolType + " doesn't supported!"); | |||||
} | |||||
if (endPoint is DnsEndPoint) | |||||
{ | |||||
var socket = new Socket(AddressFamily.InterNetworkV6, socketType, protocolType); | |||||
socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false); | |||||
return socket; | |||||
} | |||||
else | |||||
{ | |||||
return new Socket(endPoint.AddressFamily, socketType, protocolType); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -176,6 +176,7 @@ | |||||
<Compile Include="Controller\Strategy\IStrategy.cs" /> | <Compile Include="Controller\Strategy\IStrategy.cs" /> | ||||
<Compile Include="Proxy\Socks5Proxy.cs" /> | <Compile Include="Proxy\Socks5Proxy.cs" /> | ||||
<Compile Include="StringEx.cs" /> | <Compile Include="StringEx.cs" /> | ||||
<Compile Include="Util\SocketUtil.cs" /> | |||||
<Compile Include="Util\Util.cs" /> | <Compile Include="Util\Util.cs" /> | ||||
<Compile Include="View\ConfigForm.cs"> | <Compile Include="View\ConfigForm.cs"> | ||||
<SubType>Form</SubType> | <SubType>Form</SubType> | ||||