Commit 77ecb585 authored by Honfika's avatar Honfika

Http request/response writer classes added. Request/response writer methods...

Http request/response writer classes added. Request/response writer methods moved to the new classes.
Some Async method name fixes (name ends with Async)
parent 9ee09f9b
...@@ -14,23 +14,11 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -14,23 +14,11 @@ namespace Titanium.Web.Proxy.Extensions
internal static class StreamExtensions internal static class StreamExtensions
{ {
/// <summary> /// <summary>
/// Copy streams asynchronously with an initial data inserted to the beginning of stream /// Copy streams asynchronously
/// </summary> /// </summary>
/// <param name="input"></param> /// <param name="input"></param>
/// <param name="initialData"></param>
/// <param name="output"></param> /// <param name="output"></param>
/// <returns></returns> /// <param name="onCopy"></param>
internal static async Task CopyToAsync(this Stream input, string initialData, Stream output)
{
if (!string.IsNullOrEmpty(initialData))
{
var bytes = Encoding.ASCII.GetBytes(initialData);
await output.WriteAsync(bytes, 0, bytes.Length);
}
await input.CopyToAsync(output);
}
internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy) internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy)
{ {
byte[] buffer = new byte[81920]; byte[] buffer = new byte[81920];
...@@ -109,104 +97,5 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -109,104 +97,5 @@ namespace Titanium.Web.Proxy.Extensions
} }
} }
} }
/// <summary>
/// Writes the byte array body to the given stream; optionally chunked
/// </summary>
/// <param name="clientStream"></param>
/// <param name="data"></param>
/// <param name="isChunked"></param>
/// <returns></returns>
internal static async Task WriteResponseBody(this Stream clientStream, byte[] data, bool isChunked)
{
if (!isChunked)
{
await clientStream.WriteAsync(data, 0, data.Length);
}
else
{
await WriteResponseBodyChunked(data, clientStream);
}
}
/// <summary>
/// Copies the specified content length number of bytes to the output stream from the given inputs stream
/// optionally chunked
/// </summary>
/// <param name="inStreamReader"></param>
/// <param name="bufferSize"></param>
/// <param name="outStream"></param>
/// <param name="isChunked"></param>
/// <param name="contentLength"></param>
/// <returns></returns>
internal static async Task WriteResponseBody(this CustomBinaryReader inStreamReader, int bufferSize, Stream outStream, bool isChunked,
long contentLength)
{
if (!isChunked)
{
//http 1.0
if (contentLength == -1)
{
contentLength = long.MaxValue;
}
await CopyBytesToStream(inStreamReader, outStream, contentLength);
}
else
{
await WriteResponseBodyChunked(inStreamReader, outStream);
}
}
/// <summary>
/// Copies the streams chunked
/// </summary>
/// <param name="inStreamReader"></param>
/// <param name="outStream"></param>
/// <returns></returns>
internal static async Task WriteResponseBodyChunked(this CustomBinaryReader inStreamReader, Stream outStream)
{
while (true)
{
string chunkHead = await inStreamReader.ReadLineAsync();
int chunkSize = int.Parse(chunkHead, NumberStyles.HexNumber);
if (chunkSize != 0)
{
var chunkHeadBytes = Encoding.ASCII.GetBytes(chunkSize.ToString("x2"));
await outStream.WriteAsync(chunkHeadBytes, 0, chunkHeadBytes.Length);
await outStream.WriteAsync(ProxyConstants.NewLineBytes, 0, ProxyConstants.NewLineBytes.Length);
await CopyBytesToStream(inStreamReader, outStream, chunkSize);
await outStream.WriteAsync(ProxyConstants.NewLineBytes, 0, ProxyConstants.NewLineBytes.Length);
await inStreamReader.ReadLineAsync();
}
else
{
await inStreamReader.ReadLineAsync();
await outStream.WriteAsync(ProxyConstants.ChunkEnd, 0, ProxyConstants.ChunkEnd.Length);
break;
}
}
}
/// <summary>
/// Copies the given input bytes to output stream chunked
/// </summary>
/// <param name="data"></param>
/// <param name="outStream"></param>
/// <returns></returns>
internal static async Task WriteResponseBodyChunked(this byte[] data, Stream outStream)
{
var chunkHead = Encoding.ASCII.GetBytes(data.Length.ToString("x2"));
await outStream.WriteAsync(chunkHead, 0, chunkHead.Length);
await outStream.WriteAsync(ProxyConstants.NewLineBytes, 0, ProxyConstants.NewLineBytes.Length);
await outStream.WriteAsync(data, 0, data.Length);
await outStream.WriteAsync(ProxyConstants.NewLineBytes, 0, ProxyConstants.NewLineBytes.Length);
await outStream.WriteAsync(ProxyConstants.ChunkEnd, 0, ProxyConstants.ChunkEnd.Length);
}
} }
} }
...@@ -108,6 +108,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -108,6 +108,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="buffer">An array of bytes. This method copies <paramref name="count" /> bytes from <paramref name="buffer" /> to the current stream.</param> /// <param name="buffer">An array of bytes. This method copies <paramref name="count" /> bytes from <paramref name="buffer" /> to the current stream.</param>
/// <param name="offset">The zero-based byte offset in <paramref name="buffer" /> at which to begin copying bytes to the current stream.</param> /// <param name="offset">The zero-based byte offset in <paramref name="buffer" /> at which to begin copying bytes to the current stream.</param>
/// <param name="count">The number of bytes to be written to the current stream.</param> /// <param name="count">The number of bytes to be written to the current stream.</param>
[DebuggerStepThrough]
public override void Write(byte[] buffer, int offset, int count) public override void Write(byte[] buffer, int offset, int count)
{ {
OnDataSent(buffer, offset, count); OnDataSent(buffer, offset, count);
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Helpers
{
sealed class HttpRequestWriter : HttpWriter
{
public HttpRequestWriter(Stream stream) : base(stream)
{
}
public HttpRequestWriter(Stream stream, bool leaveOpen) : base(stream, leaveOpen)
{
}
}
}
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Helpers
{
sealed class HttpResponseWriter : HttpWriter
{
public HttpResponseWriter(Stream stream) : base(stream)
{
}
public HttpResponseWriter(Stream stream, bool leaveOpen) : base(stream, leaveOpen)
{
}
/// <summary>
/// Writes the response.
/// </summary>
/// <param name="response"></param>
/// <param name="flush"></param>
/// <returns></returns>
public async Task WriteResponseAsync(Response response, bool flush = true)
{
await WriteResponseStatusAsync(response.HttpVersion, response.ResponseStatusCode, response.ResponseStatusDescription);
response.ResponseHeaders.FixProxyHeaders();
await WriteHeadersAsync(response.ResponseHeaders, flush);
}
/// <summary>
/// Write response status
/// </summary>
/// <param name="version"></param>
/// <param name="code"></param>
/// <param name="description"></param>
/// <returns></returns>
public Task WriteResponseStatusAsync(Version version, int code, string description)
{
return WriteLineAsync($"HTTP/{version.Major}.{version.Minor} {code} {description}");
}
/// <summary>
/// Writes the byte array body to the stream; optionally chunked
/// </summary>
/// <param name="data"></param>
/// <param name="isChunked"></param>
/// <returns></returns>
internal async Task WriteResponseBodyAsync(byte[] data, bool isChunked)
{
if (!isChunked)
{
await BaseStream.WriteAsync(data, 0, data.Length);
}
else
{
await WriteResponseBodyChunkedAsync(data);
}
}
/// <summary>
/// Copies the specified content length number of bytes to the output stream from the given inputs stream
/// optionally chunked
/// </summary>
/// <param name="bufferSize"></param>
/// <param name="inStreamReader"></param>
/// <param name="isChunked"></param>
/// <param name="contentLength"></param>
/// <returns></returns>
internal async Task WriteResponseBodyAsync(int bufferSize, CustomBinaryReader inStreamReader, bool isChunked,
long contentLength)
{
if (!isChunked)
{
//http 1.0
if (contentLength == -1)
{
contentLength = long.MaxValue;
}
await inStreamReader.CopyBytesToStream(BaseStream, contentLength);
}
else
{
await WriteResponseBodyChunkedAsync(inStreamReader);
}
}
/// <summary>
/// Copies the streams chunked
/// </summary>
/// <param name="inStreamReader"></param>
/// <returns></returns>
internal async Task WriteResponseBodyChunkedAsync(CustomBinaryReader inStreamReader)
{
while (true)
{
string chunkHead = await inStreamReader.ReadLineAsync();
int chunkSize = int.Parse(chunkHead, NumberStyles.HexNumber);
if (chunkSize != 0)
{
var chunkHeadBytes = Encoding.ASCII.GetBytes(chunkSize.ToString("x2"));
await BaseStream.WriteAsync(chunkHeadBytes, 0, chunkHeadBytes.Length);
await BaseStream.WriteAsync(ProxyConstants.NewLineBytes, 0, ProxyConstants.NewLineBytes.Length);
await inStreamReader.CopyBytesToStream(BaseStream, chunkSize);
await BaseStream.WriteAsync(ProxyConstants.NewLineBytes, 0, ProxyConstants.NewLineBytes.Length);
await inStreamReader.ReadLineAsync();
}
else
{
await inStreamReader.ReadLineAsync();
await BaseStream.WriteAsync(ProxyConstants.ChunkEnd, 0, ProxyConstants.ChunkEnd.Length);
break;
}
}
}
/// <summary>
/// Copies the given input bytes to output stream chunked
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
internal async Task WriteResponseBodyChunkedAsync(byte[] data)
{
var chunkHead = Encoding.ASCII.GetBytes(data.Length.ToString("x2"));
await BaseStream.WriteAsync(chunkHead, 0, chunkHead.Length);
await BaseStream.WriteAsync(ProxyConstants.NewLineBytes, 0, ProxyConstants.NewLineBytes.Length);
await BaseStream.WriteAsync(data, 0, data.Length);
await BaseStream.WriteAsync(ProxyConstants.NewLineBytes, 0, ProxyConstants.NewLineBytes.Length);
await BaseStream.WriteAsync(ProxyConstants.ChunkEnd, 0, ProxyConstants.ChunkEnd.Length);
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Helpers
{
abstract class HttpWriter : StreamWriter
{
public HttpWriter(Stream stream) : base(stream, Encoding.ASCII)
{
NewLine = ProxyConstants.NewLine;
}
public HttpWriter(Stream stream, bool leaveOpen) : base(stream, Encoding.ASCII, 1024, leaveOpen)
{
NewLine = ProxyConstants.NewLine;
}
public void WriteHeaders(HeaderCollection headers, bool flush = true)
{
if (headers != null)
{
foreach (var header in headers)
{
header.WriteToStream(this);
}
}
WriteLine();
if (flush)
{
Flush();
}
}
/// <summary>
/// Write the headers to client
/// </summary>
/// <param name="headers"></param>
/// <param name="flush"></param>
/// <returns></returns>
public async Task WriteHeadersAsync(HeaderCollection headers, bool flush = true)
{
if (headers != null)
{
foreach (var header in headers)
{
await header.WriteToStreamAsync(this);
}
}
await WriteLineAsync();
if (flush)
{
await FlushAsync();
}
}
}
}
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Ssl;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
{ {
public class ConnectResponse : Response public class ConnectResponse : Response
{ {
public string ServerHelloInfo { get; set; } public ServerHelloInfo ServerHelloInfo { get; set; }
/// <summary>
/// Creates a successfull CONNECT response
/// </summary>
/// <param name="httpVersion"></param>
/// <returns></returns>
internal static ConnectResponse CreateSuccessfullConnectResponse(Version httpVersion)
{
var response = new ConnectResponse
{
HttpVersion = httpVersion,
ResponseStatusCode = (int)HttpStatusCode.OK,
ResponseStatusDescription = "Connection Established"
};
response.ResponseHeaders.AddHeader("Timestamp", DateTime.Now.ToString());
return response;
}
} }
} }
...@@ -250,6 +250,21 @@ namespace Titanium.Web.Proxy.Http ...@@ -250,6 +250,21 @@ namespace Titanium.Web.Proxy.Http
return null; return null;
} }
/// <summary>
/// Fix proxy specific headers
/// </summary>
internal void FixProxyHeaders()
{
//If proxy-connection close was returned inform to close the connection
string proxyHeader = GetHeaderValueOrNull("proxy-connection");
RemoveHeader("proxy-connection");
if (proxyHeader != null)
{
SetOrAddHeaderValue("connection", proxyHeader);
}
}
/// <summary> /// <summary>
/// Returns an enumerator that iterates through the collection. /// Returns an enumerator that iterates through the collection.
/// </summary> /// </summary>
......
...@@ -4,6 +4,7 @@ using System.IO; ...@@ -4,6 +4,7 @@ using System.IO;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -77,35 +78,39 @@ namespace Titanium.Web.Proxy.Http ...@@ -77,35 +78,39 @@ namespace Titanium.Web.Proxy.Http
{ {
var stream = ServerConnection.Stream; var stream = ServerConnection.Stream;
var requestLines = new StringBuilder(); byte[] requestBytes;
using (var ms = new MemoryStream())
using (var writer = new HttpRequestWriter(ms))
{
//prepare the request & headers
writer.WriteLine($"{Request.Method} {Request.RequestUri.PathAndQuery} HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}");
//prepare the request & headers var upstreamProxy = ServerConnection.UpStreamHttpProxy;
requestLines.AppendLine($"{Request.Method} {Request.RequestUri.PathAndQuery} HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}");
//Send Authentication to Upstream proxy if needed
if (upstreamProxy != null
&& ServerConnection.IsHttps == false
&& !string.IsNullOrEmpty(upstreamProxy.UserName)
&& upstreamProxy.Password != null)
{
HttpHeader.ProxyConnectionKeepAlive.WriteToStream(writer);
HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password).WriteToStream(writer);
}
//Send Authentication to Upstream proxy if needed //write request headers
if (ServerConnection.UpStreamHttpProxy != null foreach (var header in Request.RequestHeaders)
&& ServerConnection.IsHttps == false
&& !string.IsNullOrEmpty(ServerConnection.UpStreamHttpProxy.UserName)
&& ServerConnection.UpStreamHttpProxy.Password != null)
{
requestLines.AppendLine("Proxy-Connection: keep-alive");
requestLines.AppendLine("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(
$"{ServerConnection.UpStreamHttpProxy.UserName}:{ServerConnection.UpStreamHttpProxy.Password}")));
}
//write request headers
foreach (var header in Request.RequestHeaders)
{
if (header.Name != "Proxy-Authorization")
{ {
requestLines.AppendLine($"{header.Name}: {header.Value}"); if (header.Name != "Proxy-Authorization")
{
header.WriteToStream(writer);
}
} }
}
requestLines.AppendLine(); writer.WriteLine();
writer.Flush();
string request = requestLines.ToString(); requestBytes = ms.ToArray();
var requestBytes = Encoding.ASCII.GetBytes(request); }
await stream.WriteAsync(requestBytes, 0, requestBytes.Length); await stream.WriteAsync(requestBytes, 0, requestBytes.Length);
await stream.FlushAsync(); await stream.FlushAsync();
......
using System; using System;
using System.IO; using System.IO;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Models namespace Titanium.Web.Proxy.Models
...@@ -13,6 +14,8 @@ namespace Titanium.Web.Proxy.Models ...@@ -13,6 +14,8 @@ 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");
/// <summary> /// <summary>
/// Constructor. /// Constructor.
/// </summary> /// </summary>
...@@ -49,7 +52,21 @@ namespace Titanium.Web.Proxy.Models ...@@ -49,7 +52,21 @@ namespace Titanium.Web.Proxy.Models
return $"{Name}: {Value}"; return $"{Name}: {Value}";
} }
internal async Task WriteToStream(StreamWriter writer) internal static HttpHeader GetProxyAuthorizationHeader(string userName, string password)
{
var result = new HttpHeader("Proxy-Authorization",
"Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{userName}:{password}")));
return result;
}
internal void WriteToStream(StreamWriter writer)
{
writer.Write(Name);
writer.Write(": ");
writer.WriteLine(Value);
}
internal async Task WriteToStreamAsync(StreamWriter writer)
{ {
await writer.WriteAsync(Name); await writer.WriteAsync(Name);
await writer.WriteAsync(": "); await writer.WriteAsync(": ");
......
...@@ -17,7 +17,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -17,7 +17,7 @@ namespace Titanium.Web.Proxy.Network
/// <summary> /// <summary>
/// holds the stream to client /// holds the stream to client
/// </summary> /// </summary>
internal Stream ClientStream { get; set; } internal CustomBufferedStream ClientStream { get; set; }
/// <summary> /// <summary>
/// Used to read line by line from client /// Used to read line by line from client
...@@ -27,6 +27,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -27,6 +27,6 @@ namespace Titanium.Web.Proxy.Network
/// <summary> /// <summary>
/// used to write line by line to client /// used to write line by line to client
/// </summary> /// </summary>
internal StreamWriter ClientStreamWriter { get; set; } internal HttpResponseWriter ClientStreamWriter { get; set; }
} }
} }
...@@ -52,23 +52,22 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -52,23 +52,22 @@ namespace Titanium.Web.Proxy.Network.Tcp
try try
{ {
//If this proxy uses another external proxy then create a tunnel request for HTTP/HTTPS connections
if (useUpstreamProxy)
{
#if NET45 #if NET45
client = new TcpClient(server.UpStreamEndPoint); client = new TcpClient(server.UpStreamEndPoint);
#else #else
client = new TcpClient(server.UpStreamEndPoint.AddressFamily); // probably this is not correct, but at least it compiles
client = new TcpClient(server.UpStreamEndPoint.AddressFamily);
#endif #endif
//If this proxy uses another external proxy then create a tunnel request for HTTP/HTTPS connections
if (useUpstreamProxy)
{
await client.ConnectAsync(externalProxy.HostName, externalProxy.Port); await client.ConnectAsync(externalProxy.HostName, externalProxy.Port);
stream = new CustomBufferedStream(client.GetStream(), server.BufferSize); stream = new CustomBufferedStream(client.GetStream(), server.BufferSize);
if (isHttps) if (isHttps)
{ {
using (var writer = new StreamWriter(stream, Encoding.ASCII, server.BufferSize, true) using (var writer = new HttpRequestWriter(stream, true))
{
NewLine = ProxyConstants.NewLine
})
{ {
await writer.WriteLineAsync($"CONNECT {remoteHostName}:{remotePort} HTTP/{httpVersion}"); await writer.WriteLineAsync($"CONNECT {remoteHostName}:{remotePort} HTTP/{httpVersion}");
await writer.WriteLineAsync($"Host: {remoteHostName}:{remotePort}"); await writer.WriteLineAsync($"Host: {remoteHostName}:{remotePort}");
...@@ -76,7 +75,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -76,7 +75,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
if (!string.IsNullOrEmpty(externalProxy.UserName) && externalProxy.Password != null) if (!string.IsNullOrEmpty(externalProxy.UserName) && externalProxy.Password != null)
{ {
await writer.WriteLineAsync("Proxy-Connection: keep-alive"); await HttpHeader.ProxyConnectionKeepAlive.WriteToStreamAsync(writer);
await writer.WriteLineAsync("Proxy-Authorization" + ": Basic " + await writer.WriteLineAsync("Proxy-Authorization" + ": Basic " +
Convert.ToBase64String(Encoding.UTF8.GetBytes( Convert.ToBase64String(Encoding.UTF8.GetBytes(
externalProxy.UserName + ":" + externalProxy.Password))); externalProxy.UserName + ":" + externalProxy.Password)));
...@@ -100,11 +99,6 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -100,11 +99,6 @@ namespace Titanium.Web.Proxy.Network.Tcp
} }
else else
{ {
#if NET45
client = new TcpClient(server.UpStreamEndPoint);
#else
client = new TcpClient(server.UpStreamEndPoint.AddressFamily);
#endif
await client.ConnectAsync(remoteHostName, remotePort); await client.ConnectAsync(remoteHostName, remotePort);
stream = new CustomBufferedStream(client.GetStream(), server.BufferSize); stream = new CustomBufferedStream(client.GetStream(), server.BufferSize);
} }
......
...@@ -7,6 +7,7 @@ using System.Text; ...@@ -7,6 +7,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
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;
...@@ -14,7 +15,7 @@ namespace Titanium.Web.Proxy ...@@ -14,7 +15,7 @@ namespace Titanium.Web.Proxy
{ {
public partial class ProxyServer public partial class ProxyServer
{ {
private async Task<bool> CheckAuthorization(StreamWriter clientStreamWriter, SessionEventArgs session) private async Task<bool> CheckAuthorization(HttpResponseWriter clientStreamWriter, SessionEventArgs session)
{ {
if (AuthenticateUserFunc == null) if (AuthenticateUserFunc == null)
{ {
...@@ -64,7 +65,7 @@ namespace Titanium.Web.Proxy ...@@ -64,7 +65,7 @@ namespace Titanium.Web.Proxy
} }
} }
private async Task<Response> SendAuthentication407Response(StreamWriter clientStreamWriter, string description) private async Task<Response> SendAuthentication407Response(HttpResponseWriter clientStreamWriter, string description)
{ {
var response = new Response var response = new Response
{ {
...@@ -76,7 +77,7 @@ namespace Titanium.Web.Proxy ...@@ -76,7 +77,7 @@ namespace Titanium.Web.Proxy
response.ResponseHeaders.AddHeader("Proxy-Authenticate", $"Basic realm=\"{ProxyRealm}\""); response.ResponseHeaders.AddHeader("Proxy-Authenticate", $"Basic realm=\"{ProxyRealm}\"");
response.ResponseHeaders.AddHeader("Proxy-Connection", "close"); response.ResponseHeaders.AddHeader("Proxy-Connection", "close");
await WriteResponse(response, clientStreamWriter); await clientStreamWriter.WriteResponseAsync(response);
return response; return response;
} }
} }
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
...@@ -50,10 +51,6 @@ namespace Titanium.Web.Proxy ...@@ -50,10 +51,6 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
private Action<Exception> exceptionFunc; private Action<Exception> exceptionFunc;
#if NET45
private WinHttpWebProxyFinder systemProxyResolver;
#endif
/// <summary> /// <summary>
/// Backing field for corresponding public property /// Backing field for corresponding public property
/// </summary> /// </summary>
...@@ -75,6 +72,8 @@ namespace Titanium.Web.Proxy ...@@ -75,6 +72,8 @@ namespace Titanium.Web.Proxy
private TcpConnectionFactory tcpConnectionFactory { get; } private TcpConnectionFactory tcpConnectionFactory { get; }
#if NET45 #if NET45
private WinHttpWebProxyFinder systemProxyResolver;
/// <summary> /// <summary>
/// Manage system proxy settings /// Manage system proxy settings
/// </summary> /// </summary>
...@@ -274,6 +273,7 @@ namespace Titanium.Web.Proxy ...@@ -274,6 +273,7 @@ namespace Titanium.Web.Proxy
/// Realm used during Proxy Basic Authentication /// Realm used during Proxy Basic Authentication
/// </summary> /// </summary>
public string ProxyRealm { get; set; } = "TitaniumProxy"; public string ProxyRealm { get; set; } = "TitaniumProxy";
/// <summary> /// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP requests /// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP requests
/// return the ExternalProxy object with valid credentials /// return the ExternalProxy object with valid credentials
...@@ -633,6 +633,27 @@ namespace Titanium.Web.Proxy ...@@ -633,6 +633,27 @@ namespace Titanium.Web.Proxy
proxyRunning = false; proxyRunning = false;
} }
/// <summary>
/// Handle dispose of a client/server session
/// </summary>
/// <param name="clientStream"></param>
/// <param name="clientStreamReader"></param>
/// <param name="clientStreamWriter"></param>
/// <param name="serverConnection"></param>
private void Dispose(CustomBufferedStream clientStream, CustomBinaryReader clientStreamReader, HttpResponseWriter clientStreamWriter, TcpConnection serverConnection)
{
clientStream?.Dispose();
clientStreamReader?.Dispose();
clientStreamWriter?.Dispose();
if (serverConnection != null)
{
serverConnection.Dispose();
UpdateServerConnectionCount(false);
}
}
/// <summary> /// <summary>
/// Dispose Proxy. /// Dispose Proxy.
/// </summary> /// </summary>
......
...@@ -16,7 +16,6 @@ using Titanium.Web.Proxy.Helpers; ...@@ -16,7 +16,6 @@ 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;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.Shared;
using Titanium.Web.Proxy.Ssl; using Titanium.Web.Proxy.Ssl;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
...@@ -40,10 +39,7 @@ namespace Titanium.Web.Proxy ...@@ -40,10 +39,7 @@ namespace Titanium.Web.Proxy
var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);
var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
var clientStreamWriter = new StreamWriter(clientStream) var clientStreamWriter = new HttpResponseWriter(clientStream);
{
NewLine = ProxyConstants.NewLine
};
Uri httpRemoteUri; Uri httpRemoteUri;
...@@ -115,8 +111,8 @@ namespace Titanium.Web.Proxy ...@@ -115,8 +111,8 @@ namespace Titanium.Web.Proxy
} }
//write back successfull CONNECT response //write back successfull CONNECT response
connectArgs.WebSession.Response = CreateConnectResponse(version); connectArgs.WebSession.Response = ConnectResponse.CreateSuccessfullConnectResponse(version);
await WriteResponse(connectArgs.WebSession.Response, clientStreamWriter); await clientStreamWriter.WriteResponseAsync(connectArgs.WebSession.Response);
var clientHelloInfo = await HttpsTools.GetClientHelloInfo(clientStream); var clientHelloInfo = await HttpsTools.GetClientHelloInfo(clientStream);
bool isClientHello = clientHelloInfo != null; bool isClientHello = clientHelloInfo != null;
...@@ -153,10 +149,7 @@ namespace Titanium.Web.Proxy ...@@ -153,10 +149,7 @@ namespace Titanium.Web.Proxy
clientStreamReader.Dispose(); clientStreamReader.Dispose();
clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
clientStreamWriter = new StreamWriter(clientStream) clientStreamWriter = new HttpResponseWriter(clientStream);
{
NewLine = ProxyConstants.NewLine
};
} }
catch catch
{ {
...@@ -167,7 +160,7 @@ namespace Titanium.Web.Proxy ...@@ -167,7 +160,7 @@ namespace Titanium.Web.Proxy
//Now read the actual HTTPS request line //Now read the actual HTTPS request line
httpCmd = await clientStreamReader.ReadLineAsync(); httpCmd = await clientStreamReader.ReadLineAsync();
} }
//Sorry cannot do a HTTPS request decrypt to port 80 at this time //Hostname is excluded or it is not an HTTPS connect
else else
{ {
//create new connection //create new connection
...@@ -212,7 +205,7 @@ namespace Titanium.Web.Proxy ...@@ -212,7 +205,7 @@ namespace Titanium.Web.Proxy
var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);
CustomBinaryReader clientStreamReader = null; CustomBinaryReader clientStreamReader = null;
StreamWriter clientStreamWriter = null; HttpResponseWriter clientStreamWriter = null;
try try
{ {
...@@ -237,10 +230,7 @@ namespace Titanium.Web.Proxy ...@@ -237,10 +230,7 @@ namespace Titanium.Web.Proxy
} }
clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
clientStreamWriter = new StreamWriter(clientStream) clientStreamWriter = new HttpResponseWriter(clientStream);
{
NewLine = ProxyConstants.NewLine
};
//now read the request line //now read the request line
string httpCmd = await clientStreamReader.ReadLineAsync(); string httpCmd = await clientStreamReader.ReadLineAsync();
...@@ -273,8 +263,8 @@ namespace Titanium.Web.Proxy ...@@ -273,8 +263,8 @@ namespace Titanium.Web.Proxy
/// <param name="connectRequest"></param> /// <param name="connectRequest"></param>
/// <param name="isTransparentEndPoint"></param> /// <param name="isTransparentEndPoint"></param>
/// <returns></returns> /// <returns></returns>
private async Task<bool> HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, private async Task<bool> HandleHttpSessionRequest(TcpClient client, string httpCmd, CustomBufferedStream clientStream,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsConnectHostname, CustomBinaryReader clientStreamReader, HttpResponseWriter clientStreamWriter, string httpsConnectHostname,
ProxyEndPoint endPoint, ConnectRequest connectRequest, bool isTransparentEndPoint = false) ProxyEndPoint endPoint, ConnectRequest connectRequest, bool isTransparentEndPoint = false)
{ {
bool disposed = false; bool disposed = false;
...@@ -368,26 +358,16 @@ namespace Titanium.Web.Proxy ...@@ -368,26 +358,16 @@ namespace Titanium.Web.Proxy
{ {
//prepare the prefix content //prepare the prefix content
var requestHeaders = args.WebSession.Request.RequestHeaders; var requestHeaders = args.WebSession.Request.RequestHeaders;
byte[] requestBytes;
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
using (var writer = new StreamWriter(ms, Encoding.ASCII) { NewLine = ProxyConstants.NewLine }) using (var writer = new HttpRequestWriter(ms))
{ {
writer.WriteLine(httpCmd); writer.WriteLine(httpCmd);
writer.WriteHeaders(requestHeaders);
if (requestHeaders != null) requestBytes = ms.ToArray();
{
foreach (string header in requestHeaders.Select(t => t.ToString()))
{
writer.WriteLine(header);
}
}
writer.WriteLine();
writer.Flush();
var data = ms.ToArray();
await connection.Stream.WriteAsync(data, 0, data.Length);
} }
await connection.Stream.WriteAsync(requestBytes, 0, requestBytes.Length);
string httpStatus = await connection.StreamReader.ReadLineAsync(); string httpStatus = await connection.StreamReader.ReadLineAsync();
Version responseVersion; Version responseVersion;
...@@ -400,7 +380,7 @@ namespace Titanium.Web.Proxy ...@@ -400,7 +380,7 @@ namespace Titanium.Web.Proxy
await HeaderParser.ReadHeaders(connection.StreamReader, args.WebSession.Response.ResponseHeaders); await HeaderParser.ReadHeaders(connection.StreamReader, args.WebSession.Response.ResponseHeaders);
await WriteResponse(args.WebSession.Response, clientStreamWriter); await clientStreamWriter.WriteResponseAsync(args.WebSession.Response);
//If user requested call back then do it //If user requested call back then do it
if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked)
...@@ -481,12 +461,12 @@ namespace Titanium.Web.Proxy ...@@ -481,12 +461,12 @@ namespace Titanium.Web.Proxy
{ {
if (args.WebSession.Request.Is100Continue) if (args.WebSession.Request.Is100Continue)
{ {
await WriteResponseStatus(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.Continue, "Continue", args.ProxyClient.ClientStreamWriter); await args.ProxyClient.ClientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.Continue, "Continue");
await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
} }
else if (args.WebSession.Request.ExpectationFailed) else if (args.WebSession.Request.ExpectationFailed)
{ {
await WriteResponseStatus(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.ExpectationFailed, "Expectation Failed", args.ProxyClient.ClientStreamWriter); await args.ProxyClient.ClientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.ExpectationFailed, "Expectation Failed");
await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
} }
} }
...@@ -608,25 +588,6 @@ namespace Titanium.Web.Proxy ...@@ -608,25 +588,6 @@ namespace Titanium.Web.Proxy
customUpStreamHttpsProxy ?? UpStreamHttpsProxy); customUpStreamHttpsProxy ?? UpStreamHttpsProxy);
} }
/// <summary>
/// Write successfull CONNECT response to client
/// </summary>
/// <param name="httpVersion"></param>
/// <returns></returns>
private ConnectResponse CreateConnectResponse(Version httpVersion)
{
var response = new ConnectResponse
{
HttpVersion = httpVersion,
ResponseStatusCode = (int)HttpStatusCode.OK,
ResponseStatusDescription = "Connection established"
};
response.ResponseHeaders.AddHeader("Timestamp", DateTime.Now.ToString());
return response;
}
/// <summary> /// <summary>
/// prepare the request headers so that we can avoid encodings not parsable by this proxy /// prepare the request headers so that we can avoid encodings not parsable by this proxy
/// </summary> /// </summary>
...@@ -644,7 +605,7 @@ namespace Titanium.Web.Proxy ...@@ -644,7 +605,7 @@ namespace Titanium.Web.Proxy
} }
} }
FixProxyHeaders(requestHeaders); requestHeaders.FixProxyHeaders();
} }
/// <summary> /// <summary>
......
...@@ -72,18 +72,19 @@ namespace Titanium.Web.Proxy ...@@ -72,18 +72,19 @@ namespace Titanium.Web.Proxy
//Write back to client 100-conitinue response if that's what server returned //Write back to client 100-conitinue response if that's what server returned
if (response.Is100Continue) if (response.Is100Continue)
{ {
await WriteResponseStatus(response.HttpVersion, (int)HttpStatusCode.Continue, "Continue", args.ProxyClient.ClientStreamWriter); await args.ProxyClient.ClientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, (int)HttpStatusCode.Continue, "Continue");
await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
} }
else if (response.ExpectationFailed) else if (response.ExpectationFailed)
{ {
await WriteResponseStatus(response.HttpVersion, (int)HttpStatusCode.ExpectationFailed, "Expectation Failed", args.ProxyClient.ClientStreamWriter); await args.ProxyClient.ClientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, (int)HttpStatusCode.ExpectationFailed, "Expectation Failed");
await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
} }
//Write back response status to client //Write back response status to client
await WriteResponseStatus(response.HttpVersion, response.ResponseStatusCode, response.ResponseStatusDescription, args.ProxyClient.ClientStreamWriter); await args.ProxyClient.ClientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.ResponseStatusCode, response.ResponseStatusDescription);
response.ResponseHeaders.FixProxyHeaders();
if (response.ResponseBodyRead) if (response.ResponseBodyRead)
{ {
bool isChunked = response.IsChunked; bool isChunked = response.IsChunked;
...@@ -103,17 +104,17 @@ namespace Titanium.Web.Proxy ...@@ -103,17 +104,17 @@ namespace Titanium.Web.Proxy
} }
} }
await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, response); await args.ProxyClient.ClientStreamWriter.WriteHeadersAsync(response.ResponseHeaders);
await args.ProxyClient.ClientStream.WriteResponseBody(response.ResponseBody, isChunked); await args.ProxyClient.ClientStreamWriter.WriteResponseBodyAsync(response.ResponseBody, isChunked);
} }
else else
{ {
await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, response); await args.ProxyClient.ClientStreamWriter.WriteHeadersAsync(response.ResponseHeaders);
//Write body if exists //Write body if exists
if (response.HasBody) if (response.HasBody)
{ {
await args.WebSession.ServerConnection.StreamReader.WriteResponseBody(BufferSize, args.ProxyClient.ClientStream, await args.ProxyClient.ClientStreamWriter.WriteResponseBodyAsync(BufferSize, args.WebSession.ServerConnection.StreamReader,
response.IsChunked, response.ContentLength); response.IsChunked, response.ContentLength);
} }
} }
...@@ -144,91 +145,5 @@ namespace Titanium.Web.Proxy ...@@ -144,91 +145,5 @@ namespace Titanium.Web.Proxy
var compressor = compressionFactory.Create(encodingType); var compressor = compressionFactory.Create(encodingType);
return await compressor.Compress(responseBodyStream); return await compressor.Compress(responseBodyStream);
} }
/// <summary>
/// Writes the response.
/// </summary>
/// <param name="response"></param>
/// <param name="responseWriter"></param>
/// <param name="flush"></param>
/// <returns></returns>
private async Task WriteResponse(Response response, StreamWriter responseWriter, bool flush = true)
{
await WriteResponseStatus(response.HttpVersion, response.ResponseStatusCode, response.ResponseStatusDescription, responseWriter);
await WriteResponseHeaders(responseWriter, response, flush);
}
/// <summary>
/// Write response status
/// </summary>
/// <param name="version"></param>
/// <param name="code"></param>
/// <param name="description"></param>
/// <param name="responseWriter"></param>
/// <returns></returns>
private async Task WriteResponseStatus(Version version, int code, string description, StreamWriter responseWriter)
{
await responseWriter.WriteLineAsync($"HTTP/{version.Major}.{version.Minor} {code} {description}");
}
/// <summary>
/// Write response headers to client
/// </summary>
/// <param name="responseWriter"></param>
/// <param name="response"></param>
/// <param name="flush"></param>
/// <returns></returns>
private async Task WriteResponseHeaders(StreamWriter responseWriter, Response response, bool flush = true)
{
FixProxyHeaders(response.ResponseHeaders);
foreach (var header in response.ResponseHeaders)
{
await header.WriteToStream(responseWriter);
}
await responseWriter.WriteLineAsync();
if (flush)
{
await responseWriter.FlushAsync();
}
}
/// <summary>
/// Fix proxy specific headers
/// </summary>
/// <param name="headers"></param>
private void FixProxyHeaders(HeaderCollection headers)
{
//If proxy-connection close was returned inform to close the connection
string proxyHeader = headers.GetHeaderValueOrNull("proxy-connection");
headers.RemoveHeader("proxy-connection");
if (proxyHeader != null)
{
headers.SetOrAddHeaderValue("connection", proxyHeader);
}
}
/// <summary>
/// Handle dispose of a client/server session
/// </summary>
/// <param name="clientStream"></param>
/// <param name="clientStreamReader"></param>
/// <param name="clientStreamWriter"></param>
/// <param name="serverConnection"></param>
private void Dispose(Stream clientStream, CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, TcpConnection serverConnection)
{
clientStream?.Dispose();
clientStreamReader?.Dispose();
clientStreamWriter?.Dispose();
if (serverConnection != null)
{
serverConnection.Dispose();
UpdateServerConnectionCount(false);
}
}
} }
} }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Titanium.Web.Proxy.Ssl
{
public class ServerHelloInfo
{
private static readonly string[] compressions = {
"null",
"DEFLATE"
};
public int MajorVersion { get; set; }
public int MinorVersion { get; set; }
public byte[] Random { get; set; }
public DateTime Time
{
get
{
DateTime time = DateTime.MinValue;
if (Random.Length > 3)
{
time = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)
.AddSeconds(((uint)Random[3] << 24) + ((uint)Random[2] << 16) + ((uint)Random[1] << 8) + (uint)Random[0]).ToLocalTime();
}
return time;
}
}
public byte[] SessionId { get; set; }
public int[] Ciphers { get; set; }
public byte[] CompressionData { get; set; }
public List<SslExtension> Extensions { get; set; }
private static string SslVersionToString(int major, int minor)
{
string str = "Unknown";
if (major == 3 && minor == 3)
str = "TLS/1.2";
else if (major == 3 && minor == 2)
str = "TLS/1.1";
else if (major == 3 && minor == 1)
str = "TLS/1.0";
else if (major == 3 && minor == 0)
str = "SSL/3.0";
else if (major == 2 && minor == 0)
str = "SSL/2.0";
return $"{major}.{minor} ({str})";
}
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine("A SSLv3-compatible ServerHello handshake was found. Titanium extracted the parameters below.");
sb.AppendLine();
sb.AppendLine($"Version: {SslVersionToString(MajorVersion, MinorVersion)}");
sb.AppendLine($"Random: {string.Join(" ", Random.Select(x => x.ToString("X2")))}");
sb.AppendLine($"\"Time\": {Time}");
sb.AppendLine($"SessionID: {string.Join(" ", SessionId.Select(x => x.ToString("X2")))}");
if (Extensions != null)
{
sb.AppendLine("Extensions:");
foreach (var extension in Extensions)
{
sb.AppendLine($"{extension.Name}: {extension.Data}");
}
}
if (CompressionData.Length > 0)
{
int id = CompressionData[0];
string compression = null;
compression = compressions.Length > id ? compressions[id] : $"unknown [0x{id:X2}]";
sb.AppendLine($"Compression: {compression}");
}
if (Ciphers.Length > 0)
{
sb.AppendLine($"Ciphers:");
foreach (int cipher in Ciphers)
{
string cipherStr;
if (!SslCiphers.Ciphers.TryGetValue(cipher, out cipherStr))
{
cipherStr = $"unknown";
}
sb.AppendLine($"[0x{cipher:X4}] {cipherStr}");
}
}
return sb.ToString();
}
}
}
\ No newline at end of file
...@@ -147,5 +147,148 @@ namespace Titanium.Web.Proxy.Ssl ...@@ -147,5 +147,148 @@ namespace Titanium.Web.Proxy.Ssl
return null; return null;
} }
public static async Task<bool> IsServerHello(CustomBufferedStream serverStream)
{
var serverHello = await GetServerHelloInfo(serverStream);
return serverHello != null;
}
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
int recordType = await serverStream.PeekByteAsync(0);
if (recordType == 0x80)
{
// copied from client hello, not tested. SSL2 is deprecated
var peekStream = new CustomBufferedPeekStream(serverStream, 1);
//SSL 2
int length = peekStream.ReadByte();
if (length < 9)
{
// Message body too short.
return null;
}
if (peekStream.ReadByte() != 0x02)
{
// should be ServerHello
return null;
}
int majorVersion = serverStream.ReadByte();
int minorVersion = serverStream.ReadByte();
return new ServerHelloInfo();
}
else if (recordType == 0x16)
{
var peekStream = new CustomBufferedPeekStream(serverStream, 1);
//should contain at least 43 bytes
// 2 version + 2 length + 1 type + 3 length(?) + 2 version + 32 random + 1 sessionid length
if (!await peekStream.EnsureBufferLength(43))
{
return null;
}
//SSL 3.0 or TLS 1.0, 1.1 and 1.2
int majorVersion = peekStream.ReadByte();
int minorVersion = peekStream.ReadByte();
int length = peekStream.ReadInt16();
if (peekStream.ReadByte() != 0x01)
{
// should be ClientHello
return null;
}
length = peekStream.ReadInt24();
majorVersion = peekStream.ReadByte();
minorVersion = peekStream.ReadByte();
byte[] random = peekStream.ReadBytes(32);
length = peekStream.ReadByte();
// sessionid + 2 ciphersData length
if (!await peekStream.EnsureBufferLength(length + 2))
{
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);
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 serverHelloInfo = new ServerHelloInfo
{
MajorVersion = majorVersion,
MinorVersion = minorVersion,
Random = random,
SessionId = sessionId,
Ciphers = ciphers,
CompressionData = compressionData,
Extensions = extensions,
};
return serverHelloInfo;
}
return null;
}
} }
} }
...@@ -92,9 +92,13 @@ ...@@ -92,9 +92,13 @@
<Compile Include="Helpers\CustomBufferedPeekStream.cs" /> <Compile Include="Helpers\CustomBufferedPeekStream.cs" />
<Compile Include="Helpers\CustomBufferedStream.cs" /> <Compile Include="Helpers\CustomBufferedStream.cs" />
<Compile Include="Helpers\Firefox.cs" /> <Compile Include="Helpers\Firefox.cs" />
<Compile Include="Helpers\HttpRequestWriter.cs" />
<Compile Include="Helpers\HttpResponseWriter.cs" />
<Compile Include="Helpers\HttpWriter.cs" />
<Compile Include="Helpers\HttpHelper.cs" /> <Compile Include="Helpers\HttpHelper.cs" />
<Compile Include="Helpers\Network.cs" /> <Compile Include="Helpers\Network.cs" />
<Compile Include="Helpers\Tcp.cs" /> <Compile Include="Helpers\Tcp.cs" />
<Compile Include="Ssl\ServerHelloInfo.cs" />
<Compile Include="Ssl\ClientHelloInfo.cs" /> <Compile Include="Ssl\ClientHelloInfo.cs" />
<Compile Include="Http\ConnectRequest.cs" /> <Compile Include="Http\ConnectRequest.cs" />
<Compile Include="Http\ConnectResponse.cs" /> <Compile Include="Http\ConnectResponse.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