Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.

Dns resolve from code #496

Merged
merged 3 commits into from
Sep 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"**/*.Basic.csproj/": true,
"**/*.Docs.csproj/": true,
"**/*.Proxy.csproj/": true,
"**/*.Proxy.csproj" : true
"**/*.Mono.csproj" : true
},
"search.exclude": {
"**/.build": true,
Expand All @@ -32,6 +32,6 @@
"**/*.Basic.csproj/": true,
"**/*.Docs.csproj/": true,
"**/*.Proxy.csproj/": true,
"**/*.Proxy.csproj" : true
"**/*.Mono.csproj" : true
}
}
1 change: 1 addition & 0 deletions omnisharp.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"fileOptions": {
"excludeSearchPatterns": [
"**/*.sln",
"**/*.Docs.csproj",
"**/tests/",
"**/Titanium.Web.Proxy.Examples.Wpf/",
Expand Down
80 changes: 52 additions & 28 deletions src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
internal class TcpConnectionFactory : IDisposable
{

//Tcp server connection pool cache
private readonly ConcurrentDictionary<string, ConcurrentQueue<TcpServerConnection>> cache
= new ConcurrentDictionary<string, ConcurrentQueue<TcpServerConnection>>();
Expand Down Expand Up @@ -74,10 +75,10 @@ internal string GetConnectionCacheKey(string remoteHostName, int remotePort,
/// <summary>
/// Gets the connection cache key.
/// </summary>
/// <param name="args">The session event arguments.</param>
/// <param name="session">The session event arguments.</param>
/// <param name="applicationProtocol"></param>
/// <returns></returns>
internal async Task<string> GetConnectionCacheKey(ProxyServer server, SessionEventArgsBase args,
internal async Task<string> GetConnectionCacheKey(ProxyServer server, SessionEventArgsBase session,
SslApplicationProtocol applicationProtocol)
{
List<SslApplicationProtocol> applicationProtocols = null;
Expand All @@ -88,32 +89,32 @@ internal async Task<string> GetConnectionCacheKey(ProxyServer server, SessionEve

ExternalProxy customUpStreamProxy = null;

bool isHttps = args.IsHttps;
bool isHttps = session.IsHttps;
if (server.GetCustomUpStreamProxyFunc != null)
{
customUpStreamProxy = await server.GetCustomUpStreamProxyFunc(args);
customUpStreamProxy = await server.GetCustomUpStreamProxyFunc(session);
}

args.CustomUpStreamProxyUsed = customUpStreamProxy;
session.CustomUpStreamProxyUsed = customUpStreamProxy;

return GetConnectionCacheKey(
args.WebSession.Request.RequestUri.Host,
args.WebSession.Request.RequestUri.Port,
session.WebSession.Request.RequestUri.Host,
session.WebSession.Request.RequestUri.Port,
isHttps, applicationProtocols,
server, args.WebSession.UpStreamEndPoint ?? server.UpStreamEndPoint,
server, session.WebSession.UpStreamEndPoint ?? server.UpStreamEndPoint,
customUpStreamProxy ?? (isHttps ? server.UpStreamHttpsProxy : server.UpStreamHttpProxy));
}


/// <summary>
/// Create a server connection.
/// </summary>
/// <param name="args">The session event arguments.</param>
/// <param name="session">The session event arguments.</param>
/// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="applicationProtocol"></param>
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
internal Task<TcpServerConnection> GetServerConnection(ProxyServer server, SessionEventArgsBase args, bool isConnect,
internal Task<TcpServerConnection> GetServerConnection(ProxyServer server, SessionEventArgsBase session, bool isConnect,
SslApplicationProtocol applicationProtocol, bool noCache, CancellationToken cancellationToken)
{
List<SslApplicationProtocol> applicationProtocols = null;
Expand All @@ -122,36 +123,36 @@ internal Task<TcpServerConnection> GetServerConnection(ProxyServer server, Sessi
applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
}

return GetServerConnection(server, args, isConnect, applicationProtocols, noCache, cancellationToken);
return GetServerConnection(server, session, isConnect, applicationProtocols, noCache, cancellationToken);
}

/// <summary>
/// Create a server connection.
/// </summary>
/// <param name="args">The session event arguments.</param>
/// <param name="session">The session event arguments.</param>
/// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="applicationProtocols"></param>
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
internal async Task<TcpServerConnection> GetServerConnection(ProxyServer server, SessionEventArgsBase args, bool isConnect,
internal async Task<TcpServerConnection> GetServerConnection(ProxyServer server, SessionEventArgsBase session, bool isConnect,
List<SslApplicationProtocol> applicationProtocols, bool noCache, CancellationToken cancellationToken)
{
ExternalProxy customUpStreamProxy = null;

bool isHttps = args.IsHttps;
bool isHttps = session.IsHttps;
if (server.GetCustomUpStreamProxyFunc != null)
{
customUpStreamProxy = await server.GetCustomUpStreamProxyFunc(args);
customUpStreamProxy = await server.GetCustomUpStreamProxyFunc(session);
}

args.CustomUpStreamProxyUsed = customUpStreamProxy;
session.CustomUpStreamProxyUsed = customUpStreamProxy;

return await GetServerConnection(
args.WebSession.Request.RequestUri.Host,
args.WebSession.Request.RequestUri.Port,
args.WebSession.Request.HttpVersion,
session.WebSession.Request.RequestUri.Host,
session.WebSession.Request.RequestUri.Port,
session.WebSession.Request.HttpVersion,
isHttps, applicationProtocols, isConnect,
server, args.WebSession.UpStreamEndPoint ?? server.UpStreamEndPoint,
server, session, session.WebSession.UpStreamEndPoint ?? server.UpStreamEndPoint,
customUpStreamProxy ?? (isHttps ? server.UpStreamHttpsProxy : server.UpStreamHttpProxy),
noCache, cancellationToken);
}
Expand All @@ -172,7 +173,7 @@ internal async Task<TcpServerConnection> GetServerConnection(ProxyServer server,
/// <returns></returns>
internal async Task<TcpServerConnection> GetServerConnection(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect,
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy,
ProxyServer proxyServer, SessionEventArgsBase session, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy,
bool noCache, CancellationToken cancellationToken)
{
var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort,
Expand Down Expand Up @@ -203,7 +204,7 @@ internal async Task<TcpServerConnection> GetServerConnection(string remoteHostNa
}

var connection = await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps,
applicationProtocols, isConnect, proxyServer, upStreamEndPoint, externalProxy, cancellationToken);
applicationProtocols, isConnect, proxyServer, session, upStreamEndPoint, externalProxy, cancellationToken);

connection.CacheKey = cacheKey;

Expand All @@ -220,13 +221,14 @@ internal async Task<TcpServerConnection> GetServerConnection(string remoteHostNa
/// <param name="applicationProtocols">The list of HTTPS application level protocol to negotiate if needed.</param>
/// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="proxyServer">The current ProxyServer instance.</param>
/// <param name="session">The http session.</param>
/// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param>
/// <param name="externalProxy">The external proxy to make request via.</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
private async Task<TcpServerConnection> createServerConnection(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect,
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy,
ProxyServer proxyServer, SessionEventArgsBase session, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy,
CancellationToken cancellationToken)
{
//deny connection to proxy end points to avoid infinite connection loop.
Expand Down Expand Up @@ -282,16 +284,37 @@ private async Task<TcpServerConnection> createServerConnection(string remoteHost
tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
}

// If this proxy uses another external proxy then create a tunnel request for HTTP/HTTPS connections
if (useUpstreamProxy)
var hostname = useUpstreamProxy ? externalProxy.HostName : remoteHostName;
var port = useUpstreamProxy ? externalProxy.Port : remotePort;

var ipHostEntry = await Dns.GetHostEntryAsync(hostname);
if (ipHostEntry == null || ipHostEntry.AddressList.Length == 0)
{
await tcpClient.ConnectAsync(externalProxy.HostName, externalProxy.Port);
throw new Exception($"Could not resolve the hostname {hostname}");
}
else

session.TimeLine["Dns Resolved"] = DateTime.Now;

var ipAddresses = ipHostEntry.AddressList;

for (int i = 0; i < ipAddresses.Length; i++)
{
await tcpClient.ConnectAsync(remoteHostName, remotePort);
try
{
await tcpClient.ConnectAsync(ipAddresses[i], port);
break;
}
catch (Exception e)
{
if (i == ipAddresses.Length - 1)
{
throw new Exception($"Could not establish connection to {hostname}", e);
}
}
}

session.TimeLine["Connection Established"] = DateTime.Now;

await proxyServer.InvokeConnectionCreateEvent(tcpClient, false);

stream = new CustomBufferedStream(tcpClient.GetStream(), proxyServer.BufferPool, proxyServer.BufferSize);
Expand Down Expand Up @@ -347,6 +370,7 @@ private async Task<TcpServerConnection> createServerConnection(string remoteHost
#if NETCOREAPP2_1
negotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
#endif
session.TimeLine["HTTPS Established"] = DateTime.Now;
}
}
catch (Exception)
Expand Down
4 changes: 1 addition & 3 deletions src/Titanium.Web.Proxy/ProxyServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ public ProxyServer(string rootCertificateName, string rootCertificateIssuerName,
bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false,
bool trustRootCertificateAsAdmin = false)
{
// default values
ConnectionTimeOutSeconds = 60;

if (BufferPool == null)
{
Expand Down Expand Up @@ -186,7 +184,7 @@ public ProxyServer(string rootCertificateName, string rootCertificateIssuerName,
/// This will also determine the pool eviction time when connection pool is enabled.
/// Default value is 60 seconds.
/// </summary>
public int ConnectionTimeOutSeconds { get; set; }
public int ConnectionTimeOutSeconds { get; set; } = 60;

/// <summary>
/// Maximum number of concurrent connections per remote host in cache.
Expand Down
4 changes: 3 additions & 1 deletion src/Titanium.Web.Proxy/RequestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ await clientStreamWriter.WriteResponseAsync(args.WebSession.Response,
//for connection pool, retry fails until cache is exhausted.
var result = await retryPolicy<ServerConnectionException>().ExecuteAsync(async (serverConnection) =>
{
args.TimeLine["Server Connection Created"] = DateTime.Now;
args.TimeLine["Connection Ready"] = DateTime.Now;

// if upgrading to websocket then relay the request without reading the contents
if (request.UpgradeToWebSocket)
Expand Down Expand Up @@ -369,6 +369,8 @@ await args.WebSession.SendRequest(Enable100ContinueBehaviour, args.IsTransparent
}
}

args.TimeLine["Request Sent"] = DateTime.Now;

// If not expectation failed response was returned by server then parse response
if (!request.ExpectationFailed)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Titanium.Web.Proxy/TransparentClientHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConn
//it could cause floating server connections when client exits
prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port,
httpVersion: null, isHttps: true, applicationProtocols: null, isConnect: false,
proxyServer: this, upStreamEndPoint: UpStreamEndPoint, externalProxy: UpStreamHttpsProxy,
proxyServer: this, session: null, upStreamEndPoint: UpStreamEndPoint, externalProxy: UpStreamHttpsProxy,
noCache: false, cancellationToken: CancellationToken.None);
}

Expand Down Expand Up @@ -102,7 +102,7 @@ private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConn
{
var connection = await tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port,
httpVersion: null, isHttps: false, applicationProtocols: null,
isConnect: true, proxyServer: this, upStreamEndPoint: UpStreamEndPoint,
isConnect: true, proxyServer: this, session:null, upStreamEndPoint: UpStreamEndPoint,
externalProxy: UpStreamHttpsProxy, noCache: true, cancellationToken: cancellationToken);

try
Expand Down