133 lines
3.7 KiB
C#
133 lines
3.7 KiB
C#
using System;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
|
|
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);
|
|
}
|
|
|
|
private class AutoReleaseAsyncResult : IAsyncResult
|
|
{
|
|
|
|
public bool IsCompleted { get; } = true;
|
|
public WaitHandle AsyncWaitHandle { get; } = null;
|
|
public object AsyncState { get; set; }
|
|
public bool CompletedSynchronously { get; } = true;
|
|
|
|
public TcpUserToken UserToken { get; set; }
|
|
|
|
~AutoReleaseAsyncResult()
|
|
{
|
|
UserToken.Dispose();
|
|
}
|
|
}
|
|
|
|
private class TcpUserToken
|
|
{
|
|
public AsyncCallback Callback { get; private set; }
|
|
public SocketAsyncEventArgs Args { get; private set; }
|
|
public object AsyncState { get; private set; }
|
|
|
|
public TcpUserToken(AsyncCallback callback, object state, SocketAsyncEventArgs args)
|
|
{
|
|
Callback = callback;
|
|
AsyncState = state;
|
|
Args = args;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Args?.Dispose();
|
|
Callback = null;
|
|
Args = null;
|
|
AsyncState = null;
|
|
}
|
|
}
|
|
|
|
private static void OnTcpConnectCompleted(object sender, SocketAsyncEventArgs args)
|
|
{
|
|
args.Completed -= OnTcpConnectCompleted;
|
|
TcpUserToken token = (TcpUserToken) args.UserToken;
|
|
|
|
AutoReleaseAsyncResult r = new AutoReleaseAsyncResult
|
|
{
|
|
AsyncState = token.AsyncState,
|
|
UserToken = token
|
|
};
|
|
|
|
token.Callback(r);
|
|
}
|
|
|
|
public static void BeginConnectTcp(EndPoint endPoint, AsyncCallback callback, object state)
|
|
{
|
|
var arg = new SocketAsyncEventArgs();
|
|
arg.RemoteEndPoint = endPoint;
|
|
arg.Completed += OnTcpConnectCompleted;
|
|
arg.UserToken = new TcpUserToken(callback, state, arg);
|
|
|
|
|
|
Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, arg);
|
|
}
|
|
|
|
public static Socket EndConnectTcp(IAsyncResult asyncResult)
|
|
{
|
|
var r = asyncResult as AutoReleaseAsyncResult;
|
|
if (r == null)
|
|
{
|
|
throw new ArgumentException("Invalid asyncResult.", nameof(asyncResult));
|
|
}
|
|
|
|
var tut = r.UserToken;
|
|
|
|
var arg = tut.Args;
|
|
|
|
if (arg.SocketError != SocketError.Success)
|
|
{
|
|
if (arg.ConnectByNameError != null)
|
|
{
|
|
throw arg.ConnectByNameError;
|
|
}
|
|
|
|
var ex = new SocketException((int)arg.SocketError);
|
|
throw ex;
|
|
}
|
|
|
|
var so = tut.Args.ConnectSocket;
|
|
|
|
so.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
|
|
|
|
return so;
|
|
}
|
|
}
|
|
}
|