Commit 6178e0c9 authored by Jehonathan Thomas's avatar Jehonathan Thomas Committed by GitHub

Merge pull request #297 from honfika/develop

Parse server hello + minor impovements
parents aafb3c87 aeaf1f67
...@@ -112,18 +112,16 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -112,18 +112,16 @@ namespace Titanium.Web.Proxy.Helpers
/// Usefull for websocket requests /// Usefull for websocket requests
/// </summary> /// </summary>
/// <param name="clientStream"></param> /// <param name="clientStream"></param>
/// <param name="connection"></param> /// <param name="serverStream"></param>
/// <param name="onDataSend"></param> /// <param name="onDataSend"></param>
/// <param name="onDataReceive"></param> /// <param name="onDataReceive"></param>
/// <returns></returns> /// <returns></returns>
internal static async Task SendRaw(Stream clientStream, TcpConnection connection, Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive) internal static async Task SendRaw(Stream clientStream, Stream serverStream, Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive)
{ {
var tunnelStream = connection.Stream;
//Now async relay all server=>client & client=>server data //Now async relay all server=>client & client=>server data
var sendRelay = clientStream.CopyToAsync(tunnelStream, onDataSend); var sendRelay = clientStream.CopyToAsync(serverStream, onDataSend);
var receiveRelay = tunnelStream.CopyToAsync(clientStream, onDataReceive); var receiveRelay = serverStream.CopyToAsync(clientStream, onDataReceive);
await Task.WhenAll(sendRelay, receiveRelay); await Task.WhenAll(sendRelay, receiveRelay);
} }
......
...@@ -41,7 +41,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -41,7 +41,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary> /// <summary>
/// Server stream /// Server stream
/// </summary> /// </summary>
internal Stream Stream { get; set; } internal CustomBufferedStream Stream { get; set; }
/// <summary> /// <summary>
/// Last time this connection was used /// Last time this connection was used
......
...@@ -22,12 +22,13 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -22,12 +22,13 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <param name="remoteHostName"></param> /// <param name="remoteHostName"></param>
/// <param name="remotePort"></param> /// <param name="remotePort"></param>
/// <param name="httpVersion"></param> /// <param name="httpVersion"></param>
/// <param name="isConnect"></param>
/// <param name="isHttps"></param> /// <param name="isHttps"></param>
/// <param name="externalHttpProxy"></param> /// <param name="externalHttpProxy"></param>
/// <param name="externalHttpsProxy"></param> /// <param name="externalHttpsProxy"></param>
/// <returns></returns> /// <returns></returns>
internal async Task<TcpConnection> CreateClient(ProxyServer server, string remoteHostName, int remotePort, Version httpVersion, bool isHttps, internal async Task<TcpConnection> CreateClient(ProxyServer server, string remoteHostName, int remotePort, Version httpVersion, bool isHttps,
ExternalProxy externalHttpProxy, ExternalProxy externalHttpsProxy) bool isConnect, ExternalProxy externalHttpProxy, ExternalProxy externalHttpsProxy)
{ {
bool useUpstreamProxy = false; bool useUpstreamProxy = false;
var externalProxy = isHttps ? externalHttpsProxy : externalHttpProxy; var externalProxy = isHttps ? externalHttpsProxy : externalHttpProxy;
...@@ -60,9 +61,15 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -60,9 +61,15 @@ namespace Titanium.Web.Proxy.Network.Tcp
if (useUpstreamProxy) if (useUpstreamProxy)
{ {
await client.ConnectAsync(externalProxy.HostName, externalProxy.Port); await client.ConnectAsync(externalProxy.HostName, externalProxy.Port);
}
else
{
await client.ConnectAsync(remoteHostName, remotePort);
}
stream = new CustomBufferedStream(client.GetStream(), server.BufferSize); stream = new CustomBufferedStream(client.GetStream(), server.BufferSize);
if (isHttps) if (useUpstreamProxy && (isConnect || isHttps))
{ {
using (var writer = new HttpRequestWriter(stream)) using (var writer = new HttpRequestWriter(stream))
{ {
...@@ -77,6 +84,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -77,6 +84,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
Convert.ToBase64String(Encoding.UTF8.GetBytes( Convert.ToBase64String(Encoding.UTF8.GetBytes(
externalProxy.UserName + ":" + externalProxy.Password))); externalProxy.UserName + ":" + externalProxy.Password)));
} }
await writer.WriteLineAsync(); await writer.WriteLineAsync();
await writer.FlushAsync(); await writer.FlushAsync();
} }
...@@ -93,12 +101,6 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -93,12 +101,6 @@ namespace Titanium.Web.Proxy.Network.Tcp
await reader.ReadAndIgnoreAllLinesAsync(); await reader.ReadAndIgnoreAllLinesAsync();
} }
} }
}
else
{
await client.ConnectAsync(remoteHostName, remotePort);
stream = new CustomBufferedStream(client.GetStream(), server.BufferSize);
}
if (isHttps) if (isHttps)
{ {
......
...@@ -125,6 +125,7 @@ namespace Titanium.Web.Proxy ...@@ -125,6 +125,7 @@ namespace Titanium.Web.Proxy
if (!excluded && isClientHello) if (!excluded && isClientHello)
{ {
httpRemoteUri = new Uri("https://" + httpUrl); httpRemoteUri = new Uri("https://" + httpUrl);
connectRequest.RequestUri = httpRemoteUri;
SslStream sslStream = null; SslStream sslStream = null;
...@@ -159,9 +160,24 @@ namespace Titanium.Web.Proxy ...@@ -159,9 +160,24 @@ namespace Titanium.Web.Proxy
else else
{ {
//create new connection //create new connection
using (var connection = await GetServerConnection(connectArgs)) using (var connection = await GetServerConnection(connectArgs, true))
{ {
await TcpHelper.SendRaw(clientStream, connection, if (isClientHello)
{
if (clientStream.Available > 0)
{
//send the buffered data
var data = new byte[clientStream.Available];
await clientStream.ReadAsync(data, 0, data.Length);
await connection.Stream.WriteAsync(data, 0, data.Length);
await connection.Stream.FlushAsync();
}
var serverHelloInfo = await SslTools.GetServerHelloInfo(connection.Stream);
((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo;
}
await TcpHelper.SendRaw(clientStream, connection.Stream,
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); }); (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); });
UpdateServerConnectionCount(false); UpdateServerConnectionCount(false);
} }
...@@ -336,16 +352,17 @@ namespace Titanium.Web.Proxy ...@@ -336,16 +352,17 @@ namespace Titanium.Web.Proxy
break; break;
} }
if (connection == null)
{
connection = await GetServerConnection(args);
}
//create a new connection if hostname changes //create a new connection if hostname changes
else if (!connection.HostName.Equals(args.WebSession.Request.RequestUri.Host, StringComparison.OrdinalIgnoreCase)) if (connection != null && !connection.HostName.Equals(args.WebSession.Request.RequestUri.Host, StringComparison.OrdinalIgnoreCase))
{ {
connection.Dispose(); connection.Dispose();
UpdateServerConnectionCount(false); UpdateServerConnectionCount(false);
connection = await GetServerConnection(args); connection = null;
}
if (connection == null)
{
connection = await GetServerConnection(args, false);
} }
//if upgrading to websocket then relay the requet without reading the contents //if upgrading to websocket then relay the requet without reading the contents
...@@ -383,7 +400,7 @@ namespace Titanium.Web.Proxy ...@@ -383,7 +400,7 @@ namespace Titanium.Web.Proxy
await BeforeResponse.InvokeParallelAsync(this, args, ExceptionFunc); await BeforeResponse.InvokeParallelAsync(this, args, ExceptionFunc);
} }
await TcpHelper.SendRaw(clientStream, connection, await TcpHelper.SendRaw(clientStream, connection.Stream,
(buffer, offset, count) => { args.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); }); (buffer, offset, count) => { args.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); });
args.Dispose(); args.Dispose();
...@@ -550,8 +567,9 @@ namespace Titanium.Web.Proxy ...@@ -550,8 +567,9 @@ namespace Titanium.Web.Proxy
/// Create a Server Connection /// Create a Server Connection
/// </summary> /// </summary>
/// <param name="args"></param> /// <param name="args"></param>
/// <param name="isConnect"></param>
/// <returns></returns> /// <returns></returns>
private async Task<TcpConnection> GetServerConnection(SessionEventArgs args) private async Task<TcpConnection> GetServerConnection(SessionEventArgs args, bool isConnect)
{ {
ExternalProxy customUpStreamHttpProxy = null; ExternalProxy customUpStreamHttpProxy = null;
ExternalProxy customUpStreamHttpsProxy = null; ExternalProxy customUpStreamHttpsProxy = null;
...@@ -578,7 +596,7 @@ namespace Titanium.Web.Proxy ...@@ -578,7 +596,7 @@ namespace Titanium.Web.Proxy
args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Host,
args.WebSession.Request.RequestUri.Port, args.WebSession.Request.RequestUri.Port,
args.WebSession.Request.HttpVersion, args.WebSession.Request.HttpVersion,
args.IsHttps, args.IsHttps, isConnect,
customUpStreamHttpProxy ?? UpStreamHttpProxy, customUpStreamHttpProxy ?? UpStreamHttpProxy,
customUpStreamHttpsProxy ?? UpStreamHttpsProxy); customUpStreamHttpsProxy ?? UpStreamHttpsProxy);
} }
......
...@@ -85,24 +85,25 @@ namespace Titanium.Web.Proxy.Ssl ...@@ -85,24 +85,25 @@ namespace Titanium.Web.Proxy.Ssl
if (CompressionData.Length > 0) if (CompressionData.Length > 0)
{ {
int id = CompressionData[0]; int compressionMethod = CompressionData[0];
string compression = null; string compression = compressions.Length > compressionMethod
compression = compressions.Length > id ? compressions[id] : $"unknown [0x{id:X2}]"; ? compressions[compressionMethod]
: $"unknown [0x{compressionMethod:X2}]";
sb.AppendLine($"Compression: {compression}"); sb.AppendLine($"Compression: {compression}");
} }
if (Ciphers.Length > 0) if (Ciphers.Length > 0)
{ {
sb.AppendLine($"Ciphers:"); sb.AppendLine("Ciphers:");
foreach (int cipher in Ciphers) foreach (int cipherSuite in Ciphers)
{ {
string cipherStr; string cipherStr;
if (!SslCiphers.Ciphers.TryGetValue(cipher, out cipherStr)) if (!SslCiphers.Ciphers.TryGetValue(cipherSuite, out cipherStr))
{ {
cipherStr = $"unknown"; cipherStr = "unknown";
} }
sb.AppendLine($"[0x{cipher:X4}] {cipherStr}"); sb.AppendLine($"[0x{cipherSuite:X4}] {cipherStr}");
} }
} }
......
...@@ -35,9 +35,9 @@ namespace Titanium.Web.Proxy.Ssl ...@@ -35,9 +35,9 @@ namespace Titanium.Web.Proxy.Ssl
public byte[] SessionId { get; set; } public byte[] SessionId { get; set; }
public int[] Ciphers { get; set; } public int CipherSuite { get; set; }
public byte[] CompressionData { get; set; } public byte CompressionMethod { get; set; }
public List<SslExtension> Extensions { get; set; } public List<SslExtension> Extensions { get; set; }
...@@ -83,28 +83,19 @@ namespace Titanium.Web.Proxy.Ssl ...@@ -83,28 +83,19 @@ namespace Titanium.Web.Proxy.Ssl
} }
} }
if (CompressionData.Length > 0) string compression = compressions.Length > CompressionMethod
{ ? compressions[CompressionMethod]
int id = CompressionData[0]; : $"unknown [0x{CompressionMethod:X2}]";
string compression = null;
compression = compressions.Length > id ? compressions[id] : $"unknown [0x{id:X2}]";
sb.AppendLine($"Compression: {compression}"); sb.AppendLine($"Compression: {compression}");
}
if (Ciphers.Length > 0) sb.Append("Cipher:");
{
sb.AppendLine($"Ciphers:");
foreach (int cipher in Ciphers)
{
string cipherStr; string cipherStr;
if (!SslCiphers.Ciphers.TryGetValue(cipher, out cipherStr)) if (!SslCiphers.Ciphers.TryGetValue(CipherSuite, out cipherStr))
{ {
cipherStr = $"unknown"; cipherStr = "unknown";
} }
sb.AppendLine($"[0x{cipher:X4}] {cipherStr}"); sb.AppendLine($"[0x{CipherSuite:X4}] {cipherStr}");
}
}
return sb.ToString(); return sb.ToString();
} }
......
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
...@@ -108,42 +109,50 @@ namespace Titanium.Web.Proxy.Ssl ...@@ -108,42 +109,50 @@ namespace Titanium.Web.Proxy.Ssl
byte[] compressionData = peekStream.ReadBytes(length); byte[] compressionData = peekStream.ReadBytes(length);
var extensions = await ReadExtensions(majorVersion, minorVersion, peekStream);
var clientHelloInfo = new ClientHelloInfo
{
MajorVersion = majorVersion,
MinorVersion = minorVersion,
Random = random,
SessionId = sessionId,
Ciphers = ciphers,
CompressionData = compressionData,
Extensions = extensions,
};
return clientHelloInfo;
}
return null;
}
private static async Task<List<SslExtension>> ReadExtensions(int majorVersion, int minorVersion, CustomBufferedPeekStream peekStream)
{
List<SslExtension> extensions = null; List<SslExtension> extensions = null;
if (majorVersion > 3 || majorVersion == 3 && minorVersion >= 1) if (majorVersion > 3 || majorVersion == 3 && minorVersion >= 1)
{ {
if (await peekStream.EnsureBufferLength(2)) if (await peekStream.EnsureBufferLength(2))
{ {
length = peekStream.ReadInt16(); int extensionsLength = peekStream.ReadInt16();
if (await peekStream.EnsureBufferLength(length)) if (await peekStream.EnsureBufferLength(extensionsLength))
{ {
extensions = new List<SslExtension>(); extensions = new List<SslExtension>();
while (peekStream.Available > 3) while (extensionsLength > 3)
{ {
int id = peekStream.ReadInt16(); int id = peekStream.ReadInt16();
length = peekStream.ReadInt16(); int length = peekStream.ReadInt16();
byte[] data = peekStream.ReadBytes(length); byte[] data = peekStream.ReadBytes(length);
extensions.Add(SslExtensions.GetExtension(id, data)); extensions.Add(SslExtensions.GetExtension(id, data));
extensionsLength -= 4 + length;
} }
} }
} }
} }
var clientHelloInfo = new ClientHelloInfo return extensions;
{
MajorVersion = majorVersion,
MinorVersion = minorVersion,
Random = random,
SessionId = sessionId,
Ciphers = ciphers,
CompressionData = compressionData,
Extensions = extensions,
};
return clientHelloInfo;
}
return null;
} }
public static async Task<bool> IsServerHello(CustomBufferedStream serverStream) public static async Task<bool> IsServerHello(CustomBufferedStream serverStream)
...@@ -154,8 +163,6 @@ namespace Titanium.Web.Proxy.Ssl ...@@ -154,8 +163,6 @@ namespace Titanium.Web.Proxy.Ssl
public static async Task<ServerHelloInfo> GetServerHelloInfo(CustomBufferedStream serverStream) public static async Task<ServerHelloInfo> GetServerHelloInfo(CustomBufferedStream serverStream)
{ {
//todo: THIS IS NOT READY YET
//detects the HTTPS ClientHello message as it is described in the following url: //detects the HTTPS ClientHello message as it is described in the following url:
//https://stackoverflow.com/questions/3897883/how-to-detect-an-incoming-ssl-https-handshake-ssl-wire-format //https://stackoverflow.com/questions/3897883/how-to-detect-an-incoming-ssl-https-handshake-ssl-wire-format
...@@ -200,9 +207,9 @@ namespace Titanium.Web.Proxy.Ssl ...@@ -200,9 +207,9 @@ namespace Titanium.Web.Proxy.Ssl
int length = peekStream.ReadInt16(); int length = peekStream.ReadInt16();
if (peekStream.ReadByte() != 0x01) if (peekStream.ReadByte() != 0x02)
{ {
// should be ClientHello // should be ServerHello
return null; return null;
} }
...@@ -214,63 +221,18 @@ namespace Titanium.Web.Proxy.Ssl ...@@ -214,63 +221,18 @@ namespace Titanium.Web.Proxy.Ssl
byte[] random = peekStream.ReadBytes(32); byte[] random = peekStream.ReadBytes(32);
length = peekStream.ReadByte(); length = peekStream.ReadByte();
// sessionid + 2 ciphersData length // sessionid + cipherSuite + compressionMethod
if (!await peekStream.EnsureBufferLength(length + 2)) if (!await peekStream.EnsureBufferLength(length + 2 + 1))
{ {
return null; return null;
} }
byte[] sessionId = peekStream.ReadBytes(length); byte[] sessionId = peekStream.ReadBytes(length);
length = peekStream.ReadInt16(); int cipherSuite = peekStream.ReadInt16();
byte compressionMethod = peekStream.ReadByte();
// ciphersData + compressionData length
if (!await peekStream.EnsureBufferLength(length + 1))
{
return null;
}
byte[] ciphersData = peekStream.ReadBytes(length);
int[] ciphers = new int[ciphersData.Length / 2];
for (int i = 0; i < ciphers.Length; i++)
{
ciphers[i] = (ciphersData[2 * i] << 8) + ciphersData[2 * i + 1];
}
length = peekStream.ReadByte();
if (length < 1)
{
return null;
}
// compressionData
if (!await peekStream.EnsureBufferLength(length))
{
return null;
}
byte[] compressionData = peekStream.ReadBytes(length);
List<SslExtension> extensions = null; var extensions = await ReadExtensions(majorVersion, minorVersion, peekStream);
if (majorVersion > 3 || majorVersion == 3 && minorVersion >= 1)
{
if (await peekStream.EnsureBufferLength(2))
{
length = peekStream.ReadInt16();
if (await peekStream.EnsureBufferLength(length))
{
extensions = new List<SslExtension>();
while (peekStream.Available > 3)
{
int id = peekStream.ReadInt16();
length = peekStream.ReadInt16();
byte[] data = peekStream.ReadBytes(length);
extensions.Add(SslExtensions.GetExtension(id, data));
}
}
}
}
var serverHelloInfo = new ServerHelloInfo var serverHelloInfo = new ServerHelloInfo
{ {
...@@ -278,8 +240,8 @@ namespace Titanium.Web.Proxy.Ssl ...@@ -278,8 +240,8 @@ namespace Titanium.Web.Proxy.Ssl
MinorVersion = minorVersion, MinorVersion = minorVersion,
Random = random, Random = random,
SessionId = sessionId, SessionId = sessionId,
Ciphers = ciphers, CipherSuite = cipherSuite,
CompressionData = compressionData, CompressionMethod = compressionMethod,
Extensions = extensions, Extensions = extensions,
}; };
......
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