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; } } }