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(); } } }
public async Task StartAsync() { bool dontDispose = false; try { NetworkStream localStream = new NetworkStream(_localSocket); Stream remoteStream = null; string lastHost = null; int lastPort = 0; while (true) { HttpRequest httpRequest; { Task <HttpRequest> task = HttpRequest.ReadRequestAsync(localStream); using (CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource()) { if (await Task.WhenAny(task, Task.Delay(CLIENT_REQUEST_TIMEOUT, timeoutCancellationTokenSource.Token)) != task) { return; //request timed out } timeoutCancellationTokenSource.Cancel(); //cancel delay task } httpRequest = await task; } if (httpRequest == null) { return; //connection closed gracefully by client } if (_authenticationManager != null) { string proxyAuth = httpRequest.Headers[HttpRequestHeader.ProxyAuthorization]; if (string.IsNullOrEmpty(proxyAuth)) { await SendResponseAsync(407, "<h1>Proxy Authentication Required</h1>"); return; } string username; string password; { string[] parts = proxyAuth.Split(new char[] { ' ' }, 2); if (!parts[0].Equals("BASIC", StringComparison.OrdinalIgnoreCase) || (parts.Length < 2)) { await SendResponseAsync(407, "<h1>Proxy Authentication Required</h1><p>Proxy authentication method is not supported.</p>"); return; } string[] credParts = Encoding.ASCII.GetString(Convert.FromBase64String(parts[1])).Split(new char[] { ':' }, 2); if (credParts.Length != 2) { await SendResponseAsync(407, "<h1>Proxy Authentication Required</h1><p>Proxy authentication method is not supported.</p>"); return; } username = credParts[0]; password = credParts[1]; } if (!_authenticationManager.Authenticate(username, password)) { await SendResponseAsync(407, "<h1>Proxy Authentication Required</h1><p>Invalid username or password.</p>"); return; } } if (httpRequest.HttpMethod.Equals("CONNECT", StringComparison.OrdinalIgnoreCase)) { await DoConnectAsync(localStream, httpRequest); dontDispose = true; break; } else { #region connect to remote server string host; int port; string requestPathAndQuery; if (Uri.TryCreate(httpRequest.RequestPathAndQuery, UriKind.Absolute, out Uri requestUri)) { host = requestUri.Host; port = requestUri.Port; requestPathAndQuery = requestUri.PathAndQuery; } else { string hostHeader = httpRequest.Headers[HttpRequestHeader.Host]; if (string.IsNullOrEmpty(hostHeader)) { throw new HttpProxyServerException("Invalid proxy request."); } string[] parts = hostHeader.Split(':'); host = parts[0]; if (parts.Length > 1) { port = int.Parse(parts[1]); } else { port = 80; } requestPathAndQuery = httpRequest.RequestPathAndQuery; } if (!host.Equals(lastHost) || port != lastPort || !_remoteSocket.Connected) { if (_remoteSocket != null) { if (_remoteSocket.Connected) { try { _remoteSocket.Shutdown(SocketShutdown.Both); } catch { } } _remoteSocket.Dispose(); } _remoteSocket = await _connectionManager.ConnectAsync(EndPointExtension.GetEndPoint(host, port)); remoteStream = new WriteBufferedStream(new NetworkStream(_remoteSocket), 512); lastHost = host; lastPort = port; //pipe response stream _ = _remoteSocket.CopyToAsync(_localSocket).ContinueWith(delegate(Task prevTask) { Dispose(); }); } #endregion #region relay client request to server foreach (string header in httpRequest.Headers.AllKeys) { if (header.StartsWith("Proxy-", StringComparison.OrdinalIgnoreCase)) { httpRequest.Headers.Remove(header); } } await remoteStream.WriteAsync(Encoding.ASCII.GetBytes(httpRequest.HttpMethod + " " + requestPathAndQuery + " " + httpRequest.Protocol + "\r\n")); await remoteStream.WriteAsync(httpRequest.Headers.ToByteArray()); if (httpRequest.InputStream != null) { await httpRequest.InputStream.CopyToAsync(remoteStream); } await remoteStream.FlushAsync(); #endregion } } } catch (Exception ex) { await SendResponseAsync(ex); } finally { if (!dontDispose) { Dispose(); } } }