public async Task <SocksProxyUdpAssociateHandler> UdpAssociateAsync(EndPoint localEP) { //bind local ep Socket udpSocket = new Socket(localEP.AddressFamily, SocketType.Dgram, ProtocolType.Udp); udpSocket.Bind(localEP); //connect to proxy server Socket socket = await GetTcpConnectionAsync(_proxyEP); try { Stream stream = new WriteBufferedStream(new NetworkStream(socket)); await AuthenticateAsync(stream); EndPoint relayEP = await RequestAsync(stream, new SocksProxyRequest(SocksProxyRequestCommand.UdpAssociate, udpSocket.LocalEndPoint)); return(new SocksProxyUdpAssociateHandler(socket, udpSocket, relayEP)); } catch { if (socket != null) { socket.Dispose(); } if (udpSocket != null) { udpSocket.Dispose(); } throw; } }
private Stream GetConnection() { lock (_getConnectionLock) { Stream tcpStream = _tcpStream; if (tcpStream != null) { return(tcpStream); } Socket socket; if (_proxy == null) { if (_server.IPEndPoint == null) { _server.RecursiveResolveIPAddress(new SimpleDnsCache()); } socket = new Socket(_server.IPEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); IAsyncResult result = socket.BeginConnect(_server.IPEndPoint, null, null); if (!result.AsyncWaitHandle.WaitOne(_timeout)) { throw new SocketException((int)SocketError.TimedOut); } if (!socket.Connected) { throw new SocketException((int)SocketError.ConnectionRefused); } } else { socket = _proxy.Connect(_server.EndPoint, _timeout); } socket.SendTimeout = _timeout; socket.ReceiveTimeout = SOCKET_RECEIVE_TIMEOUT; socket.SendBufferSize = 512; socket.ReceiveBufferSize = 2048; socket.NoDelay = true; tcpStream = new WriteBufferedStream(GetNetworkStream(socket), 2048); _tcpStream = tcpStream; _readThread = new Thread(ReadDnsDatagramAsync); _readThread.IsBackground = true; _readThread.Start(); return(tcpStream); } }
protected override async Task <Socket> ConnectAsync(EndPoint remoteEP, Socket viaSocket) { try { Stream stream = new WriteBufferedStream(new NetworkStream(viaSocket)); await AuthenticateAsync(stream); await RequestAsync(stream, new SocksProxyRequest(SocksProxyRequestCommand.Connect, remoteEP)); return(viaSocket); } catch { viaSocket.Dispose(); throw; } }
private void AcceptTcpConnectionAsync(object parameter) { try { while (true) { Socket socket = _tcpListener.Accept(); socket.NoDelay = true; socket.SendTimeout = SOCKET_SEND_TIMEOUT; socket.ReceiveTimeout = SOCKET_RECV_TIMEOUT; ThreadPool.QueueUserWorkItem(delegate(object state) { try { using (Stream s = new WriteBufferedStream(new NetworkStream(socket, true), WRITE_BUFFERED_STREAM_SIZE)) { IPEndPoint remoteNodeEP = socket.RemoteEndPoint as IPEndPoint; if (remoteNodeEP.Address.IsIPv4MappedToIPv6) { remoteNodeEP = new IPEndPoint(remoteNodeEP.Address.MapToIPv4(), remoteNodeEP.Port); } _dhtNode.AcceptConnection(s, remoteNodeEP); } } catch (Exception ex) { Debug.Write(this.GetType().Name, ex); socket.Dispose(); } }); } } catch (ThreadAbortException) { //stopping } catch (Exception ex) { Debug.Write(this.GetType().Name, ex); } }
public async Task <SocksProxyBindHandler> BindAsync(AddressFamily family = AddressFamily.InterNetwork) { EndPoint endPoint; switch (family) { case AddressFamily.InterNetwork: endPoint = new IPEndPoint(IPAddress.Any, 0); break; case AddressFamily.InterNetworkV6: endPoint = new IPEndPoint(IPAddress.IPv6Any, 0); break; default: throw new NotSupportedException("Address family not supported."); } //connect to proxy server Socket socket = await GetTcpConnectionAsync(_proxyEP); try { Stream stream = new WriteBufferedStream(new NetworkStream(socket)); await AuthenticateAsync(stream); EndPoint bindEP = await RequestAsync(stream, new SocksProxyRequest(SocksProxyRequestCommand.Bind, endPoint)); return(new SocksProxyBindHandler(socket, bindEP)); } catch { socket.Dispose(); throw; } }
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(); } } }
private void Start(Stream stream) { try { WriteBufferedStream bufferedStream; if (!(stream is WriteBufferedStream)) { bufferedStream = new WriteBufferedStream(stream, 8 * 1024); } else { bufferedStream = stream as WriteBufferedStream; } //read client hello SecureChannelHandshakeHello clientHello = new SecureChannelHandshakeHello(bufferedStream); switch (clientHello.Version) { case 1: ProtocolV1(bufferedStream, clientHello); break; default: throw new SecureChannelException(SecureChannelCode.ProtocolVersionNotSupported, _remotePeerEP, _remotePeerUserId, "SecureChannel protocol version not supported: " + clientHello.Version); } } catch (SecureChannelException ex) { if (ex.Code == SecureChannelCode.RemoteError) { throw new SecureChannelException(ex.Code, _remotePeerEP, _remotePeerUserId, ex.Message, ex); } else { try { Stream s; if (_baseStream == null) { s = stream; } else { s = this; } new SecureChannelHandshakePacket(ex.Code).WriteTo(s); s.Flush(); } catch { } if (ex.PeerEP == null) { throw new SecureChannelException(ex.Code, _remotePeerEP, _remotePeerUserId, ex.Message, ex); } throw; } } catch (IOException) { throw; } catch { try { Stream s; if (_baseStream == null) { s = stream; } else { s = this; } new SecureChannelHandshakePacket(SecureChannelCode.UnknownException).WriteTo(s); s.Flush(); } catch { } throw; } }
private void ProtocolV1(WriteBufferedStream bufferedStream, SecureChannelHandshakeHello clientHello) { #region 1. hello handshake check //select crypto option SecureChannelCipherSuite availableCiphers = _supportedCiphers & clientHello.SupportedCiphers; if (availableCiphers == SecureChannelCipherSuite.None) { throw new SecureChannelException(SecureChannelCode.NoMatchingCipherAvailable, _remotePeerEP, _remotePeerUserId); } else if (availableCiphers.HasFlag(SecureChannelCipherSuite.DHE2048_RSA2048_WITH_AES256_CBC_HMAC_SHA256)) { _selectedCipher = SecureChannelCipherSuite.DHE2048_RSA2048_WITH_AES256_CBC_HMAC_SHA256; } else if (availableCiphers.HasFlag(SecureChannelCipherSuite.DHE2048_ANON_WITH_AES256_CBC_HMAC_SHA256)) { _selectedCipher = SecureChannelCipherSuite.DHE2048_ANON_WITH_AES256_CBC_HMAC_SHA256; } else { throw new SecureChannelException(SecureChannelCode.NoMatchingCipherAvailable, _remotePeerEP, _remotePeerUserId); } //match options if (_options != clientHello.Options) { throw new SecureChannelException(SecureChannelCode.NoMatchingOptionsAvailable, _remotePeerEP, _remotePeerUserId); } //write server hello SecureChannelHandshakeHello serverHello = new SecureChannelHandshakeHello(_selectedCipher, _options); serverHello.WriteTo(bufferedStream); #endregion #region 2. key exchange KeyAgreement keyAgreement; switch (_selectedCipher) { case SecureChannelCipherSuite.DHE2048_ANON_WITH_AES256_CBC_HMAC_SHA256: case SecureChannelCipherSuite.DHE2048_RSA2048_WITH_AES256_CBC_HMAC_SHA256: keyAgreement = new DiffieHellman(DiffieHellmanGroupType.RFC3526_GROUP14_2048BIT, KeyAgreementKeyDerivationFunction.Hmac, KeyAgreementKeyDerivationHashAlgorithm.SHA256); break; default: throw new SecureChannelException(SecureChannelCode.NoMatchingCipherAvailable, _remotePeerEP, _remotePeerUserId); } //send server key exchange data SecureChannelHandshakeKeyExchange serverKeyExchange = new SecureChannelHandshakeKeyExchange(keyAgreement, serverHello, clientHello, _preSharedKey); serverKeyExchange.WriteTo(bufferedStream); bufferedStream.Flush(); //read client key exchange data SecureChannelHandshakeKeyExchange clientKeyExchange = new SecureChannelHandshakeKeyExchange(bufferedStream); if (_options.HasFlag(SecureChannelOptions.PRE_SHARED_KEY_AUTHENTICATION_REQUIRED)) { if (!clientKeyExchange.IsPskAuthValid(serverHello, clientHello, _preSharedKey)) { throw new SecureChannelException(SecureChannelCode.PskAuthenticationFailed, _remotePeerEP, _remotePeerUserId); } } #endregion #region 3. enable encryption EnableEncryption(bufferedStream, serverHello, clientHello, keyAgreement, clientKeyExchange); #endregion #region 4. UserId based authentication switch (_selectedCipher) { case SecureChannelCipherSuite.DHE2048_RSA2048_WITH_AES256_CBC_HMAC_SHA256: if (_options.HasFlag(SecureChannelOptions.CLIENT_AUTHENTICATION_REQUIRED)) { //read client auth SecureChannelHandshakeAuthentication clientAuth = new SecureChannelHandshakeAuthentication(this); _remotePeerUserId = clientAuth.UserId; //authenticate client if (!clientAuth.IsSignatureValid(clientKeyExchange, serverHello, clientHello)) { throw new SecureChannelException(SecureChannelCode.PeerAuthenticationFailed, _remotePeerEP, _remotePeerUserId); } //check if client is trusted if (!clientAuth.IsTrustedUserId(_trustedUserIds)) { throw new SecureChannelException(SecureChannelCode.UntrustedRemotePeerUserId, _remotePeerEP, _remotePeerUserId); } } //write server auth new SecureChannelHandshakeAuthentication(serverKeyExchange, serverHello, clientHello, _userId, _privateKey).WriteTo(this); this.Flush(); break; case SecureChannelCipherSuite.DHE2048_ANON_WITH_AES256_CBC_HMAC_SHA256: break; //no auth for ANON default: throw new SecureChannelException(SecureChannelCode.NoMatchingCipherAvailable, _remotePeerEP, _remotePeerUserId); } #endregion }
private void ProtocolV4(Stream stream) { WriteBufferedStream bufferedStream = new WriteBufferedStream(stream, 8 * 1024); #region 1. hello handshake //read client hello SecureChannelHandshakeHello clientHello = new SecureChannelHandshakeHello(bufferedStream); //select crypto option _selectedCryptoOption = _supportedOptions & clientHello.CryptoOptions; if (_selectedCryptoOption == SecureChannelCryptoOptionFlags.None) { throw new SecureChannelException(SecureChannelCode.NoMatchingCryptoAvailable, _remotePeerEP, _remotePeerCert); } else if ((_selectedCryptoOption & SecureChannelCryptoOptionFlags.ECDHE256_RSA_WITH_AES256_CBC_HMAC_SHA256) > 0) { _selectedCryptoOption = SecureChannelCryptoOptionFlags.ECDHE256_RSA_WITH_AES256_CBC_HMAC_SHA256; } else if ((_selectedCryptoOption & SecureChannelCryptoOptionFlags.DHE2048_RSA_WITH_AES256_CBC_HMAC_SHA256) > 0) { _selectedCryptoOption = SecureChannelCryptoOptionFlags.DHE2048_RSA_WITH_AES256_CBC_HMAC_SHA256; } else { throw new SecureChannelException(SecureChannelCode.NoMatchingCryptoAvailable, _remotePeerEP, _remotePeerCert); } //send server hello SecureChannelHandshakeHello serverHello = new SecureChannelHandshakeHello(BinaryNumber.GenerateRandomNumber256(), _selectedCryptoOption); serverHello.WriteTo(bufferedStream); #endregion #region 2. key exchange SymmetricEncryptionAlgorithm encAlgo; string hashAlgo; KeyAgreement keyAgreement; switch (_selectedCryptoOption) { case SecureChannelCryptoOptionFlags.DHE2048_RSA_WITH_AES256_CBC_HMAC_SHA256: encAlgo = SymmetricEncryptionAlgorithm.Rijndael; hashAlgo = "SHA256"; keyAgreement = new DiffieHellman(DiffieHellmanGroupType.RFC3526, 2048, KeyAgreementKeyDerivationFunction.Hmac, KeyAgreementKeyDerivationHashAlgorithm.SHA256); break; case SecureChannelCryptoOptionFlags.ECDHE256_RSA_WITH_AES256_CBC_HMAC_SHA256: encAlgo = SymmetricEncryptionAlgorithm.Rijndael; hashAlgo = "SHA256"; keyAgreement = new TechnitiumLibrary.Security.Cryptography.ECDiffieHellman(256, KeyAgreementKeyDerivationFunction.Hmac, KeyAgreementKeyDerivationHashAlgorithm.SHA256); break; default: throw new SecureChannelException(SecureChannelCode.NoMatchingCryptoAvailable, _remotePeerEP, _remotePeerCert); } //send server key exchange data new SecureChannelHandshakeKeyExchange(keyAgreement.GetPublicKey(), _serverCredentials.PrivateKey, hashAlgo).WriteTo(bufferedStream); bufferedStream.Flush(); //read client key exchange data SecureChannelHandshakeKeyExchange clientKeyExchange = new SecureChannelHandshakeKeyExchange(bufferedStream); //generate master key byte[] masterKey = GenerateMasterKey(clientHello, serverHello, _preSharedKey, keyAgreement, clientKeyExchange.PublicKey); //verify master key using HMAC authentication { SecureChannelHandshakeAuthentication clientAuthentication = new SecureChannelHandshakeAuthentication(bufferedStream); if (!clientAuthentication.IsValid(serverHello, masterKey)) { throw new SecureChannelException(SecureChannelCode.ProtocolAuthenticationFailed, _remotePeerEP, _remotePeerCert); } SecureChannelHandshakeAuthentication serverAuthentication = new SecureChannelHandshakeAuthentication(clientHello, masterKey); serverAuthentication.WriteTo(bufferedStream); bufferedStream.Flush(); } //enable channel encryption switch (encAlgo) { case SymmetricEncryptionAlgorithm.Rijndael: //using MD5 for generating AES IV of 128bit block size HashAlgorithm md5Hash = HashAlgorithm.Create("MD5"); byte[] eIV = md5Hash.ComputeHash(serverHello.Nonce.Number); byte[] dIV = md5Hash.ComputeHash(clientHello.Nonce.Number); //create encryption and decryption objects SymmetricCryptoKey encryptionKey = new SymmetricCryptoKey(SymmetricEncryptionAlgorithm.Rijndael, masterKey, eIV, PaddingMode.None); SymmetricCryptoKey decryptionKey = new SymmetricCryptoKey(SymmetricEncryptionAlgorithm.Rijndael, masterKey, dIV, PaddingMode.None); //enable encryption EnableEncryption(stream, encryptionKey, decryptionKey, new HMACSHA256(masterKey), new HMACSHA256(masterKey)); break; default: throw new SecureChannelException(SecureChannelCode.NoMatchingCryptoAvailable, _remotePeerEP, _remotePeerCert); } //channel encryption is ON! #endregion #region 3. exchange & verify certificates & signatures if (!IsReNegotiating()) { //read client certificate _remotePeerCert = new SecureChannelHandshakeCertificate(this).Certificate; //verify client certificate try { _remotePeerCert.Verify(_trustedRootCertificates); } catch (Exception ex) { throw new SecureChannelException(SecureChannelCode.InvalidRemoteCertificate, _remotePeerEP, _remotePeerCert, "Invalid remote certificate.", ex); } } //verify key exchange signature switch (_selectedCryptoOption) { case SecureChannelCryptoOptionFlags.DHE2048_RSA_WITH_AES256_CBC_HMAC_SHA256: case SecureChannelCryptoOptionFlags.ECDHE256_RSA_WITH_AES256_CBC_HMAC_SHA256: if (_remotePeerCert.PublicKeyEncryptionAlgorithm != AsymmetricEncryptionAlgorithm.RSA) { throw new SecureChannelException(SecureChannelCode.InvalidRemoteCertificateAlgorithm, _remotePeerEP, _remotePeerCert); } if (!clientKeyExchange.IsSignatureValid(_remotePeerCert, "SHA256")) { throw new SecureChannelException(SecureChannelCode.InvalidRemoteKeyExchangeSignature, _remotePeerEP, _remotePeerCert); } break; default: throw new SecureChannelException(SecureChannelCode.NoMatchingCryptoAvailable, _remotePeerEP, _remotePeerCert); } if ((_manager != null) && !_manager.ProceedConnection(_remotePeerCert)) { throw new SecureChannelException(SecureChannelCode.SecurityManagerDeclinedAccess, _remotePeerEP, _remotePeerCert, "Security manager declined access."); } //send server certificate if (!IsReNegotiating()) { new SecureChannelHandshakeCertificate(_serverCredentials.Certificate).WriteTo(this); this.Flush(); } #endregion }
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(); } } }