/// <summary> /// Read and execute request. /// </summary> /// <param name="request"> /// A <see cref="Request"/> /// </param> public static void ProcessHttpConnect(Request request, Stream clientStream, CachedConnection remote) { request.Response = new Response (remote); request.Response.HttpVersion = "HTTP/1.1"; request.Response.HttpCode = HttpStatusCode.OK; request.Response.HTTPMessage = HttpStatusCode.OK.ToString (); request.Response.KeepAlive = false; request.Response.Add ("Proxy-Agent: HitProxy"); request.Response.SendHeaders (clientStream); Thread t = new Thread (() => { try { request.Stream.PipeTo (request.Response.Stream); } catch (Exception) { remote.Dispose (); } finally { } }); t.Name = Thread.CurrentThread.Name + "ConnectOutput"; t.Start (); try { request.Response.Stream.PipeTo (request.Stream); } catch (Exception e) { Console.WriteLine ("ConnectProxy " + e.GetType ().Name + " :" + e.Message); request.Stream.NullSafeDispose (); } finally { remote.Dispose (); clientStream.NullSafeDispose (); } t.Join (); request.Response.Dispose (); request.Response = null; }
/// <summary> /// Intercepts a HTTP CONNECT so we can filter the encrypted requests /// </summary> public static Stream InterceptConnect(Request request, Stream clientStream, CachedConnection remote) { //This code may work but it has not been tested yet. string certPath = Path.Combine (Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData), "HitProxy"), "server.pfx"); if (File.Exists (certPath) == false) throw new FileNotFoundException ("Need a server certificate", certPath); X509Certificate cert = X509Certificate2.CreateFromCertFile (certPath); request.Response = new Response (remote); request.Response.HttpVersion = "HTTP/1.1"; request.Response.HttpCode = HttpStatusCode.OK; request.Response.HTTPMessage = HttpStatusCode.OK.ToString (); request.Response.KeepAlive = false; request.Response.Add ("Proxy-Agent: HitProxy"); request.Response.SendHeaders (clientStream); //Client SslStream ssl = new SslStream (clientStream, false, RemoteCertificateValidation, LocalCertValidation); try { ssl.AuthenticateAsServer (cert, false, System.Security.Authentication.SslProtocols.Tls, false); } catch (Exception e) { Console.WriteLine (e.Message); throw; } //Remote server SslStream remoteSsl = new SslStream (remote.Stream, false, RemoteCertificateValidation); remoteSsl.AuthenticateAsClient (request.Uri.Host); remote.Stream = remoteSsl; return ssl; }
//See http://tools.ietf.org/html/rfc1928 for details /// <summary> /// Given a new connection to a socks proxy we initate the handshake. /// </summary> void PrepareSocks(CachedConnection remoteConnection) { Socket socket = remoteConnection.remoteSocket; //Send Version identifier byte[] version = new byte[3]; //Socks5 version [0] = 5; //Numberof methods following version [1] = 1; //No authentication version [2] = 0; socket.Send (version); //Read Selection message byte[] selection = new byte[2]; socket.Receive (selection); //Socks version if (selection [0] != 5) throw new HeaderException ("Socks5 not supported, got Socks" + selection [0], HttpStatusCode.MethodNotAllowed); if (selection [1] != 0) throw new HeaderException ("No authentication not supported: " + selection [0], HttpStatusCode.MethodNotAllowed); //Send request byte[] name = Encoding.ASCII.GetBytes (request.Uri.Host); byte[] sreq = new byte[7 + name.Length]; sreq [0] = 5; //Connect sreq [1] = 1; //(Reserved) sreq [2] = 0; //Address type: DomainName = 3 sreq [3] = 3; //Length of domain name sreq [4] = (byte)name.Length; //Domain Name name.CopyTo (sreq, 5); //Port sreq [5 + name.Length] = (byte)(request.Uri.Port >> 8); sreq [6 + name.Length] = (byte)(request.Uri.Port & 0xFF); socket.Send (sreq); //Read response byte[] resp = new byte[5]; socket.Receive (resp); if (resp [0] != 5) throw new HeaderException ("Socks5 not supported, got Socks" + selection [0], HttpStatusCode.MethodNotAllowed); byte[] resp2; switch (resp [3]) { case 01: //IPv4 resp2 = new byte[4 + 2]; resp2 [0] = resp [4]; socket.Receive (resp2, 1, 4 - 1 + 2, SocketFlags.None); break; case 03: //Domain name resp2 = new byte[resp [4] + 2]; socket.Receive (resp2); break; case 04: //IPv6 resp2 = new byte[16 + 2]; resp2 [0] = resp [4]; socket.Receive (resp2, 1, 16 - 1 + 2, SocketFlags.None); break; } switch (resp [1]) { case 0: break; case 1: throw new HeaderException ("Socks connect failed: general SOCKS server failure", HttpStatusCode.InternalServerError); case 2: throw new HeaderException ("Socks connect failed: Connection not allowed by ruleset", HttpStatusCode.InternalServerError); case 3: throw new HeaderException ("Socks connect failed: Network unrechable", HttpStatusCode.InternalServerError); case 4: throw new HeaderException ("Socks connect failed: Host unreachable", HttpStatusCode.InternalServerError); case 5: throw new HeaderException ("Socks connect failed: Connection refused", HttpStatusCode.InternalServerError); case 6: throw new HeaderException ("Socks connect failed: TTL expired", HttpStatusCode.InternalServerError); case 7: throw new HeaderException ("Socks connect failed: Command not supported", HttpStatusCode.InternalServerError); case 8: throw new HeaderException ("Socks connect failed: Address type not supported", HttpStatusCode.InternalServerError); default: throw new HeaderException ("Socks connect failed: " + resp [0], HttpStatusCode.InternalServerError); } return; }
/// <returns> /// True if Keep-alive /// </returns> bool ProcessRequest(CachedConnection remoteConnection) { try { //Begin connection communication //Prepare socks connection if (request.Proxy != null && request.Proxy.Scheme == "socks") PrepareSocks (remoteConnection); //initiate HTTP CONNECT request if (request.Method == "CONNECT") { Status = "Connecting Socket"; if (request.InterceptSSL) { //Intercept SSL this.ClientStream = ConnectProxy.InterceptConnect (request, ClientStream, remoteConnection); this.sslConnect = remoteConnection; return true; } else { //Pass connect stream unmodified Status = "Connected"; ConnectProxy.ProcessHttpConnect (request, ClientStream, remoteConnection); Status = "Connection closed"; return false; } } //All done, send the traffic to the remote server ProcessHttp (remoteConnection); Status = "Request done"; } catch (HeaderException e) { Console.Error.WriteLine (e.GetType () + ": " + e.Message); if (Status == "Sending response") return false; request.Response = new Response (e); if (SendResponse () == false) return false; } catch (SocketException e) { Console.Error.WriteLine (e.GetType () + ": " + e.Message + "\n" + e.StackTrace); if (Status == "Sending response") return false; request.Response = new Response (e); if (SendResponse () == false) return false; } catch (IOException e) { Console.Error.WriteLine (e.GetType () + ": " + e.Message + "\n" + e.StackTrace); if (Status == "Sending response") return false; request.Response = new Response (e); if (SendResponse () == false) return false; } catch (ObjectDisposedException e) { Console.Error.WriteLine (e.GetType () + ": " + e.Message + "\n" + e.StackTrace); if (Status == "Sending response") return false; request.Response = new Response (e); if (SendResponse () == false) return false; } //Close connection if (request.Response.Chunked == false && request.Response.HasBody == false) return false; if (request.Response.KeepAlive == false) return false; return request.KeepAlive; }
/// <summary> /// Remove a connection from the ServerCache /// This will trigger an event allowing pending connections to start. /// </summary> /// <param name="connection"> /// The connection to remove /// </param> public void Remove(CachedConnection connection) { lock (connections) { connections.Remove (connection); } manager.releasedConnection.Set (); }
public CachedConnection GetUnlimitedNewConnection() { CachedConnection c = new CachedConnection (this); lock (connections) { connections.Add (c); } c.Connect (); return c; }
/// <summary> /// Creates and return a new connection. /// If the maximum number of connections to that server is reached, /// it will return null. /// </summary> public CachedConnection GetNewConnection() { CachedConnection c = new CachedConnection (this); lock (connections) { if (connections.Count >= max) { return null; } connections.Add (c); } c.Connect (); return c; }