Commit d86d63f2 authored by Honfika's avatar Honfika

prepare for HTTP/2

parent d69c52bf
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFrameworks>net45;netcoreapp2.0</TargetFrameworks> <TargetFrameworks>net45;netcoreapp2.0;netcoreapp2.1</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<LangVersion>7.1</LangVersion> <LangVersion>7.1</LangVersion>
</PropertyGroup> </PropertyGroup>
......
...@@ -3,6 +3,7 @@ using System.IO; ...@@ -3,6 +3,7 @@ using System.IO;
using System.Net; using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended; using StreamExtended;
...@@ -10,6 +11,7 @@ using StreamExtended.Helpers; ...@@ -10,6 +11,7 @@ using StreamExtended.Helpers;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
...@@ -100,6 +102,7 @@ namespace Titanium.Web.Proxy ...@@ -100,6 +102,7 @@ namespace Titanium.Web.Proxy
//write back successfull CONNECT response //write back successfull CONNECT response
var response = ConnectResponse.CreateSuccessfullConnectResponse(version); var response = ConnectResponse.CreateSuccessfullConnectResponse(version);
// Set ContentLength explicitly to properly handle HTTP 1.0 // Set ContentLength explicitly to properly handle HTTP 1.0
response.ContentLength = 0; response.ContentLength = 0;
response.Headers.FixProxyHeaders(); response.Headers.FixProxyHeaders();
...@@ -133,7 +136,21 @@ namespace Titanium.Web.Proxy ...@@ -133,7 +136,21 @@ namespace Titanium.Web.Proxy
await CertificateManager.CreateCertificateAsync(certName); await CertificateManager.CreateCertificateAsync(certName);
//Successfully managed to authenticate the client using the fake certificate //Successfully managed to authenticate the client using the fake certificate
await sslStream.AuthenticateAsServerAsync(certificate, false, SupportedSslProtocols, false); var options = new SslServerAuthenticationOptions();
options.ApplicationProtocols = clientHelloInfo.GetAlpn();
if (options.ApplicationProtocols == null || options.ApplicationProtocols.Count == 0)
{
options.ApplicationProtocols = SslExtensions.Http11ProtocolAsList;
}
// client connection is always HTTP 1.x, todo
options.ApplicationProtocols = SslExtensions.Http11ProtocolAsList;
options.ServerCertificate = certificate;
options.ClientCertificateRequired = false;
options.EnabledSslProtocols = SupportedSslProtocols;
options.CertificateRevocationCheckMode = X509RevocationMode.NoCheck;
await sslStream.AuthenticateAsServerAsync(options, new CancellationToken(false));
//HTTPS server created - we can now decrypt the client's traffic //HTTPS server created - we can now decrypt the client's traffic
clientStream = new CustomBufferedStream(sslStream, BufferSize); clientStream = new CustomBufferedStream(sslStream, BufferSize);
......
using StreamExtended; using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using StreamExtended;
namespace Titanium.Web.Proxy.Extensions namespace Titanium.Web.Proxy.Extensions
{ {
internal static class SslExtensions internal static class SslExtensions
{ {
public static readonly List<SslApplicationProtocol> Http11ProtocolAsList = new List<SslApplicationProtocol> { SslApplicationProtocol.Http11 };
public static string GetServerName(this ClientHelloInfo clientHelloInfo) public static string GetServerName(this ClientHelloInfo clientHelloInfo)
{ {
if (clientHelloInfo.Extensions != null && if (clientHelloInfo.Extensions != null &&
...@@ -14,5 +25,97 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -14,5 +25,97 @@ namespace Titanium.Web.Proxy.Extensions
return null; return null;
} }
#if NETCOREAPP2_1
public static List<SslApplicationProtocol> GetAlpn(this ClientHelloInfo clientHelloInfo)
{
if (clientHelloInfo.Extensions != null && clientHelloInfo.Extensions.TryGetValue("ALPN", out var alpnExtension))
{
var alpn = alpnExtension.Data.Split(',');
if (alpn.Length != 0)
{
var result = new List<SslApplicationProtocol>(alpn.Length);
foreach (string p in alpn)
{
string protocol = p.Trim();
if (protocol.Equals("http/1.1"))
{
result.Add(SslApplicationProtocol.Http11);
}
else if (protocol.Equals("h2"))
{
result.Add(SslApplicationProtocol.Http2);
}
}
return result;
}
}
return null;
}
#else
public static List<SslApplicationProtocol> GetAlpn(this ClientHelloInfo clientHelloInfo)
{
return Http11ProtocolAsList;
}
public static Task AuthenticateAsClientAsync(this SslStream sslStream, SslClientAuthenticationOptions option, CancellationToken token)
{
return sslStream.AuthenticateAsClientAsync(option.TargetHost, option.ClientCertificates, option.EnabledSslProtocols, option.CertificateRevocationCheckMode != X509RevocationMode.NoCheck);
}
public static Task AuthenticateAsServerAsync(this SslStream sslStream, SslServerAuthenticationOptions options, CancellationToken token)
{
return sslStream.AuthenticateAsServerAsync(options.ServerCertificate, options.ClientCertificateRequired, options.EnabledSslProtocols, options.CertificateRevocationCheckMode != X509RevocationMode.NoCheck);
}
#endif
}
#if !NETCOREAPP2_1
public enum SslApplicationProtocol
{
Http11, Http2
}
public class SslClientAuthenticationOptions
{
public bool AllowRenegotiation { get; set; }
public string TargetHost { get; set; }
public X509CertificateCollection ClientCertificates { get; set; }
public LocalCertificateSelectionCallback LocalCertificateSelectionCallback { get; set; }
public SslProtocols EnabledSslProtocols { get; set; }
public X509RevocationMode CertificateRevocationCheckMode { get; set; }
public List<SslApplicationProtocol> ApplicationProtocols { get; set; }
public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }
public EncryptionPolicy EncryptionPolicy { get; set; }
}
public class SslServerAuthenticationOptions
{
public bool AllowRenegotiation { get; set; }
public X509Certificate ServerCertificate { get; set; }
public bool ClientCertificateRequired { get; set; }
public SslProtocols EnabledSslProtocols { get; set; }
public X509RevocationMode CertificateRevocationCheckMode { get; set; }
public List<SslApplicationProtocol> ApplicationProtocols { get; set; }
public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }
public EncryptionPolicy EncryptionPolicy { get; set; }
} }
#endif
} }
...@@ -66,6 +66,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -66,6 +66,7 @@ namespace Titanium.Web.Proxy.Extensions
} }
catch catch
{ {
// ignored
} }
} }
} }
......
...@@ -17,7 +17,9 @@ namespace Titanium.Web.Proxy.Models ...@@ -17,7 +17,9 @@ namespace Titanium.Web.Proxy.Models
internal static readonly Version Version11 = new Version(1, 1); internal static readonly Version Version11 = new Version(1, 1);
internal static HttpHeader ProxyConnectionKeepAlive = new HttpHeader("Proxy-Connection", "keep-alive"); internal static readonly Version Version20 = new Version(2, 0);
internal static readonly HttpHeader ProxyConnectionKeepAlive = new HttpHeader("Proxy-Connection", "keep-alive");
/// <summary> /// <summary>
/// Constructor. /// Constructor.
......
using System; using System;
using System.Collections.Generic;
using System.Net; using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
...@@ -22,6 +24,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -22,6 +24,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary> /// </summary>
/// <param name="remoteHostName"></param> /// <param name="remoteHostName"></param>
/// <param name="remotePort"></param> /// <param name="remotePort"></param>
/// <param name="applicationProtocols"></param>
/// <param name="httpVersion"></param> /// <param name="httpVersion"></param>
/// <param name="isHttps"></param> /// <param name="isHttps"></param>
/// <param name="isConnect"></param> /// <param name="isConnect"></param>
...@@ -29,8 +32,8 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -29,8 +32,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <param name="upStreamEndPoint"></param> /// <param name="upStreamEndPoint"></param>
/// <param name="externalProxy"></param> /// <param name="externalProxy"></param>
/// <returns></returns> /// <returns></returns>
internal async Task<TcpConnection> CreateClient(string remoteHostName, internal async Task<TcpConnection> CreateClient(string remoteHostName, int remotePort,
int remotePort, Version httpVersion, bool isHttps, bool isConnect, List<SslApplicationProtocol> applicationProtocols, Version httpVersion, bool isHttps, bool isConnect,
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy) ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy)
{ {
bool useUpstreamProxy = false; bool useUpstreamProxy = false;
...@@ -106,8 +109,21 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -106,8 +109,21 @@ namespace Titanium.Web.Proxy.Network.Tcp
proxyServer.SelectClientCertificate); proxyServer.SelectClientCertificate);
stream = new CustomBufferedStream(sslStream, proxyServer.BufferSize); stream = new CustomBufferedStream(sslStream, proxyServer.BufferSize);
await sslStream.AuthenticateAsClientAsync(remoteHostName, null, proxyServer.SupportedSslProtocols, var options = new SslClientAuthenticationOptions();
proxyServer.CheckCertificateRevocation); options.ApplicationProtocols = applicationProtocols;
if (options.ApplicationProtocols == null || options.ApplicationProtocols.Count == 0)
{
options.ApplicationProtocols = SslExtensions.Http11ProtocolAsList;
}
// server connection is always HTTP 1.x, todo
options.ApplicationProtocols = SslExtensions.Http11ProtocolAsList;
options.TargetHost = remoteHostName;
options.ClientCertificates = null;
options.EnabledSslProtocols = proxyServer.SupportedSslProtocols;
options.CertificateRevocationCheckMode = proxyServer.CheckCertificateRevocation;
await sslStream.AuthenticateAsClientAsync(options, new CancellationToken(false));
} }
client.ReceiveTimeout = proxyServer.ConnectionTimeOutSeconds * 1000; client.ReceiveTimeout = proxyServer.ConnectionTimeOutSeconds * 1000;
......
...@@ -4,6 +4,7 @@ using System.Linq; ...@@ -4,6 +4,7 @@ using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Authentication; using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
...@@ -125,9 +126,9 @@ namespace Titanium.Web.Proxy ...@@ -125,9 +126,9 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Should we check for certificare revocation during SSL authentication to servers /// Should we check for certificare revocation during SSL authentication to servers
/// Note: If enabled can reduce performance (Default disabled) /// Note: If enabled can reduce performance (Default: NoCheck)
/// </summary> /// </summary>
public bool CheckCertificateRevocation { get; set; } public X509RevocationMode CheckCertificateRevocation { get; set; }
/// <summary> /// <summary>
/// Does this proxy uses the HTTP protocol 100 continue behaviour strictly? /// Does this proxy uses the HTTP protocol 100 continue behaviour strictly?
......
...@@ -57,6 +57,21 @@ namespace Titanium.Web.Proxy ...@@ -57,6 +57,21 @@ namespace Titanium.Web.Proxy
{ {
// read the request line // read the request line
string httpCmd = await clientStreamReader.ReadLineAsync(); string httpCmd = await clientStreamReader.ReadLineAsync();
if (httpCmd == "PRI * HTTP/2.0")
{
// HTTP/2 Connection Preface
string line = await clientStreamReader.ReadLineAsync();
if (line != string.Empty) throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
line = await clientStreamReader.ReadLineAsync();
if (line != "SM") throw new Exception($"HTTP/2 Protocol violation. 'SM' expected, '{line}' received");
line = await clientStreamReader.ReadLineAsync();
if (line != string.Empty) throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
// todo
}
if (string.IsNullOrEmpty(httpCmd)) if (string.IsNullOrEmpty(httpCmd))
{ {
return; return;
...@@ -362,12 +377,12 @@ namespace Titanium.Web.Proxy ...@@ -362,12 +377,12 @@ namespace Titanium.Web.Proxy
args.CustomUpStreamProxyUsed = customUpStreamProxy; args.CustomUpStreamProxyUsed = customUpStreamProxy;
return await tcpConnectionFactory.CreateClient(args.WebSession.Request.RequestUri.Host, return await tcpConnectionFactory.CreateClient(
args.WebSession.Request.RequestUri.Host,
args.WebSession.Request.RequestUri.Port, args.WebSession.Request.RequestUri.Port,
args.WebSession.Request.HttpVersion, args.WebSession.ConnectRequest?.ClientHelloInfo?.GetAlpn(),
isHttps, args.WebSession.Request.HttpVersion, isHttps, isConnect,
isConnect, this, this, args.WebSession.UpStreamEndPoint ?? UpStreamEndPoint,
args.WebSession.UpStreamEndPoint ?? UpStreamEndPoint,
customUpStreamProxy ?? (isHttps ? UpStreamHttpsProxy : UpStreamHttpProxy)); customUpStreamProxy ?? (isHttps ? UpStreamHttpsProxy : UpStreamHttpProxy));
} }
......
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net45;netstandard2.0</TargetFrameworks> <TargetFrameworks>net45;netstandard2.0;netcoreapp2.1</TargetFrameworks>
<RootNamespace>Titanium.Web.Proxy</RootNamespace> <RootNamespace>Titanium.Web.Proxy</RootNamespace>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<SignAssembly>True</SignAssembly> <SignAssembly>True</SignAssembly>
...@@ -25,4 +25,13 @@ ...@@ -25,4 +25,13 @@
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
<PackageReference Include="Microsoft.Win32.Registry">
<Version>4.4.0</Version>
</PackageReference>
<PackageReference Include="System.Security.Principal.Windows">
<Version>4.4.1</Version>
</PackageReference>
</ItemGroup>
</Project> </Project>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment