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
/// Usefull for websocket requests
/// </summary>
/// <param name="clientStream"></param>
/// <param name="connection"></param>
/// <param name="serverStream"></param>
/// <param name="onDataSend"></param>
/// <param name="onDataReceive"></param>
/// <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
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);
}
......
......@@ -41,7 +41,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary>
/// Server stream
/// </summary>
internal Stream Stream { get; set; }
internal CustomBufferedStream Stream { get; set; }
/// <summary>
/// Last time this connection was used
......
......@@ -22,12 +22,13 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <param name="remoteHostName"></param>
/// <param name="remotePort"></param>
/// <param name="httpVersion"></param>
/// <param name="isConnect"></param>
/// <param name="isHttps"></param>
/// <param name="externalHttpProxy"></param>
/// <param name="externalHttpsProxy"></param>
/// <returns></returns>
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;
var externalProxy = isHttps ? externalHttpsProxy : externalHttpProxy;
......@@ -60,9 +61,15 @@ namespace Titanium.Web.Proxy.Network.Tcp
if (useUpstreamProxy)
{
await client.ConnectAsync(externalProxy.HostName, externalProxy.Port);
}
else
{
await client.ConnectAsync(remoteHostName, remotePort);
}
stream = new CustomBufferedStream(client.GetStream(), server.BufferSize);
if (isHttps)
if (useUpstreamProxy && (isConnect || isHttps))
{
using (var writer = new HttpRequestWriter(stream))
{
......@@ -77,6 +84,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
Convert.ToBase64String(Encoding.UTF8.GetBytes(
externalProxy.UserName + ":" + externalProxy.Password)));
}
await writer.WriteLineAsync();
await writer.FlushAsync();
}
......@@ -93,12 +101,6 @@ namespace Titanium.Web.Proxy.Network.Tcp
await reader.ReadAndIgnoreAllLinesAsync();
}
}
}
else
{
await client.ConnectAsync(remoteHostName, remotePort);
stream = new CustomBufferedStream(client.GetStream(), server.BufferSize);
}
if (isHttps)
{
......
......@@ -125,6 +125,7 @@ namespace Titanium.Web.Proxy
if (!excluded && isClientHello)
{
httpRemoteUri = new Uri("https://" + httpUrl);
connectRequest.RequestUri = httpRemoteUri;
SslStream sslStream = null;
......@@ -159,9 +160,24 @@ namespace Titanium.Web.Proxy
else
{
//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); });
UpdateServerConnectionCount(false);
}
......@@ -336,16 +352,17 @@ namespace Titanium.Web.Proxy
break;
}
if (connection == null)
{
connection = await GetServerConnection(args);
}
//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();
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
......@@ -383,7 +400,7 @@ namespace Titanium.Web.Proxy
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); });
args.Dispose();
......@@ -550,8 +567,9 @@ namespace Titanium.Web.Proxy
/// Create a Server Connection
/// </summary>
/// <param name="args"></param>
/// <param name="isConnect"></param>
/// <returns></returns>
private async Task<TcpConnection> GetServerConnection(SessionEventArgs args)
private async Task<TcpConnection> GetServerConnection(SessionEventArgs args, bool isConnect)
{
ExternalProxy customUpStreamHttpProxy = null;
ExternalProxy customUpStreamHttpsProxy = null;
......@@ -578,7 +596,7 @@ namespace Titanium.Web.Proxy
args.WebSession.Request.RequestUri.Host,
args.WebSession.Request.RequestUri.Port,
args.WebSession.Request.HttpVersion,
args.IsHttps,
args.IsHttps, isConnect,
customUpStreamHttpProxy ?? UpStreamHttpProxy,
customUpStreamHttpsProxy ?? UpStreamHttpsProxy);
}
......
......@@ -85,24 +85,25 @@ namespace Titanium.Web.Proxy.Ssl
if (CompressionData.Length > 0)
{
int id = CompressionData[0];
string compression = null;
compression = compressions.Length > id ? compressions[id] : $"unknown [0x{id:X2}]";
int compressionMethod = CompressionData[0];
string compression = compressions.Length > compressionMethod
? compressions[compressionMethod]
: $"unknown [0x{compressionMethod:X2}]";
sb.AppendLine($"Compression: {compression}");
}
if (Ciphers.Length > 0)
{
sb.AppendLine($"Ciphers:");
foreach (int cipher in Ciphers)
sb.AppendLine("Ciphers:");
foreach (int cipherSuite in Ciphers)
{
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
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; }
......@@ -83,28 +83,19 @@ namespace Titanium.Web.Proxy.Ssl
}
}
if (CompressionData.Length > 0)
{
int id = CompressionData[0];
string compression = null;
compression = compressions.Length > id ? compressions[id] : $"unknown [0x{id:X2}]";
string compression = compressions.Length > CompressionMethod
? compressions[CompressionMethod]
: $"unknown [0x{CompressionMethod:X2}]";
sb.AppendLine($"Compression: {compression}");
}
if (Ciphers.Length > 0)
{
sb.AppendLine($"Ciphers:");
foreach (int cipher in Ciphers)
{
sb.Append("Cipher:");
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();
}
......
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers;
......@@ -108,42 +109,50 @@ namespace Titanium.Web.Proxy.Ssl
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;
if (majorVersion > 3 || majorVersion == 3 && minorVersion >= 1)
{
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>();
while (peekStream.Available > 3)
while (extensionsLength > 3)
{
int id = peekStream.ReadInt16();
length = peekStream.ReadInt16();
int length = peekStream.ReadInt16();
byte[] data = peekStream.ReadBytes(length);
extensions.Add(SslExtensions.GetExtension(id, data));
extensionsLength -= 4 + length;
}
}
}
}
var clientHelloInfo = new ClientHelloInfo
{
MajorVersion = majorVersion,
MinorVersion = minorVersion,
Random = random,
SessionId = sessionId,
Ciphers = ciphers,
CompressionData = compressionData,
Extensions = extensions,
};
return clientHelloInfo;
}
return null;
return extensions;
}
public static async Task<bool> IsServerHello(CustomBufferedStream serverStream)
......@@ -154,8 +163,6 @@ namespace Titanium.Web.Proxy.Ssl
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:
//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
int length = peekStream.ReadInt16();
if (peekStream.ReadByte() != 0x01)
if (peekStream.ReadByte() != 0x02)
{
// should be ClientHello
// should be ServerHello
return null;
}
......@@ -214,63 +221,18 @@ namespace Titanium.Web.Proxy.Ssl
byte[] random = peekStream.ReadBytes(32);
length = peekStream.ReadByte();
// sessionid + 2 ciphersData length
if (!await peekStream.EnsureBufferLength(length + 2))
// sessionid + cipherSuite + compressionMethod
if (!await peekStream.EnsureBufferLength(length + 2 + 1))
{
return null;
}
byte[] sessionId = peekStream.ReadBytes(length);
length = peekStream.ReadInt16();
// 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);
int cipherSuite = peekStream.ReadInt16();
byte compressionMethod = peekStream.ReadByte();
List<SslExtension> extensions = null;
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 extensions = await ReadExtensions(majorVersion, minorVersion, peekStream);
var serverHelloInfo = new ServerHelloInfo
{
......@@ -278,8 +240,8 @@ namespace Titanium.Web.Proxy.Ssl
MinorVersion = minorVersion,
Random = random,
SessionId = sessionId,
Ciphers = ciphers,
CompressionData = compressionData,
CipherSuite = cipherSuite,
CompressionMethod = compressionMethod,
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