Commit d0dc2ff5 authored by Honfika's avatar Honfika

Add ALPN ssl extension to server hello

parent ce5aaa62
......@@ -7,7 +7,9 @@ namespace Titanium.Web.Proxy.Network.Tcp
{
internal class ClientHelloAlpnAdderStream : Stream
{
private CustomBufferedStream stream;
private readonly CustomBufferedStream stream;
private bool called;
public ClientHelloAlpnAdderStream(CustomBufferedStream stream)
{
......@@ -37,6 +39,13 @@ namespace Titanium.Web.Proxy.Network.Tcp
public override void Write(byte[] buffer, int offset, int count)
{
if (called)
{
stream.Write(buffer, offset, count);
return;
}
called = true;
var ms = new MemoryStream(buffer, offset, count);
//this can be non async, because reads from a memory stream
......@@ -46,9 +55,12 @@ namespace Titanium.Web.Proxy.Network.Tcp
// 0x00 0x10: ALPN identifier
// 0x00 0x0e: length of ALPN data
// 0x00 0x0c: length of ALPN data again:)
var dataToAdd = new byte[] { 0x0, 0x10, 0x0, 0xE, 0x0, 0xC,
var dataToAdd = new byte[]
{
0x0, 0x10, 0x0, 0xE, 0x0, 0xC,
2, (byte)'h', (byte)'2',
8, (byte)'h', (byte)'t', (byte)'t', (byte)'p', (byte)'/', (byte)'1', (byte)'.', (byte)'1' };
8, (byte)'h', (byte)'t', (byte)'t', (byte)'p', (byte)'/', (byte)'1', (byte)'.', (byte)'1'
};
int newByteCount = clientHello.Extensions == null ? dataToAdd.Length + 2 : dataToAdd.Length;
var buffer2 = new byte[buffer.Length + newByteCount];
......@@ -70,10 +82,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
buffer2[offset + 7] = (byte)(length >> 8);
buffer2[offset + 8] = (byte)length;
int pos = offset + clientHello.EntensionsStartPosition;
int endPos = offset + clientHello.ClientHelloLength;
if (clientHello.Extensions != null)
{
// update ALPN length
int pos = offset + clientHello.EntensionsStartPosition;
length = (buffer[pos] << 8) + buffer[pos + 1];
length += newByteCount;
buffer2[pos] = (byte)(length >> 8);
......@@ -82,19 +95,21 @@ namespace Titanium.Web.Proxy.Network.Tcp
else
{
// add ALPN length
buffer2[buffer.Length] = 0;
buffer2[buffer.Length + 1] = 18;
length = dataToAdd.Length;
buffer2[pos] = (byte)(length >> 8);
buffer2[pos + 1] = (byte)length;
endPos += 2;
}
for (int i = 0; i < dataToAdd.Length; i++)
{
buffer2[buffer2.Length - dataToAdd.Length + i] = dataToAdd[i];
buffer2[endPos + i] = dataToAdd[i];
}
// copy the reamining data if any
for (int i = clientHello.ClientHelloLength; i < count; i++)
{
buffer2[offset + newByteCount + i] = buffer[offset + clientHello.ClientHelloLength];
buffer2[offset + newByteCount + i] = buffer[offset + i];
}
buffer = buffer2;
......
using System.Diagnostics;
using System.IO;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Ssl;
namespace Titanium.Web.Proxy.Network.Tcp
{
internal class ServerHelloAlpnAdderStream : Stream
{
private readonly CustomBufferedStream stream;
private bool called;
public ServerHelloAlpnAdderStream(CustomBufferedStream stream)
{
this.stream = stream;
}
public override void Flush()
{
stream.Flush();
}
public override long Seek(long offset, SeekOrigin origin)
{
return stream.Seek(offset, origin);
}
public override void SetLength(long value)
{
stream.SetLength(value);
}
[DebuggerStepThrough]
public override int Read(byte[] buffer, int offset, int count)
{
return stream.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count)
{
if (called)
{
stream.Write(buffer, offset, count);
return;
}
called = true;
var ms = new MemoryStream(buffer, offset, count);
//this can be non async, because reads from a memory stream
var serverHello = SslTools.GetServerHelloInfo(new CustomBufferedStream(ms, (int)ms.Length)).Result;
if (serverHello != null)
{
// 0x00 0x10: ALPN identifier
// 0x00 0x0e: length of ALPN data
// 0x00 0x0c: length of ALPN data again:)
var dataToAdd = new byte[]
{
0x0, 0x10, 0x0, 0x5, 0x0, 0x3,
2, (byte)'h', (byte)'2'
};
int newByteCount = serverHello.Extensions == null ? dataToAdd.Length + 2 : dataToAdd.Length;
var buffer2 = new byte[buffer.Length + newByteCount];
for (int i = 0; i < buffer.Length; i++)
{
buffer2[i] = buffer[i];
}
//this is a hacky solution, but works
int length = (buffer[offset + 3] << 8) + buffer[offset + 4];
length += newByteCount;
buffer2[offset + 3] = (byte)(length >> 8);
buffer2[offset + 4] = (byte)length;
length = (buffer[offset + 6] << 16) + (buffer[offset + 7] << 8) + buffer[offset + 8];
length += newByteCount;
buffer2[offset + 6] = (byte)(length >> 16);
buffer2[offset + 7] = (byte)(length >> 8);
buffer2[offset + 8] = (byte)length;
int pos = offset + serverHello.EntensionsStartPosition;
int endPos = offset + serverHello.ServerHelloLength;
if (serverHello.Extensions != null)
{
// update ALPN length
length = (buffer[pos] << 8) + buffer[pos + 1];
length += newByteCount;
buffer2[pos] = (byte)(length >> 8);
buffer2[pos + 1] = (byte)length;
}
else
{
// add ALPN length
length = dataToAdd.Length;
buffer2[pos] = (byte)(length >> 8);
buffer2[pos + 1] = (byte)length;
endPos += 2;
}
for (int i = 0; i < dataToAdd.Length; i++)
{
buffer2[endPos + i] = dataToAdd[i];
}
// copy the reamining data if any
for (int i = serverHello.ServerHelloLength; i < count; i++)
{
buffer2[offset + newByteCount + i] = buffer[offset + i];
}
buffer = buffer2;
count += newByteCount;
}
stream.Write(buffer, offset, count);
}
public override bool CanRead => stream.CanRead;
public override bool CanSeek => stream.CanSeek;
public override bool CanWrite => stream.CanWrite;
public override long Length => stream.Length;
public override long Position
{
get { return stream.Position; }
set { stream.Position = value; }
}
}
}
\ No newline at end of file
......@@ -105,8 +105,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
if (isHttps)
{
bool alpnEnabled = false;
var alpnStream = alpnEnabled ? (Stream)new ClientHelloAlpnAdderStream(stream) : stream;
var alpnStream = ProxyServer.AlpnEnabled ? (Stream)new ClientHelloAlpnAdderStream(stream) : stream;
var sslStream = new SslStream(alpnStream, false, server.ValidateServerCertificate, server.SelectClientCertificate);
stream = new CustomBufferedStream(sslStream, server.BufferSize);
......
......@@ -35,6 +35,11 @@ namespace Titanium.Web.Proxy
internal const string UriSchemeHttps = "https";
#endif
/// <summary>
/// Enable the experimental ALPN adder streams
/// </summary>
internal static bool AlpnEnabled = false;
/// <summary>
/// Is the proxy currently running
/// </summary>
......
......@@ -131,7 +131,8 @@ namespace Titanium.Web.Proxy
try
{
sslStream = new SslStream(clientStream);
var alpnStream = AlpnEnabled ? (Stream)new ServerHelloAlpnAdderStream(clientStream) : clientStream;
sslStream = new SslStream(alpnStream);
string certName = HttpHelper.GetWildCardDomainName(httpRemoteUri.Host);
......
......@@ -39,6 +39,10 @@ namespace Titanium.Web.Proxy.Ssl
public byte CompressionMethod { get; set; }
internal int ServerHelloLength { get; set; }
internal int EntensionsStartPosition { get; set; }
public List<SslExtension> Extensions { get; set; }
private static string SslVersionToString(int major, int minor)
......
......@@ -238,8 +238,12 @@ namespace Titanium.Web.Proxy.Ssl
int cipherSuite = peekStream.ReadInt16();
byte compressionMethod = peekStream.ReadByte();
int extenstionsStartPosition = peekStream.Position;
var extensions = await ReadExtensions(majorVersion, minorVersion, peekStream);
//var rawBytes = new CustomBufferedPeekStream(serverStream).ReadBytes(peekStream.Position);
var serverHelloInfo = new ServerHelloInfo
{
MajorVersion = majorVersion,
......@@ -248,6 +252,8 @@ namespace Titanium.Web.Proxy.Ssl
SessionId = sessionId,
CipherSuite = cipherSuite,
CompressionMethod = compressionMethod,
ServerHelloLength = peekStream.Position,
EntensionsStartPosition = extenstionsStartPosition,
Extensions = extensions,
};
......
......@@ -98,6 +98,7 @@
<Compile Include="Helpers\HttpHelper.cs" />
<Compile Include="Helpers\Network.cs" />
<Compile Include="Helpers\Tcp.cs" />
<Compile Include="Network\Tcp\ServerHelloAlpnAdderStream.cs" />
<Compile Include="Network\Tcp\ClientHelloAlpnAdderStream.cs" />
<Compile Include="Ssl\ServerHelloInfo.cs" />
<Compile Include="Ssl\ClientHelloInfo.cs" />
......
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