/// <summary> /// The establish method is used to connect to the server and try to request access to the specific address. /// </summary> /// <param name="address"> /// The requested address. /// </param> /// <param name="port"> /// The requested port. /// </param> /// <param name="client"> /// The client to connect with. /// </param> /// <param name="headerData"> /// The data to send after connecting. /// </param> /// <param name="sendDataDelegate"> /// The send callback. /// </param> /// <param name="receiveDataDelegate"> /// The receive callback. /// </param> /// <param name="connectionStatusCallback"> /// The connection status changed callback. /// </param> public override void Establish( string address, ushort port, ProxyClient client, byte[] headerData = null, DataCallbackDelegate sendDataDelegate = null, DataCallbackDelegate receiveDataDelegate = null, ConnectionCallbackDelegate connectionStatusCallback = null) { try { this.requestedAddress = address; this.requestedPort = port; this.ParentClient = client; this.SendDataDelegate = sendDataDelegate; this.ReceiveDataDelegate = receiveDataDelegate; this.ConnectionStatusCallback = connectionStatusCallback; this.UnderlyingSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); this.UnderlyingSocket.BeginConnect( this.ServerAddress, this.ServerPort, delegate(IAsyncResult ar) { try { this.UnderlyingSocket.EndConnect(ar); this.UnderlyingSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.DontFragment, true); this.ParentClient.Controller.FailAttempts = 0; HttpForger.SendRequest( this.UnderlyingSocket, string.IsNullOrWhiteSpace(this.ServerDomain) ? "~" : this.ServerDomain, "~", this.ForgerCompatibility ? "LINK" : "GET"); if (new HttpForger(this.UnderlyingSocket).ReceiveResponse() != HttpForger.CurrentState.ValidData) { this.Close( "HTTPForger failed to validate server response.", null, ErrorRenderer.HttpHeaderCode.C417ExpectationFailed); return; } this.Protocol = new PeaRoxyProtocol( this.UnderlyingSocket, this.encryptionType, this.compressionTypes) { ReceivePacketSize = this.ParentClient.ReceivePacketSize, SendPacketSize = this.ParentClient.SendPacketSize, CloseCallback = this.Close }; byte[] clientRequest; if (this.Username.Trim() != string.Empty && this.Password != string.Empty) { byte[] username = Encoding.ASCII.GetBytes(this.Username.Trim()); byte[] password = Encoding.ASCII.GetBytes(this.Password); password = MD5.Create().ComputeHash(password); if (username.Length > 255) { this.Close( "Username is too long.", null, ErrorRenderer.HttpHeaderCode.C417ExpectationFailed); return; } clientRequest = new byte[3 + username.Length + password.Length]; clientRequest[0] = (byte)Common.AuthenticationMethods.UserPass; clientRequest[1] = (byte)username.Length; clientRequest[username.Length + 2] = (byte)password.Length; Array.Copy(username, 0, clientRequest, 2, username.Length); Array.Copy(password, 0, clientRequest, username.Length + 3, password.Length); } else { clientRequest = new byte[1]; clientRequest[0] = (byte)Common.AuthenticationMethods.None; } byte clientAddressType; IPAddress clientIp; byte[] clientAddressBytes; if (IPAddress.TryParse(this.requestedAddress, out clientIp)) { clientAddressBytes = clientIp.GetAddressBytes(); if (clientAddressBytes.Length == 16) { clientAddressType = 4; } else if (clientAddressBytes.Length == 4) { clientAddressType = 1; } else { this.Close( "Unknown IP Type.", null, ErrorRenderer.HttpHeaderCode.C417ExpectationFailed); return; } } else { clientAddressType = 3; clientAddressBytes = Encoding.ASCII.GetBytes(this.requestedAddress); } byte[] clientRequestAddress = new byte[6 + clientAddressBytes.Length + ((clientAddressType == 3) ? 1 : 0)]; clientRequestAddress[0] = ProtocolVersion; clientRequestAddress[1] = 0; clientRequestAddress[2] = 0; clientRequestAddress[3] = clientAddressType; if (clientAddressType == 3) { if (clientAddressBytes.Length > 255) { this.Close( "Hostname is too long.", null, ErrorRenderer.HttpHeaderCode.C417ExpectationFailed); return; } clientRequestAddress[4] = (byte)clientAddressBytes.Length; } Array.Copy( clientAddressBytes, 0, clientRequestAddress, 4 + ((clientAddressType == 3) ? 1 : 0), clientAddressBytes.Length); clientRequestAddress[clientRequestAddress.GetUpperBound(0) - 1] = (byte)Math.Floor(this.requestedPort / 256d); clientRequestAddress[clientRequestAddress.GetUpperBound(0)] = (byte)(this.requestedPort % 256); Array.Resize(ref clientRequest, clientRequestAddress.Length + clientRequest.Length); Array.Copy( clientRequestAddress, 0, clientRequest, clientRequest.Length - clientRequestAddress.Length, clientRequestAddress.Length); this.Protocol.Write(clientRequest, false); byte[] serverResponse = this.Protocol.Read(); if (serverResponse == null) { this.Close( "Connection closed by server or timed out.", null, ErrorRenderer.HttpHeaderCode.C504GatewayTimeout); return; } if (serverResponse[0] != clientRequestAddress[0]) { this.Close( string.Format( "Server version is different from what we expect. Server's version: {0}, Expected: {1}", serverResponse[0], clientRequestAddress[0]), null, ErrorRenderer.HttpHeaderCode.C502BadGateway); return; } if (serverResponse[1] == 99) { this.Close( "Connection failed, Error Code: Authentication Failed.", null, ErrorRenderer.HttpHeaderCode.C417ExpectationFailed); return; } if (serverResponse[1] != 0) { this.Close( "Connection failed, Error Code: " + serverResponse[1], null, ErrorRenderer.HttpHeaderCode.C502BadGateway); return; } if (this.Password != string.Empty) { this.Protocol.EncryptionKey = Encoding.ASCII.GetBytes(this.Password); } this.IsServerValid = true; if (headerData != null && Common.IsSocketConnected(this.UnderlyingSocket)) { this.Protocol.Write(headerData, true); } this.currentTimeout = this.NoDataTimeout * 1000; client.Controller.ClientMoveToRouting(this); } catch (Exception ex) { this.ParentClient.Controller.FailAttempts++; this.Close(ex.Message, ex.StackTrace, ErrorRenderer.HttpHeaderCode.C504GatewayTimeout); } }, null); } catch (Exception e) { this.Close(e.Message, e.StackTrace); } }
/// <summary> /// The method to handle the accepting process, should call repeatedly /// </summary> public void Accepting() { try { if (Common.IsSocketConnected(this.UnderlyingSocket) && this.currentTimeout > 0) { switch (this.CurrentStage) { case RequestStages.JustConnected: string clientAddress = this.UnderlyingSocket.RemoteEndPoint.ToString(); if ( this.Controller.Settings.BlackListedAddresses.Any( blacklist => Common.DoesMatchWildCard(clientAddress, blacklist))) { this.Close("Blacklisted Client: " + clientAddress); return; } this.currentTimeout = this.NoDataTimeout * 1000; this.forger = new HttpForger( this.UnderlyingSocket, this.Controller.Settings.PeaRoxyDomain, false); this.CurrentStage = RequestStages.WaitingForForger; break; case RequestStages.WaitingForForger: var result = this.forger.ReceiveRequest(); if (result == HttpForger.CurrentState.ValidData) { HttpForger.SendResponse(this.UnderlyingSocket); this.Protocol = new PeaRoxyProtocol( this.UnderlyingSocket, this.underlyingClientEncryption, this.underlyingClientCompression) { ReceivePacketSize = this.underlyingClientReceivePacketSize, SendPacketSize = this.underlyingClientSendPacketSize, CloseCallback = this.Close, ClientSupportedCompressionType = this.ClientSupportedCompressionType, ClientSupportedEncryptionType = this.ClientSupportedEncryptionType }; this.Id = Screen.ClientConnected( this.Username, "C." + this.Protocol.UnderlyingSocket.RemoteEndPoint); // Report that a new client connected this.currentTimeout = this.NoDataTimeout * 1000; this.CurrentStage = RequestStages.WaitingForWelcomeMessage; } else if (result == HttpForger.CurrentState.InvalidData) { System.IO.File.WriteAllBytes( System.IO.Path.GetRandomFileName(), this.forger.HeaderBytes); if (this.Controller.Settings.HttpForwardingPort == 0 || !Common.IsSocketConnected(this.UnderlyingSocket)) { this.Close(); } else { this.isForwarded = true; this.CurrentStage = RequestStages.ResolvingLocalServer; this.Id = Screen.ClientConnected( this.Username, "F." + this.UnderlyingSocket.RemoteEndPoint); } } break; case RequestStages.WaitingForWelcomeMessage: if (this.Protocol.IsDataAvailable()) { byte serverErrorCode = 0; byte[] clientRequest = this.Protocol.Read(); // Read data from client if (clientRequest == null || clientRequest.Length <= 0) { // Check if data is correct this.Close("8. " + "No data received. Connection Timeout."); return; } ConfigUser acceptedUser = null; // Select Authentication Type if ((byte)this.SelectedAuthMode != clientRequest[0]) { serverErrorCode = 99; } else { switch (this.SelectedAuthMode) { case Common.AuthenticationMethods.None: Array.Copy(clientRequest, 1, clientRequest, 0, clientRequest.Length - 1); break; case Common.AuthenticationMethods.UserPass: { // Authentication using user name and password hash string username = Encoding.ASCII.GetString( clientRequest, 2, clientRequest[1]); // Read UserName byte[] passwordHash = new byte[clientRequest[clientRequest[1] + 2]]; // Initialize Password Hash Byte Array Array.Copy( clientRequest, clientRequest[1] + 3, passwordHash, 0, passwordHash.Length); // Read Password Hash acceptedUser = this.Controller.Settings.AthorizedUsers.FirstOrDefault( user => user.Username.Equals( username, StringComparison.OrdinalIgnoreCase) && user.Hash.SequenceEqual(passwordHash)); if (acceptedUser == null) { // Let check if we have a fail result with authentication, If so, Close Connection serverErrorCode = 99; } else { Screen.ChangeUser(this.Username, acceptedUser.Username, this.Id); // Let inform that user is changed, We are not "Anonymous" anymore this.Username = acceptedUser.Username; // Save user name in userId field for later access to screen Array.Copy( clientRequest, clientRequest[1] + passwordHash.Length + 3, clientRequest, 0, clientRequest.Length - (clientRequest[1] + passwordHash.Length + 3)); } } break; } } string clientRequestedAddress = null; ushort clientRequestedPort = 0; if (serverErrorCode == 0) { // Authentication OK if (clientRequest[0] != ServerPeaRoxyVersion) { // Check again if client use same version as we are this.Close(); // "6. " + "Unknown version, Expected " + version.ToString()); return; } byte clientAddressType = clientRequest[3]; // Read address type client want to connect byte[] clientPlainRequestedAddress; switch (clientAddressType) { // Getting request address and port depending to address type case 1: // IPv4 clientPlainRequestedAddress = new byte[4]; Array.Copy(clientRequest, 4, clientPlainRequestedAddress, 0, 4); clientRequestedAddress = new IPAddress(clientPlainRequestedAddress).ToString(); clientRequestedPort = (ushort)((ushort)(clientRequest[8] * 256) + clientRequest[9]); break; case 3: // Domain Name clientPlainRequestedAddress = new byte[clientRequest[4]]; Array.Copy( clientRequest, 5, clientPlainRequestedAddress, 0, clientRequest[4]); clientRequestedAddress = Encoding.ASCII.GetString(clientPlainRequestedAddress); clientRequestedPort = (ushort) ((ushort)(clientRequest[5 + clientRequest[4]] * 256) + clientRequest[5 + clientRequest[4] + 1]); break; case 4: // IPv6 clientPlainRequestedAddress = new byte[16]; Array.Copy(clientRequest, 4, clientPlainRequestedAddress, 0, 16); clientRequestedAddress = new IPAddress(clientPlainRequestedAddress).ToString(); clientRequestedPort = (ushort)((ushort)(clientRequest[20] * 256) + clientRequest[21]); break; default: serverErrorCode = 8; // This type of address is not supported break; } if (clientRequestedAddress != null) { string clientRequestedConnectionString = clientRequestedAddress.ToLower().Trim() + ":" + clientRequestedPort; if ( this.Controller.Settings.BlackListedAddresses.Any( blacklist => Common.DoesMatchWildCard(clientRequestedConnectionString, blacklist))) { this.Close("Blacklisted Request: " + clientRequestedAddress); return; } } } // Initialize server response to this request byte[] serverResponse = new byte[2]; serverResponse[0] = ServerPeaRoxyVersion; serverResponse[1] = serverErrorCode; this.Protocol.Write(serverResponse, true); // Send response to client if (serverErrorCode != 0 || clientRequestedAddress == null) { // Check if we have any problem with request this.Close("5. " + "Response Error, Code: " + serverErrorCode); return; } if (acceptedUser != null) { this.Protocol.EncryptionKey = Encoding.ASCII.GetBytes(acceptedUser.Password); } Screen.SetRequestIpAddress( this.Username, this.Id, clientRequestedAddress + ":" + clientRequestedPort); // Inform that we have a request for an address this.destinationSocket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); this.destinationSocket.BeginConnect( clientRequestedAddress, clientRequestedPort, delegate(IAsyncResult ar) { try { this.destinationSocket.EndConnect(ar); this.destinationSocket.Blocking = false; this.destinationSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.DontFragment, true); this.currentTimeout = this.NoDataTimeout * 1000; this.Controller.ClientMoveToRouting(this); this.CurrentStage = RequestStages.Routing; } catch (Exception) { this.Close(); } }, null); this.currentTimeout = this.NoDataTimeout * 1000; this.CurrentStage = RequestStages.ConnectingToTheServer; } break; case RequestStages.ResolvingLocalServer: Screen.ChangeUser(this.Username, "Forwarder", this.Id); this.Username = "******"; Screen.SetRequestIpAddress( this.Username, this.Id, this.Controller.Settings.HttpForwardingIp + ":" + this.Controller.Settings.HttpForwardingPort); // Inform that we have a request for an address this.destinationSocket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); this.destinationSocket.BeginConnect( this.Controller.Settings.HttpForwardingIp, this.Controller.Settings.HttpForwardingPort, delegate(IAsyncResult ar) { try { this.destinationSocket.EndConnect(ar); this.forwarder = new Forwarder( this.destinationSocket, this.underlyingClientReceivePacketSize); this.forwarder.Write(this.forger.HeaderBytes); this.currentTimeout = this.NoDataTimeout * 1000; this.Controller.ClientMoveToRouting(this); this.CurrentStage = RequestStages.Routing; } catch (Exception e) { this.Close("9. " + e.Message + "\r\n" + e.StackTrace); } }, null); this.currentTimeout = this.NoDataTimeout * 1000; this.CurrentStage = RequestStages.ConnectingToTheServer; break; case RequestStages.ConnectingToTheServer: break; } this.currentTimeout--; } else { this.Close(); } } catch (Exception e) { if (e.TargetSite.Name == "Receive") { this.Close(); } else { this.Close("4. " + e.Message + "\r\n" + e.StackTrace); } } }