public override async Task <int> UdpQueryAsync(ArraySegment <byte> request, ArraySegment <byte> response, EndPoint remoteEP, int timeout = 10000, int retries = 1, bool expBackoffTimeout = false, CancellationToken cancellationToken = default) { if (IsBypassed(remoteEP)) { IPEndPoint ep = await remoteEP.GetIPEndPointAsync(); using (Socket socket = new Socket(ep.AddressFamily, SocketType.Dgram, ProtocolType.Udp)) { return(await socket.UdpQueryAsync(request, response, ep, timeout, retries, expBackoffTimeout, cancellationToken)); } } if (_viaProxy != null) { throw new NotSupportedException("Cannot chain proxies for Udp protocol."); } using (SocksProxyUdpAssociateHandler proxyUdpRequestHandler = await UdpAssociateAsync()) { return(await proxyUdpRequestHandler.UdpQueryAsync(request, response, remoteEP, timeout, retries, expBackoffTimeout, cancellationToken)); } }
public override async Task <bool> IsUdpAvailableAsync() { if (_isUdpAvailableChecked) { return(_isUdpAvailable); } SocksProxyUdpAssociateHandler udpHandler = null; try { udpHandler = await UdpAssociateAsync(); _isUdpAvailable = true; } catch (SocksProxyException ex) { if (ex.ReplyCode == SocksProxyReplyCode.CommandNotSupported) { _isUdpAvailable = false; } else { throw; } } finally { if (udpHandler != null) { udpHandler.Dispose(); } } _isUdpAvailableChecked = true; return(_isUdpAvailable); }
public async Task StartAsync() { bool dontDispose = false; try { NetworkStream localNetworkStream = new NetworkStream(_localSocket); Stream localStream = new WriteBufferedStream(localNetworkStream, 512); #region authenticate SocksProxyNegotiationRequest negotiationRequest = await SocksProxyNegotiationRequest.ReadRequestAsync(localStream).WithTimeout(CLIENT_WAIT_TIMEOUT); if (!negotiationRequest.IsVersionSupported) { await new SocksProxyNegotiationReply(SocksProxyAuthenticationMethod.NoAcceptableMethods).WriteToAsync(localStream); await localStream.FlushAsync(); return; } //match method and authenticate bool methodMatched = false; SocksProxyAuthenticationMethod serverMethod = _authenticationManager == null ? SocksProxyAuthenticationMethod.NoAuthenticationRequired : SocksProxyAuthenticationMethod.UsernamePassword; foreach (SocksProxyAuthenticationMethod method in negotiationRequest.Methods) { if (method == serverMethod) { //method matches await new SocksProxyNegotiationReply(method).WriteToAsync(localStream); await localStream.FlushAsync(); switch (serverMethod) { case SocksProxyAuthenticationMethod.NoAuthenticationRequired: methodMatched = true; break; case SocksProxyAuthenticationMethod.UsernamePassword: //read method version SocksProxyAuthenticationRequest authenticationRequest = await SocksProxyAuthenticationRequest.ReadRequestAsync(localStream).WithTimeout(CLIENT_WAIT_TIMEOUT); if (!authenticationRequest.IsVersionSupported) { await new SocksProxyAuthenticationReply(SocksProxyAuthenticationStatus.Failure).WriteToAsync(localStream); await localStream.FlushAsync(); return; } if (!_authenticationManager.Authenticate(authenticationRequest.Username, authenticationRequest.Password)) { await new SocksProxyAuthenticationReply(SocksProxyAuthenticationStatus.Failure).WriteToAsync(localStream); await localStream.FlushAsync(); return; } await new SocksProxyAuthenticationReply(SocksProxyAuthenticationStatus.Success).WriteToAsync(localStream); await localStream.FlushAsync(); methodMatched = true; break; } break; } } if (!methodMatched) { //no method matched await new SocksProxyNegotiationReply(SocksProxyAuthenticationMethod.NoAcceptableMethods).WriteToAsync(localStream); await localStream.FlushAsync(); return; } #endregion #region process request //read request SocksProxyRequest request = await SocksProxyRequest.ReadRequestAsync(localStream).WithTimeout(CLIENT_WAIT_TIMEOUT); if (!request.IsVersionSupported) { await new SocksProxyReply(SocksProxyReplyCode.GeneralSocksServerFailure).WriteToAsync(localStream); await localStream.FlushAsync(); return; } //process command SocksProxyReplyCode reply; EndPoint bindEP; switch (request.Command) { case SocksProxyRequestCommand.Connect: { try { _remoteSocket = await _connectionManager.ConnectAsync(request.DestinationEndPoint); reply = SocksProxyReplyCode.Succeeded; bindEP = _remoteSocket.LocalEndPoint; } catch (SocketException ex) { switch (ex.SocketErrorCode) { case SocketError.NetworkUnreachable: reply = SocksProxyReplyCode.NetworkUnreachable; break; case SocketError.HostUnreachable: reply = SocksProxyReplyCode.HostUnreachable; break; case SocketError.ConnectionRefused: reply = SocksProxyReplyCode.ConnectionRefused; break; default: reply = SocksProxyReplyCode.GeneralSocksServerFailure; break; } bindEP = new IPEndPoint(IPAddress.Any, 0); } } break; case SocksProxyRequestCommand.Bind: { _bindHandler = await _connectionManager.GetBindHandlerAsync(request.DestinationEndPoint.AddressFamily); reply = _bindHandler.ReplyCode; bindEP = _bindHandler.ProxyLocalEndPoint; } break; case SocksProxyRequestCommand.UdpAssociate: { switch (_localSocket.LocalEndPoint.AddressFamily) { case AddressFamily.InterNetwork: case AddressFamily.InterNetworkV6: EndPoint localEP = new IPEndPoint((_localSocket.LocalEndPoint as IPEndPoint).Address, 0); _udpRelaySocket = new Socket(localEP.AddressFamily, SocketType.Dgram, ProtocolType.Udp); _udpRelaySocket.Bind(localEP); reply = SocksProxyReplyCode.Succeeded; bindEP = _udpRelaySocket.LocalEndPoint as IPEndPoint; break; default: reply = SocksProxyReplyCode.AddressTypeNotSupported; bindEP = new IPEndPoint(IPAddress.Any, 0); break; } } break; default: reply = SocksProxyReplyCode.CommandNotSupported; bindEP = new IPEndPoint(IPAddress.Any, 0); break; } //send response await new SocksProxyReply(reply, bindEP).WriteToAsync(localStream); await localStream.FlushAsync(); if (reply != SocksProxyReplyCode.Succeeded) { return; //nothing to do further } //final command process switch (request.Command) { case SocksProxyRequestCommand.Connect: { //pipe sockets _ = _localSocket.CopyToAsync(_remoteSocket).ContinueWith(delegate(Task prevTask) { Dispose(); }); _ = _remoteSocket.CopyToAsync(_localSocket).ContinueWith(delegate(Task prevTask) { Dispose(); }); dontDispose = true; } break; case SocksProxyRequestCommand.Bind: { try { _remoteSocket = await _bindHandler.AcceptAsync().WithTimeout(CLIENT_WAIT_TIMEOUT); } catch (SocksProxyException ex) { //send second reply await new SocksProxyReply(ex.ReplyCode, _bindHandler.ProxyLocalEndPoint).WriteToAsync(localStream); await localStream.FlushAsync(); } catch { //send second reply await new SocksProxyReply(SocksProxyReplyCode.GeneralSocksServerFailure, _bindHandler.ProxyLocalEndPoint).WriteToAsync(localStream); await localStream.FlushAsync(); } if (_remoteSocket != null) { _bindHandler.Dispose(); //send second reply await new SocksProxyReply(SocksProxyReplyCode.Succeeded, _bindHandler.ProxyRemoteEndPoint).WriteToAsync(localStream); await localStream.FlushAsync(); //pipe sockets _ = _localSocket.CopyToAsync(_remoteSocket).ContinueWith(delegate(Task prevTask) { Dispose(); }); _ = _remoteSocket.CopyToAsync(_localSocket).ContinueWith(delegate(Task prevTask) { Dispose(); }); dontDispose = true; } } break; case SocksProxyRequestCommand.UdpAssociate: { EndPoint localEP = null; switch (_localSocket.LocalEndPoint.AddressFamily) { case AddressFamily.InterNetwork: localEP = new IPEndPoint(IPAddress.Any, 0); break; case AddressFamily.InterNetworkV6: localEP = new IPEndPoint(IPAddress.IPv6Any, 0); break; default: throw new NotSupportedException(); } using (IProxyServerUdpAssociateHandler udpRemoteHandler = await _connectionManager.GetUdpAssociateHandlerAsync(localEP)) { using (IProxyServerUdpAssociateHandler udpLocalHandler = new SocksProxyUdpAssociateHandler(_localSocket, _udpRelaySocket, new IPEndPoint((_localSocket.RemoteEndPoint as IPEndPoint).Address, (request.DestinationEndPoint as IPEndPoint).Port))) { _ = CopyToAsync(udpRemoteHandler, udpLocalHandler); await CopyToAsync(udpLocalHandler, udpRemoteHandler); } } } break; } #endregion } finally { if (!dontDispose) { Dispose(); } } }