Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
T
Titanium-Web-Proxy
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Titanium-Web-Proxy
Commits
142cabcb
Commit
142cabcb
authored
Apr 29, 2017
by
Jehonathan Thomas
Committed by
GitHub
Apr 29, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #194 from justcoding121/develop
Mege Beta with develop
parents
43895859
a229da42
Show whitespace changes
Inline
Side-by-side
Showing
54 changed files
with
1470 additions
and
875 deletions
+1470
-875
ProxyTestController.cs
.../Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
+6
-6
Titanium.Web.Proxy.Examples.Basic.csproj
...y.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.csproj
+2
-1
README.md
README.md
+141
-142
AssemblyInfo.cs
...ium.Web.Proxy.IntegrationTests/Properties/AssemblyInfo.cs
+36
-0
SslTests.cs
Tests/Titanium.Web.Proxy.IntegrationTests/SslTests.cs
+130
-0
Titanium.Web.Proxy.IntegrationTests.csproj
...tegrationTests/Titanium.Web.Proxy.IntegrationTests.csproj
+91
-0
CertificateManagerTests.cs
...s/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs
+1
-1
Titanium.Web.Proxy.sln
Titanium.Web.Proxy.sln
+8
-1
CertificateHandler.cs
Titanium.Web.Proxy/CertificateHandler.cs
+14
-12
CompressionFactory.cs
Titanium.Web.Proxy/Compression/CompressionFactory.cs
+0
-2
GZipCompression.cs
Titanium.Web.Proxy/Compression/GZipCompression.cs
+2
-2
ZlibCompression.cs
Titanium.Web.Proxy/Compression/ZlibCompression.cs
+0
-25
DecompressionFactory.cs
Titanium.Web.Proxy/Decompression/DecompressionFactory.cs
+0
-2
DeflateDecompression.cs
Titanium.Web.Proxy/Decompression/DeflateDecompression.cs
+4
-6
GZipDecompression.cs
Titanium.Web.Proxy/Decompression/GZipDecompression.cs
+0
-1
ZlibDecompression.cs
Titanium.Web.Proxy/Decompression/ZlibDecompression.cs
+0
-33
CertificateSelectionEventArgs.cs
...Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs
+18
-1
CertificateValidationEventArgs.cs
...eb.Proxy/EventArguments/CertificateValidationEventArgs.cs
+13
-6
SessionEventArgs.cs
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+21
-6
BodyNotFoundException.cs
Titanium.Web.Proxy/Exceptions/BodyNotFoundException.cs
+5
-2
ByteArrayExtensions.cs
Titanium.Web.Proxy/Extensions/ByteArrayExtensions.cs
+4
-1
TcpExtensions.cs
Titanium.Web.Proxy/Extensions/TcpExtensions.cs
+3
-10
CustomBinaryReader.cs
Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs
+6
-6
Firefox.cs
Titanium.Web.Proxy/Helpers/Firefox.cs
+28
-28
Network.cs
Titanium.Web.Proxy/Helpers/Network.cs
+6
-24
RunTime.cs
Titanium.Web.Proxy/Helpers/RunTime.cs
+19
-0
SystemProxy.cs
Titanium.Web.Proxy/Helpers/SystemProxy.cs
+1
-4
Tcp.cs
Titanium.Web.Proxy/Helpers/Tcp.cs
+1
-0
HttpWebClient.cs
Titanium.Web.Proxy/Http/HttpWebClient.cs
+42
-36
Request.cs
Titanium.Web.Proxy/Http/Request.cs
+10
-22
Response.cs
Titanium.Web.Proxy/Http/Response.cs
+14
-9
GenericResponse.cs
Titanium.Web.Proxy/Http/Responses/GenericResponse.cs
+10
-1
OkResponse.cs
Titanium.Web.Proxy/Http/Responses/OkResponse.cs
+3
-0
RedirectResponse.cs
Titanium.Web.Proxy/Http/Responses/RedirectResponse.cs
+3
-0
EndPoint.cs
Titanium.Web.Proxy/Models/EndPoint.cs
+47
-11
ExternalProxy.cs
Titanium.Web.Proxy/Models/ExternalProxy.cs
+16
-1
HttpHeader.cs
Titanium.Web.Proxy/Models/HttpHeader.cs
+13
-1
BCCertificateMaker.cs
Titanium.Web.Proxy/Network/Certificate/BCCertificateMaker.cs
+174
-0
ICertificateMaker.cs
Titanium.Web.Proxy/Network/Certificate/ICertificateMaker.cs
+12
-0
WinCertificateMaker.cs
...nium.Web.Proxy/Network/Certificate/WinCertificateMaker.cs
+228
-0
CertificateMaker.cs
Titanium.Web.Proxy/Network/CertificateMaker.cs
+0
-234
CertificateManager.cs
Titanium.Web.Proxy/Network/CertificateManager.cs
+97
-67
TcpConnection.cs
Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs
+3
-0
TcpConnectionFactory.cs
Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
+6
-8
TcpRow.cs
Titanium.Web.Proxy/Network/Tcp/TcpRow.cs
+3
-3
TcpTable.cs
Titanium.Web.Proxy/Network/Tcp/TcpTable.cs
+3
-1
ProxyAuthorizationHandler.cs
Titanium.Web.Proxy/ProxyAuthorizationHandler.cs
+64
-40
ProxyServer.cs
Titanium.Web.Proxy/ProxyServer.cs
+94
-27
RequestHandler.cs
Titanium.Web.Proxy/RequestHandler.cs
+45
-66
ResponseHandler.cs
Titanium.Web.Proxy/ResponseHandler.cs
+12
-17
ProxyConstants.cs
Titanium.Web.Proxy/Shared/ProxyConstants.cs
+1
-2
Titanium.Web.Proxy.csproj
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
+8
-5
Titanium.Web.Proxy.nuspec
Titanium.Web.Proxy/Titanium.Web.Proxy.nuspec
+1
-1
packages.config
Titanium.Web.Proxy/packages.config
+1
-1
No files found.
Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
View file @
142cabcb
...
...
@@ -31,23 +31,23 @@ namespace Titanium.Web.Proxy.Examples.Basic
var
explicitEndPoint
=
new
ExplicitProxyEndPoint
(
IPAddress
.
Any
,
8000
,
true
)
{
//Exclude Https addresses you don't want to proxy
//Useful
l
for clients that use certificate pinning
//Useful for clients that use certificate pinning
//for example dropbox.com
// ExcludedHttpsHostNameRegex = new List<string>() { "google.com", "dropbox.com" }
//Use self-issued generic certificate on all https requests
//Optimizes performance by not creating a certificate for each https-enabled domain
//Useful
l when certificate trust is not requie
red by proxy clients
//Useful
when certificate trust is not requi
red by proxy clients
// GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password")
};
//An explicit endpoint is where the client knows about the exist
a
nce of a proxy
//An explicit endpoint is where the client knows about the exist
e
nce of a proxy
//So client sends request in a proxy friendly manner
proxyServer
.
AddEndPoint
(
explicitEndPoint
);
proxyServer
.
Start
();
//Transparent endpoint is useful
l for reverse proxying (client is not aware of the exista
nce of proxy)
//Transparent endpoint is useful
for reverse proxying (client is not aware of the existe
nce of proxy)
//A transparent endpoint usually requires a network router port forwarding HTTP(S) packets to this endpoint
//Currently do not support Server Name Indication (It is not currently supported by SslStream class)
//That means that the transparent endpoint will always provide the same Generic Certificate to all HTTPS requests
...
...
@@ -81,7 +81,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
proxyServer
.
Stop
();
}
//intecept & cancel
,
redirect or update requests
//intecept & cancel redirect or update requests
public
async
Task
OnRequest
(
object
sender
,
SessionEventArgs
e
)
{
Console
.
WriteLine
(
e
.
WebSession
.
Request
.
Url
);
...
...
Examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.csproj
View file @
142cabcb
...
...
@@ -25,7 +25,7 @@
<Prefer32Bit>
false
</Prefer32Bit>
</PropertyGroup>
<PropertyGroup
Condition=
" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "
>
<DebugType>
pdbonly
</DebugType>
<DebugType>
none
</DebugType>
<Optimize>
true
</Optimize>
<OutputPath>
bin\Release\
</OutputPath>
<DefineConstants>
TRACE;NET45
</DefineConstants>
...
...
@@ -33,6 +33,7 @@
<WarningLevel>
4
</WarningLevel>
<TargetFrameworkVersion>
v4.5
</TargetFrameworkVersion>
<Prefer32Bit>
false
</Prefer32Bit>
<DebugSymbols>
false
</DebugSymbols>
</PropertyGroup>
<PropertyGroup>
<StartupObject
/>
...
...
README.md
View file @
142cabcb
...
...
@@ -38,76 +38,76 @@ For stable releases on [stable branch](https://github.com/justcoding121/Titanium
Setup HTTP proxy:
```
csharp
var
proxyServer
=
new
ProxyServer
();
//locally trust root certificate used by this proxy
proxyServer
.
TrustRootCertificate
=
true
;
proxyServer
.
BeforeRequest
+=
OnRequest
;
proxyServer
.
BeforeResponse
+=
OnResponse
;
proxyServer
.
ServerCertificateValidationCallback
+=
OnCertificateValidation
;
proxyServer
.
ClientCertificateSelectionCallback
+=
OnCertificateSelection
;
var
explicitEndPoint
=
new
ExplicitProxyEndPoint
(
IPAddress
.
Any
,
8000
,
true
)
{
//Exclude Https addresses you don't want to proxy
//Usefull for clients that use certificate pinning
//for example dropbox.com
// ExcludedHttpsHostNameRegex = new List<string>() { "google.com", "dropbox.com" }
//Use self-issued generic certificate on all https requests
//Optimizes performance by not creating a certificate for each https-enabled domain
//Usefull when certificate trust is not requiered by proxy clients
// GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password")
};
//An explicit endpoint is where the client knows about the existance of a proxy
//So client sends request in a proxy friendly manner
proxyServer
.
AddEndPoint
(
explicitEndPoint
);
proxyServer
.
Start
();
//Transparent endpoint is usefull for reverse proxying (client is not aware of the existance of proxy)
//A transparent endpoint usually requires a network router port forwarding HTTP(S) packets to this endpoint
//Currently do not support Server Name Indication (It is not currently supported by SslStream class)
//That means that the transparent endpoint will always provide the same Generic Certificate to all HTTPS requests
//In this example only google.com will work for HTTPS requests
//Other sites will receive a certificate mismatch warning on browser
var
transparentEndPoint
=
new
TransparentProxyEndPoint
(
IPAddress
.
Any
,
8001
,
true
)
{
GenericCertificateName
=
"google.com"
};
proxyServer
.
AddEndPoint
(
transparentEndPoint
);
//proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
//proxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
foreach
(
var
endPoint
in
proxyServer
.
ProxyEndPoints
)
Console
.
WriteLine
(
"Listening on '{0}' endpoint at Ip {1} and port: {2} "
,
var
proxyServer
=
new
ProxyServer
();
//locally trust root certificate used by this proxy
proxyServer
.
TrustRootCertificate
=
true
;
proxyServer
.
BeforeRequest
+=
OnRequest
;
proxyServer
.
BeforeResponse
+=
OnResponse
;
proxyServer
.
ServerCertificateValidationCallback
+=
OnCertificateValidation
;
proxyServer
.
ClientCertificateSelectionCallback
+=
OnCertificateSelection
;
var
explicitEndPoint
=
new
ExplicitProxyEndPoint
(
IPAddress
.
Any
,
8000
,
true
)
{
//Exclude Https addresses you don't want to proxy
//Usefull for clients that use certificate pinning
//for example dropbox.com
// ExcludedHttpsHostNameRegex = new List<string>() { "google.com", "dropbox.com" }
//Use self-issued generic certificate on all https requests
//Optimizes performance by not creating a certificate for each https-enabled domain
//Usefull when certificate trust is not requiered by proxy clients
// GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password")
};
//An explicit endpoint is where the client knows about the existance of a proxy
//So client sends request in a proxy friendly manner
proxyServer
.
AddEndPoint
(
explicitEndPoint
);
proxyServer
.
Start
();
//Warning! Transparent endpoint is not tested end to end
//Transparent endpoint is usefull for reverse proxying (client is not aware of the existance of proxy)
//A transparent endpoint usually requires a network router port forwarding HTTP(S) packets to this endpoint
//Currently do not support Server Name Indication (It is not currently supported by SslStream class)
//That means that the transparent endpoint will always provide the same Generic Certificate to all HTTPS requests
//In this example only google.com will work for HTTPS requests
//Other sites will receive a certificate mismatch warning on browser
var
transparentEndPoint
=
new
TransparentProxyEndPoint
(
IPAddress
.
Any
,
8001
,
true
)
{
GenericCertificateName
=
"google.com"
};
proxyServer
.
AddEndPoint
(
transparentEndPoint
);
//proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
//proxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
foreach
(
var
endPoint
in
proxyServer
.
ProxyEndPoints
)
Console
.
WriteLine
(
"Listening on '{0}' endpoint at Ip {1} and port: {2} "
,
endPoint
.
GetType
().
Name
,
endPoint
.
IpAddress
,
endPoint
.
Port
);
//Only explicit proxies can be set as system proxy!
proxyServer
.
SetAsSystemHttpProxy
(
explicitEndPoint
);
proxyServer
.
SetAsSystemHttpsProxy
(
explicitEndPoint
);
//Only explicit proxies can be set as system proxy!
proxyServer
.
SetAsSystemHttpProxy
(
explicitEndPoint
);
proxyServer
.
SetAsSystemHttpsProxy
(
explicitEndPoint
);
//wait here (You can use something else as a wait function, I am using this as a demo)
Console
.
Read
();
//wait here (You can use something else as a wait function, I am using this as a demo)
Console
.
Read
();
//Unsubscribe & Quit
proxyServer
.
BeforeRequest
-=
OnRequest
;
proxyServer
.
BeforeResponse
-=
OnResponse
;
proxyServer
.
ServerCertificateValidationCallback
-=
OnCertificateValidation
;
proxyServer
.
ClientCertificateSelectionCallback
-=
OnCertificateSelection
;
//Unsubscribe & Quit
proxyServer
.
BeforeRequest
-=
OnRequest
;
proxyServer
.
BeforeResponse
-=
OnResponse
;
proxyServer
.
ServerCertificateValidationCallback
-=
OnCertificateValidation
;
proxyServer
.
ClientCertificateSelectionCallback
-=
OnCertificateSelection
;
proxyServer
.
Stop
();
proxyServer
.
Stop
();
```
Sample request and response event handlers
```
csharp
public
async
Task
OnRequest
(
object
sender
,
SessionEventArgs
e
)
{
public
async
Task
OnRequest
(
object
sender
,
SessionEventArgs
e
)
{
Console
.
WriteLine
(
e
.
WebSession
.
Request
.
Url
);
////read request headers
...
...
@@ -143,11 +143,11 @@ Sample request and response event handlers
{
await
e
.
Redirect
(
"https://www.paypal.com"
);
}
}
}
//Modify response
public
async
Task
OnResponse
(
object
sender
,
SessionEventArgs
e
)
{
//Modify response
public
async
Task
OnResponse
(
object
sender
,
SessionEventArgs
e
)
{
//read response headers
var
responseHeaders
=
e
.
WebSession
.
Response
.
ResponseHeaders
;
...
...
@@ -166,25 +166,24 @@ Sample request and response event handlers
}
}
}
}
}
/// Allows overriding default certificate validation logic
public
Task
OnCertificateValidation
(
object
sender
,
CertificateValidationEventArgs
e
)
{
/// Allows overriding default certificate validation logic
public
Task
OnCertificateValidation
(
object
sender
,
CertificateValidationEventArgs
e
)
{
//set IsValid to true/false based on Certificate Errors
if
(
e
.
SslPolicyErrors
==
System
.
Net
.
Security
.
SslPolicyErrors
.
None
)
e
.
IsValid
=
true
;
return
Task
.
FromResult
(
0
);
}
}
/// Allows overriding default client certificate selection logic during mutual authentication
public
Task
OnCertificateSelection
(
object
sender
,
CertificateSelectionEventArgs
e
)
{
/// Allows overriding default client certificate selection logic during mutual authentication
public
Task
OnCertificateSelection
(
object
sender
,
CertificateSelectionEventArgs
e
)
{
//set e.clientCertificate to override
return
Task
.
FromResult
(
0
);
}
}
```
Future roadmap
============
...
...
Tests/Titanium.Web.Proxy.IntegrationTests/Properties/AssemblyInfo.cs
0 → 100644
View file @
142cabcb
using
System.Reflection
;
using
System.Runtime.CompilerServices
;
using
System.Runtime.InteropServices
;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Titanium.Web.Proxy.IntegrationTests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Titanium.Web.Proxy.IntegrationTests")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("32231301-b0fb-4f9e-98df-b3e8a88f4c16")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
Tests/Titanium.Web.Proxy.IntegrationTests/SslTests.cs
0 → 100644
View file @
142cabcb
using
System
;
using
Microsoft.VisualStudio.TestTools.UnitTesting
;
using
System.Net
;
using
System.Collections.Generic
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Models
;
using
System.Net.Http
;
using
System.Diagnostics
;
namespace
Titanium.Web.Proxy.IntegrationTests
{
[
TestClass
]
public
class
SslTests
{
[
TestMethod
]
public
void
TestSsl
()
{
//expand this to stress test to find
//why in long run proxy becomes unresponsive as per issue #184
var
testUrl
=
"https://google.com"
;
int
proxyPort
=
8086
;
var
proxy
=
new
ProxyTestController
();
proxy
.
StartProxy
(
proxyPort
);
using
(
var
client
=
CreateHttpClient
(
testUrl
,
proxyPort
))
{
var
response
=
client
.
GetAsync
(
new
Uri
(
testUrl
)).
Result
;
}
}
private
HttpClient
CreateHttpClient
(
string
url
,
int
localProxyPort
)
{
var
handler
=
new
HttpClientHandler
{
Proxy
=
new
WebProxy
(
string
.
Format
(
"http://localhost:{0}"
,
localProxyPort
),
false
),
UseProxy
=
true
,
};
var
client
=
new
HttpClient
(
handler
);
return
client
;
}
}
public
class
ProxyTestController
{
private
ProxyServer
proxyServer
;
public
ProxyTestController
()
{
proxyServer
=
new
ProxyServer
();
proxyServer
.
TrustRootCertificate
=
true
;
}
public
void
StartProxy
(
int
proxyPort
)
{
proxyServer
.
BeforeRequest
+=
OnRequest
;
proxyServer
.
BeforeResponse
+=
OnResponse
;
proxyServer
.
ServerCertificateValidationCallback
+=
OnCertificateValidation
;
proxyServer
.
ClientCertificateSelectionCallback
+=
OnCertificateSelection
;
var
explicitEndPoint
=
new
ExplicitProxyEndPoint
(
IPAddress
.
Any
,
proxyPort
,
true
);
//An explicit endpoint is where the client knows about the existance of a proxy
//So client sends request in a proxy friendly manner
proxyServer
.
AddEndPoint
(
explicitEndPoint
);
proxyServer
.
Start
();
foreach
(
var
endPoint
in
proxyServer
.
ProxyEndPoints
)
Console
.
WriteLine
(
"Listening on '{0}' endpoint at Ip {1} and port: {2} "
,
endPoint
.
GetType
().
Name
,
endPoint
.
IpAddress
,
endPoint
.
Port
);
}
public
void
Stop
()
{
proxyServer
.
BeforeRequest
-=
OnRequest
;
proxyServer
.
BeforeResponse
-=
OnResponse
;
proxyServer
.
ServerCertificateValidationCallback
-=
OnCertificateValidation
;
proxyServer
.
ClientCertificateSelectionCallback
-=
OnCertificateSelection
;
proxyServer
.
Stop
();
}
//intecept & cancel, redirect or update requests
public
async
Task
OnRequest
(
object
sender
,
SessionEventArgs
e
)
{
Debug
.
WriteLine
(
e
.
WebSession
.
Request
.
Url
);
await
Task
.
FromResult
(
0
);
}
//Modify response
public
async
Task
OnResponse
(
object
sender
,
SessionEventArgs
e
)
{
await
Task
.
FromResult
(
0
);
}
/// <summary>
/// Allows overriding default certificate validation logic
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public
Task
OnCertificateValidation
(
object
sender
,
CertificateValidationEventArgs
e
)
{
//set IsValid to true/false based on Certificate Errors
if
(
e
.
SslPolicyErrors
==
System
.
Net
.
Security
.
SslPolicyErrors
.
None
)
{
e
.
IsValid
=
true
;
}
return
Task
.
FromResult
(
0
);
}
/// <summary>
/// Allows overriding default client certificate selection logic during mutual authentication
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public
Task
OnCertificateSelection
(
object
sender
,
CertificateSelectionEventArgs
e
)
{
//set e.clientCertificate to override
return
Task
.
FromResult
(
0
);
}
}
}
Tests/Titanium.Web.Proxy.IntegrationTests/Titanium.Web.Proxy.IntegrationTests.csproj
0 → 100644
View file @
142cabcb
<?xml version="1.0" encoding="utf-8"?>
<Project
ToolsVersion=
"14.0"
DefaultTargets=
"Build"
xmlns=
"http://schemas.microsoft.com/developer/msbuild/2003"
>
<PropertyGroup>
<Configuration
Condition=
" '$(Configuration)' == '' "
>
Debug
</Configuration>
<Platform
Condition=
" '$(Platform)' == '' "
>
AnyCPU
</Platform>
<ProjectGuid>
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}
</ProjectGuid>
<OutputType>
Library
</OutputType>
<AppDesignerFolder>
Properties
</AppDesignerFolder>
<RootNamespace>
Titanium.Web.Proxy.IntegrationTests
</RootNamespace>
<AssemblyName>
Titanium.Web.Proxy.IntegrationTests
</AssemblyName>
<TargetFrameworkVersion>
v4.5.2
</TargetFrameworkVersion>
<FileAlignment>
512
</FileAlignment>
<ProjectTypeGuids>
{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
</ProjectTypeGuids>
<VisualStudioVersion
Condition=
"'$(VisualStudioVersion)' == ''"
>
10.0
</VisualStudioVersion>
<VSToolsPath
Condition=
"'$(VSToolsPath)' == ''"
>
$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
</VSToolsPath>
<ReferencePath>
$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
</ReferencePath>
<IsCodedUITest>
False
</IsCodedUITest>
<TestProjectType>
UnitTest
</TestProjectType>
<TargetFrameworkProfile
/>
</PropertyGroup>
<PropertyGroup
Condition=
" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "
>
<DebugSymbols>
true
</DebugSymbols>
<DebugType>
full
</DebugType>
<Optimize>
false
</Optimize>
<OutputPath>
bin\Debug\
</OutputPath>
<DefineConstants>
DEBUG;TRACE
</DefineConstants>
<ErrorReport>
prompt
</ErrorReport>
<WarningLevel>
4
</WarningLevel>
</PropertyGroup>
<PropertyGroup
Condition=
" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "
>
<DebugType>
pdbonly
</DebugType>
<Optimize>
true
</Optimize>
<OutputPath>
bin\Release\
</OutputPath>
<DefineConstants>
TRACE
</DefineConstants>
<ErrorReport>
prompt
</ErrorReport>
<WarningLevel>
4
</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference
Include=
"System"
/>
<Reference
Include=
"System.Net.Http"
/>
</ItemGroup>
<Choose>
<When
Condition=
"('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"
>
<ItemGroup>
<Reference
Include=
"Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
/>
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference
Include=
"Microsoft.VisualStudio.QualityTools.UnitTestFramework"
/>
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup>
<Compile
Include=
"SslTests.cs"
/>
<Compile
Include=
"Properties\AssemblyInfo.cs"
/>
</ItemGroup>
<ItemGroup>
<ProjectReference
Include=
"..\..\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj"
>
<Project>
{8d73a1be-868c-42d2-9ece-f32cc1a02906}
</Project>
<Name>
Titanium.Web.Proxy
</Name>
</ProjectReference>
</ItemGroup>
<Choose>
<When
Condition=
"'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'"
>
<ItemGroup>
<Reference
Include=
"Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
>
<Private>
False
</Private>
</Reference>
<Reference
Include=
"Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
>
<Private>
False
</Private>
</Reference>
<Reference
Include=
"Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
>
<Private>
False
</Private>
</Reference>
<Reference
Include=
"Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
>
<Private>
False
</Private>
</Reference>
</ItemGroup>
</When>
</Choose>
<Import
Project=
"$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets"
Condition=
"Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')"
/>
<Import
Project=
"$(MSBuildToolsPath)\Microsoft.CSharp.targets"
/>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
\ No newline at end of file
Tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs
View file @
142cabcb
...
...
@@ -20,7 +20,7 @@ namespace Titanium.Web.Proxy.UnitTests
{
var
tasks
=
new
List
<
Task
>();
var
mgr
=
new
CertificateManager
(
"Titanium"
,
"Titanium Root Certificate Authority"
,
var
mgr
=
new
CertificateManager
(
CertificateEngine
.
DefaultWindows
,
"Titanium"
,
"Titanium Root Certificate Authority"
,
new
Lazy
<
Action
<
Exception
>>(()
=>
(
e
=>
{
})).
Value
);
mgr
.
ClearIdleCertificates
(
1
);
...
...
Titanium.Web.Proxy.sln
View file @
142cabcb
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25
123.0
VisualStudioVersion = 14.0.25
420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{B6DBABDC-C985-4872-9C38-B4E5079CBC4B}"
EndProject
...
...
@@ -33,6 +33,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BC1E0789
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.UnitTests", "Tests\Titanium.Web.Proxy.UnitTests\Titanium.Web.Proxy.UnitTests.csproj", "{B517E3D0-D03B-436F-AB03-34BA0D5321AF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.IntegrationTests", "Tests\Titanium.Web.Proxy.IntegrationTests\Titanium.Web.Proxy.IntegrationTests.csproj", "{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
...
...
@@ -51,6 +53,10 @@ Global
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Release|Any CPU.Build.0 = Release|Any CPU
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
...
...
@@ -58,6 +64,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{F3B7E553-1904-4E80-BDC7-212342B5C952} = {B6DBABDC-C985-4872-9C38-B4E5079CBC4B}
{B517E3D0-D03B-436F-AB03-34BA0D5321AF} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64}
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EnterpriseLibraryConfigurationToolBinariesPath = .1.505.2\lib\NET35
...
...
Titanium.Web.Proxy/CertificateHandler.cs
View file @
142cabcb
using
System
;
using
System.Linq
;
using
System.Net.Security
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Threading.Tasks
;
...
...
@@ -26,11 +25,13 @@ namespace Titanium.Web.Proxy
//if user callback is registered then do it
if
(
ServerCertificateValidationCallback
!=
null
)
{
var
args
=
new
CertificateValidationEventArgs
();
var
args
=
new
CertificateValidationEventArgs
{
Certificate
=
certificate
,
Chain
=
chain
,
SslPolicyErrors
=
sslPolicyErrors
};
args
.
Certificate
=
certificate
;
args
.
Chain
=
chain
;
args
.
SslPolicyErrors
=
sslPolicyErrors
;
Delegate
[]
invocationList
=
ServerCertificateValidationCallback
.
GetInvocationList
();
...
...
@@ -73,7 +74,6 @@ namespace Titanium.Web.Proxy
string
[]
acceptableIssuers
)
{
X509Certificate
clientCertificate
=
null
;
var
customSslStream
=
sender
as
SslStream
;
if
(
acceptableIssuers
!=
null
&&
acceptableIssuers
.
Length
>
0
&&
...
...
@@ -100,13 +100,15 @@ namespace Titanium.Web.Proxy
//If user call back is registered
if
(
ClientCertificateSelectionCallback
!=
null
)
{
var
args
=
new
CertificateSelectionEventArgs
();
var
args
=
new
CertificateSelectionEventArgs
{
TargetHost
=
targetHost
,
LocalCertificates
=
localCertificates
,
RemoteCertificate
=
remoteCertificate
,
AcceptableIssuers
=
acceptableIssuers
,
ClientCertificate
=
clientCertificate
};
args
.
TargetHost
=
targetHost
;
args
.
LocalCertificates
=
localCertificates
;
args
.
RemoteCertificate
=
remoteCertificate
;
args
.
AcceptableIssuers
=
acceptableIssuers
;
args
.
ClientCertificate
=
clientCertificate
;
Delegate
[]
invocationList
=
ClientCertificateSelectionCallback
.
GetInvocationList
();
Task
[]
handlerTasks
=
new
Task
[
invocationList
.
Length
];
...
...
Titanium.Web.Proxy/Compression/CompressionFactory.cs
View file @
142cabcb
...
...
@@ -13,8 +13,6 @@
return
new
GZipCompression
();
case
"deflate"
:
return
new
DeflateCompression
();
case
"zlib"
:
return
new
ZlibCompression
();
default
:
return
null
;
}
...
...
Titanium.Web.Proxy/Compression/GZipCompression.cs
View file @
142cabcb
using
Ionic.Zlib
;
using
System.IO
;
using
System.IO
;
using
System.IO
.Compression
;
using
System.Threading.Tasks
;
namespace
Titanium.Web.Proxy.Compression
...
...
Titanium.Web.Proxy/Compression/ZlibCompression.cs
deleted
100644 → 0
View file @
43895859
using
Ionic.Zlib
;
using
System.IO
;
using
System.Threading.Tasks
;
namespace
Titanium.Web.Proxy.Compression
{
/// <summary>
/// concrete implementation of zlib compression
/// </summary>
internal
class
ZlibCompression
:
ICompression
{
public
async
Task
<
byte
[
]>
Compress
(
byte
[]
responseBody
)
{
using
(
var
ms
=
new
MemoryStream
())
{
using
(
var
zip
=
new
ZlibStream
(
ms
,
CompressionMode
.
Compress
,
true
))
{
await
zip
.
WriteAsync
(
responseBody
,
0
,
responseBody
.
Length
);
}
return
ms
.
ToArray
();
}
}
}
}
Titanium.Web.Proxy/Decompression/DecompressionFactory.cs
View file @
142cabcb
...
...
@@ -13,8 +13,6 @@
return
new
GZipDecompression
();
case
"deflate"
:
return
new
DeflateDecompression
();
case
"zlib"
:
return
new
ZlibDecompression
();
default
:
return
new
DefaultDecompression
();
}
...
...
Titanium.Web.Proxy/Decompression/DeflateDecompression.cs
View file @
142cabcb
using
Ionic.Zlib
;
using
System.IO
;
using
System.IO
;
using
System.IO
.Compression
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Shared
;
namespace
Titanium.Web.Proxy.Decompression
{
...
...
@@ -12,8 +11,7 @@ namespace Titanium.Web.Proxy.Decompression
{
public
async
Task
<
byte
[
]>
Decompress
(
byte
[]
compressedArray
,
int
bufferSize
)
{
var
stream
=
new
MemoryStream
(
compressedArray
);
using
(
var
stream
=
new
MemoryStream
(
compressedArray
))
using
(
var
decompressor
=
new
DeflateStream
(
stream
,
CompressionMode
.
Decompress
))
{
var
buffer
=
new
byte
[
bufferSize
];
...
...
Titanium.Web.Proxy/Decompression/GZipDecompression.cs
View file @
142cabcb
using
System.IO
;
using
System.IO.Compression
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Shared
;
namespace
Titanium.Web.Proxy.Decompression
{
...
...
Titanium.Web.Proxy/Decompression/ZlibDecompression.cs
deleted
100644 → 0
View file @
43895859
using
Ionic.Zlib
;
using
System.IO
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Shared
;
namespace
Titanium.Web.Proxy.Decompression
{
/// <summary>
/// concrete implemetation of zlib de-compression
/// </summary>
internal
class
ZlibDecompression
:
IDecompression
{
public
async
Task
<
byte
[
]>
Decompress
(
byte
[]
compressedArray
,
int
bufferSize
)
{
var
memoryStream
=
new
MemoryStream
(
compressedArray
);
using
(
var
decompressor
=
new
ZlibStream
(
memoryStream
,
CompressionMode
.
Decompress
))
{
var
buffer
=
new
byte
[
bufferSize
];
using
(
var
output
=
new
MemoryStream
())
{
int
read
;
while
((
read
=
await
decompressor
.
ReadAsync
(
buffer
,
0
,
buffer
.
Length
))
>
0
)
{
await
output
.
WriteAsync
(
buffer
,
0
,
read
);
}
return
output
.
ToArray
();
}
}
}
}
}
Titanium.Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs
View file @
142cabcb
...
...
@@ -8,12 +8,29 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public
class
CertificateSelectionEventArgs
:
EventArgs
{
/// <summary>
/// Sender object.
/// </summary>
public
object
Sender
{
get
;
internal
set
;
}
/// <summary>
/// Target host.
/// </summary>
public
string
TargetHost
{
get
;
internal
set
;
}
/// <summary>
/// Local certificates.
/// </summary>
public
X509CertificateCollection
LocalCertificates
{
get
;
internal
set
;
}
/// <summary>
/// Remote certificate.
/// </summary>
public
X509Certificate
RemoteCertificate
{
get
;
internal
set
;
}
/// <summary>
/// Acceptable issuers.
/// </summary>
public
string
[]
AcceptableIssuers
{
get
;
internal
set
;
}
/// <summary>
/// Client Certificate.
/// </summary>
public
X509Certificate
ClientCertificate
{
get
;
set
;
}
}
...
...
Titanium.Web.Proxy/EventArguments/CertificateValidationEventArgs.cs
View file @
142cabcb
...
...
@@ -7,17 +7,24 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// An argument passed on to the user for validating the server certificate during SSL authentication
/// </summary>
public
class
CertificateValidationEventArgs
:
EventArgs
,
IDisposable
public
class
CertificateValidationEventArgs
:
EventArgs
{
/// <summary>
/// Certificate
/// </summary>
public
X509Certificate
Certificate
{
get
;
internal
set
;
}
/// <summary>
/// Certificate chain
/// </summary>
public
X509Chain
Chain
{
get
;
internal
set
;
}
/// <summary>
/// SSL policy errors.
/// </summary>
public
SslPolicyErrors
SslPolicyErrors
{
get
;
internal
set
;
}
/// <summary>
/// is a valid certificate?
/// </summary>
public
bool
IsValid
{
get
;
set
;
}
public
void
Dispose
()
{
}
}
}
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
View file @
142cabcb
...
...
@@ -44,7 +44,9 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public
Guid
Id
=>
WebSession
.
RequestId
;
//Should we send a rerequest
/// <summary>
/// Should we send a rerequest
/// </summary>
public
bool
ReRequest
{
get
;
...
...
@@ -56,7 +58,9 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public
bool
IsHttps
=>
WebSession
.
Request
.
RequestUri
.
Scheme
==
Uri
.
UriSchemeHttps
;
/// <summary>
/// Client End Point.
/// </summary>
public
IPEndPoint
ClientEndPoint
=>
(
IPEndPoint
)
ProxyClient
.
TcpClient
.
Client
.
RemoteEndPoint
;
/// <summary>
...
...
@@ -65,8 +69,14 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public
HttpWebClient
WebSession
{
get
;
set
;
}
/// <summary>
/// Are we using a custom upstream HTTP proxy?
/// </summary>
public
ExternalProxy
CustomUpStreamHttpProxyUsed
{
get
;
set
;
}
/// <summary>
/// Are we using a custom upstream HTTPS proxy?
/// </summary>
public
ExternalProxy
CustomUpStreamHttpsProxyUsed
{
get
;
set
;
}
/// <summary>
...
...
@@ -105,7 +115,7 @@ namespace Titanium.Web.Proxy.EventArguments
//For chunked request we need to read data as they arrive, until we reach a chunk end symbol
if
(
WebSession
.
Request
.
IsChunked
)
{
await
this
.
ProxyClient
.
ClientStreamReader
.
CopyBytesToStreamChunked
(
bufferSize
,
requestBodyStream
);
await
ProxyClient
.
ClientStreamReader
.
CopyBytesToStreamChunked
(
bufferSize
,
requestBodyStream
);
}
else
{
...
...
@@ -113,7 +123,7 @@ namespace Titanium.Web.Proxy.EventArguments
if
(
WebSession
.
Request
.
ContentLength
>
0
)
{
//If not chunked then its easy just read the amount of bytes mentioned in content length header of response
await
this
.
ProxyClient
.
ClientStreamReader
.
CopyBytesToStream
(
bufferSize
,
requestBodyStream
,
await
ProxyClient
.
ClientStreamReader
.
CopyBytesToStream
(
bufferSize
,
requestBodyStream
,
WebSession
.
Request
.
ContentLength
);
}
...
...
@@ -431,7 +441,7 @@ namespace Titanium.Web.Proxy.EventArguments
await
GenericResponse
(
html
,
null
,
status
);
}
///
<summary>
///
<summary>
/// Before request is made to server
/// Respond with the specified HTML string to client
/// and the specified status
...
...
@@ -485,12 +495,17 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession
.
Request
.
CancelRequest
=
true
;
}
/// <summary>
/// Redirect to URL.
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public
async
Task
Redirect
(
string
url
)
{
var
response
=
new
RedirectResponse
();
response
.
HttpVersion
=
WebSession
.
Request
.
HttpVersion
;
response
.
ResponseHeaders
.
Add
(
"Location"
,
new
Models
.
HttpHeader
(
"Location"
,
url
));
response
.
ResponseHeaders
.
Add
(
"Location"
,
new
HttpHeader
(
"Location"
,
url
));
response
.
ResponseBody
=
Encoding
.
ASCII
.
GetBytes
(
string
.
Empty
);
await
Respond
(
response
);
...
...
Titanium.Web.Proxy/Exceptions/BodyNotFoundException.cs
View file @
142cabcb
using
System
;
namespace
Titanium.Web.Proxy.Exceptions
{
/// <summary>
...
...
@@ -7,6 +6,10 @@ namespace Titanium.Web.Proxy.Exceptions
/// </summary>
public
class
BodyNotFoundException
:
ProxyException
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="message"></param>
public
BodyNotFoundException
(
string
message
)
:
base
(
message
)
{
...
...
Titanium.Web.Proxy/Extensions/ByteArrayExtensions.cs
View file @
142cabcb
...
...
@@ -2,6 +2,9 @@
namespace
Titanium.Web.Proxy.Extensions
{
/// <summary>
/// Extension methods for Byte Arrays.
/// </summary>
public
static
class
ByteArrayExtensions
{
/// <summary>
...
...
@@ -14,7 +17,7 @@ namespace Titanium.Web.Proxy.Extensions
/// <returns></returns>
public
static
T
[]
SubArray
<
T
>(
this
T
[]
data
,
int
index
,
int
length
)
{
T
[]
result
=
new
T
[
length
];
var
result
=
new
T
[
length
];
Array
.
Copy
(
data
,
index
,
result
,
0
,
length
);
return
result
;
}
...
...
Titanium.Web.Proxy/Extensions/TcpExtensions.cs
View file @
142cabcb
...
...
@@ -12,11 +12,11 @@ namespace Titanium.Web.Proxy.Extensions
internal
static
bool
IsConnected
(
this
Socket
client
)
{
// This is how you can determine whether a socket is still connected.
bool
blockingState
=
client
.
Blocking
;
var
blockingState
=
client
.
Blocking
;
try
{
byte
[]
tmp
=
new
byte
[
1
];
var
tmp
=
new
byte
[
1
];
client
.
Blocking
=
false
;
client
.
Send
(
tmp
,
0
,
0
);
...
...
@@ -25,14 +25,7 @@ namespace Titanium.Web.Proxy.Extensions
catch
(
SocketException
e
)
{
// 10035 == WSAEWOULDBLOCK
if
(
e
.
NativeErrorCode
.
Equals
(
10035
))
{
return
true
;
}
else
{
return
false
;
}
return
e
.
NativeErrorCode
.
Equals
(
10035
);
}
finally
{
...
...
Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs
View file @
142cabcb
...
...
@@ -4,7 +4,6 @@ using System.IO;
using
System.Text
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Shared
;
namespace
Titanium.Web.Proxy.Helpers
{
...
...
@@ -16,15 +15,15 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary>
internal
class
CustomBinaryReader
:
IDisposable
{
private
Stream
stream
;
private
Encoding
encoding
;
private
readonly
Stream
stream
;
private
readonly
Encoding
encoding
;
internal
CustomBinaryReader
(
Stream
stream
)
{
this
.
stream
=
stream
;
//default to UTF-8
this
.
encoding
=
Encoding
.
UTF8
;
encoding
=
Encoding
.
UTF8
;
}
internal
Stream
BaseStream
=>
stream
;
...
...
@@ -40,7 +39,7 @@ namespace Titanium.Web.Proxy.Helpers
var
lastChar
=
default
(
char
);
var
buffer
=
new
byte
[
1
];
while
((
await
this
.
stream
.
ReadAsync
(
buffer
,
0
,
1
))
>
0
)
while
((
await
stream
.
ReadAsync
(
buffer
,
0
,
1
))
>
0
)
{
//if new line
if
(
lastChar
==
'\r'
&&
buffer
[
0
]
==
'\n'
)
...
...
@@ -82,6 +81,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary>
/// Read the specified number of raw bytes from the base stream
/// </summary>
/// <param name="bufferSize"></param>
/// <param name="totalBytesToRead"></param>
/// <returns></returns>
internal
async
Task
<
byte
[
]>
ReadBytesAsync
(
int
bufferSize
,
long
totalBytesToRead
)
...
...
@@ -98,7 +98,7 @@ namespace Titanium.Web.Proxy.Helpers
using
(
var
outStream
=
new
MemoryStream
())
{
while
((
bytesRead
+=
await
this
.
stream
.
ReadAsync
(
buffer
,
0
,
bytesToRead
))
>
0
)
while
((
bytesRead
+=
await
stream
.
ReadAsync
(
buffer
,
0
,
bytesToRead
))
>
0
)
{
await
outStream
.
WriteAsync
(
buffer
,
0
,
bytesRead
);
totalBytesRead
+=
bytesRead
;
...
...
Titanium.Web.Proxy/Helpers/Firefox.cs
View file @
142cabcb
...
...
@@ -8,6 +8,9 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary>
public
class
FireFoxProxySettingsManager
{
/// <summary>
/// Add Firefox settings.
/// </summary>
public
void
AddFirefox
()
{
try
...
...
@@ -16,28 +19,27 @@ namespace Titanium.Web.Proxy.Helpers
new
DirectoryInfo
(
Environment
.
GetFolderPath
(
Environment
.
SpecialFolder
.
ApplicationData
)
+
"\\Mozilla\\Firefox\\Profiles\\"
).
GetDirectories
(
"*.default"
);
var
myFfPrefFile
=
myProfileDirectory
[
0
].
FullName
+
"\\prefs.js"
;
if
(
File
.
Exists
(
myFfPrefFile
))
{
if
(!
File
.
Exists
(
myFfPrefFile
))
return
;
// We have a pref file so let''s make sure it has the proxy setting
var
myReader
=
new
StreamReader
(
myFfPrefFile
);
var
myPrefContents
=
myReader
.
ReadToEnd
();
myReader
.
Close
();
if
(
myPrefContents
.
Contains
(
"user_pref(\"network.proxy.type\", 0);"
))
{
if
(!
myPrefContents
.
Contains
(
"user_pref(\"network.proxy.type\", 0);"
))
return
;
// Add the proxy enable line and write it back to the file
myPrefContents
=
myPrefContents
.
Replace
(
"user_pref(\"network.proxy.type\", 0);"
,
""
);
File
.
Delete
(
myFfPrefFile
);
File
.
WriteAllText
(
myFfPrefFile
,
myPrefContents
);
}
}
}
catch
(
Exception
)
{
// Only exception should be a read/write error because the user opened up FireFox so they can be ignored.
}
}
/// <summary>
/// Remove firefox settings.
/// </summary>
public
void
RemoveFirefox
()
{
try
...
...
@@ -46,8 +48,7 @@ namespace Titanium.Web.Proxy.Helpers
new
DirectoryInfo
(
Environment
.
GetFolderPath
(
Environment
.
SpecialFolder
.
ApplicationData
)
+
"\\Mozilla\\Firefox\\Profiles\\"
).
GetDirectories
(
"*.default"
);
var
myFfPrefFile
=
myProfileDirectory
[
0
].
FullName
+
"\\prefs.js"
;
if
(
File
.
Exists
(
myFfPrefFile
))
{
if
(!
File
.
Exists
(
myFfPrefFile
))
return
;
// We have a pref file so let''s make sure it has the proxy setting
var
myReader
=
new
StreamReader
(
myFfPrefFile
);
var
myPrefContents
=
myReader
.
ReadToEnd
();
...
...
@@ -61,7 +62,6 @@ namespace Titanium.Web.Proxy.Helpers
File
.
WriteAllText
(
myFfPrefFile
,
myPrefContents
);
}
}
}
catch
(
Exception
)
{
// Only exception should be a read/write error because the user opened up FireFox so they can be ignored.
...
...
Titanium.Web.Proxy/Helpers/Network.cs
View file @
142cabcb
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Linq
;
using
System.Net
;
using
System.Text
;
using
System.Threading.Tasks
;
namespace
Titanium.Web.Proxy.Helpers
{
...
...
@@ -33,28 +29,14 @@ namespace Titanium.Web.Proxy.Helpers
/// Adapated from below link
/// http://stackoverflow.com/questions/11834091/how-to-check-if-localhost
/// </summary>
/// <param name="address></param>
/// <param name="address
"
></param>
/// <returns></returns>
internal
static
bool
IsLocalIpAddress
(
IPAddress
address
)
{
try
{
// get local IP addresses
IPAddress
[]
localIPs
=
Dns
.
GetHostAddresses
(
Dns
.
GetHostName
());
var
localIPs
=
Dns
.
GetHostAddresses
(
Dns
.
GetHostName
());
// test if any host IP equals to any local IP or to localhost
// is localhost
if
(
IPAddress
.
IsLoopback
(
address
))
return
true
;
// is local address
foreach
(
IPAddress
localIP
in
localIPs
)
{
if
(
address
.
Equals
(
localIP
))
return
true
;
}
}
catch
{
}
return
false
;
return
IPAddress
.
IsLoopback
(
address
)
||
localIPs
.
Contains
(
address
);
}
}
}
Titanium.Web.Proxy/Helpers/RunTime.cs
0 → 100644
View file @
142cabcb
using
System
;
namespace
Titanium.Web.Proxy.Helpers
{
/// <summary>
/// Run time helpers
/// </summary>
internal
class
RunTime
{
/// <summary>
/// Checks if current run time is Mono
/// </summary>
/// <returns></returns>
internal
static
bool
IsRunningOnMono
()
{
return
Type
.
GetType
(
"Mono.Runtime"
)
!=
null
;
}
}
}
Titanium.Web.Proxy/Helpers/SystemProxy.cs
View file @
142cabcb
...
...
@@ -4,11 +4,8 @@ using Microsoft.Win32;
using
System.Text.RegularExpressions
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Net.Sockets
;
/// <summary>
/// Helper classes for setting system proxy settings
/// </summary>
// Helper classes for setting system proxy settings
namespace
Titanium.Web.Proxy.Helpers
{
internal
enum
ProxyProtocolType
...
...
Titanium.Web.Proxy/Helpers/Tcp.cs
View file @
142cabcb
...
...
@@ -140,6 +140,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="localCertificateSelectionCallback"></param>
/// <param name="clientStream"></param>
/// <param name="tcpConnectionFactory"></param>
/// <param name="upStreamEndPoint"></param>
/// <returns></returns>
internal
static
async
Task
SendRaw
(
int
bufferSize
,
int
connectionTimeOutSeconds
,
string
remoteHostName
,
int
remotePort
,
string
httpCmd
,
Version
httpVersion
,
Dictionary
<
string
,
HttpHeader
>
requestHeaders
,
...
...
Titanium.Web.Proxy/Http/HttpWebClient.cs
View file @
142cabcb
using
System
;
using
System.Collections.Generic
;
using
System.IO
;
using
System.Text
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Network
;
using
Titanium.Web.Proxy.Network.Tcp
;
using
Titanium.Web.Proxy.Shared
;
...
...
@@ -15,17 +13,26 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public
class
HttpWebClient
{
/// <summary>
/// Connection to server
/// </summary>
internal
TcpConnection
ServerConnection
{
get
;
set
;
}
public
Guid
RequestId
{
get
;
private
set
;
}
/// <summary>
/// Request ID.
/// </summary>
public
Guid
RequestId
{
get
;
}
/// <summary>
/// Headers passed with Connect.
/// </summary>
public
List
<
HttpHeader
>
ConnectHeaders
{
get
;
set
;
}
/// <summary>
/// Web Request.
/// </summary>
public
Request
Request
{
get
;
set
;
}
/// <summary>
/// Web Response.
/// </summary>
public
Response
Response
{
get
;
set
;
}
/// <summary>
...
...
@@ -37,15 +44,14 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Is Https?
/// </summary>
public
bool
IsHttps
=>
this
.
Request
.
RequestUri
.
Scheme
==
Uri
.
UriSchemeHttps
;
public
bool
IsHttps
=>
Request
.
RequestUri
.
Scheme
==
Uri
.
UriSchemeHttps
;
internal
HttpWebClient
()
{
this
.
RequestId
=
Guid
.
NewGuid
();
this
.
Request
=
new
Request
();
this
.
Response
=
new
Response
();
RequestId
=
Guid
.
NewGuid
();
Request
=
new
Request
();
Response
=
new
Response
();
}
/// <summary>
...
...
@@ -65,18 +71,18 @@ namespace Titanium.Web.Proxy.Http
/// <returns></returns>
internal
async
Task
SendRequest
(
bool
enable100ContinueBehaviour
)
{
Stream
stream
=
ServerConnection
.
Stream
;
var
stream
=
ServerConnection
.
Stream
;
StringBuilde
r
requestLines
=
new
StringBuilder
();
va
r
requestLines
=
new
StringBuilder
();
//prepare the request & headers
if
((
ServerConnection
.
UpStreamHttpProxy
!=
null
&&
ServerConnection
.
IsHttps
==
false
)
||
(
ServerConnection
.
UpStreamHttpsProxy
!=
null
&&
ServerConnection
.
IsHttps
==
true
))
if
((
ServerConnection
.
UpStreamHttpProxy
!=
null
&&
ServerConnection
.
IsHttps
==
false
)
||
(
ServerConnection
.
UpStreamHttpsProxy
!=
null
&&
ServerConnection
.
IsHttps
))
{
requestLines
.
AppendLine
(
string
.
Join
(
" "
,
this
.
Request
.
Method
,
this
.
Request
.
RequestUri
.
AbsoluteUri
,
$"HTTP/
{
this
.
Request
.
HttpVersion
.
Major
}
.
{
this
.
Request
.
HttpVersion
.
Minor
}
"
));
requestLines
.
AppendLine
(
string
.
Join
(
" "
,
Request
.
Method
,
Request
.
RequestUri
.
AbsoluteUri
,
$"HTTP/
{
Request
.
HttpVersion
.
Major
}
.
{
Request
.
HttpVersion
.
Minor
}
"
));
}
else
{
requestLines
.
AppendLine
(
string
.
Join
(
" "
,
this
.
Request
.
Method
,
this
.
Request
.
RequestUri
.
PathAndQuery
,
$"HTTP/
{
this
.
Request
.
HttpVersion
.
Major
}
.
{
this
.
Request
.
HttpVersion
.
Minor
}
"
));
requestLines
.
AppendLine
(
string
.
Join
(
" "
,
Request
.
Method
,
Request
.
RequestUri
.
PathAndQuery
,
$"HTTP/
{
Request
.
HttpVersion
.
Major
}
.
{
Request
.
HttpVersion
.
Minor
}
"
));
}
//Send Authentication to Upstream proxy if needed
...
...
@@ -86,7 +92,7 @@ namespace Titanium.Web.Proxy.Http
requestLines
.
AppendLine
(
"Proxy-Authorization"
+
": Basic "
+
Convert
.
ToBase64String
(
Encoding
.
UTF8
.
GetBytes
(
ServerConnection
.
UpStreamHttpProxy
.
UserName
+
":"
+
ServerConnection
.
UpStreamHttpProxy
.
Password
)));
}
//write request headers
foreach
(
var
headerItem
in
this
.
Request
.
RequestHeaders
)
foreach
(
var
headerItem
in
Request
.
RequestHeaders
)
{
var
header
=
headerItem
.
Value
;
if
(
headerItem
.
Key
!=
"Proxy-Authorization"
)
...
...
@@ -96,7 +102,7 @@ namespace Titanium.Web.Proxy.Http
}
//write non unique request headers
foreach
(
var
headerItem
in
this
.
Request
.
NonUniqueRequestHeaders
)
foreach
(
var
headerItem
in
Request
.
NonUniqueRequestHeaders
)
{
var
headers
=
headerItem
.
Value
;
foreach
(
var
header
in
headers
)
...
...
@@ -110,15 +116,15 @@ namespace Titanium.Web.Proxy.Http
requestLines
.
AppendLine
();
string
request
=
requestLines
.
ToString
();
byte
[]
requestBytes
=
Encoding
.
ASCII
.
GetBytes
(
request
);
var
request
=
requestLines
.
ToString
();
var
requestBytes
=
Encoding
.
ASCII
.
GetBytes
(
request
);
await
stream
.
WriteAsync
(
requestBytes
,
0
,
requestBytes
.
Length
);
await
stream
.
FlushAsync
();
if
(
enable100ContinueBehaviour
)
{
if
(
this
.
Request
.
ExpectContinue
)
if
(
Request
.
ExpectContinue
)
{
var
httpResult
=
(
await
ServerConnection
.
StreamReader
.
ReadLineAsync
()).
Split
(
ProxyConstants
.
SpaceSplit
,
3
);
var
responseStatusCode
=
httpResult
[
1
].
Trim
();
...
...
@@ -128,13 +134,13 @@ namespace Titanium.Web.Proxy.Http
if
(
responseStatusCode
.
Equals
(
"100"
)
&&
responseStatusDescription
.
ToLower
().
Equals
(
"continue"
))
{
this
.
Request
.
Is100Continue
=
true
;
Request
.
Is100Continue
=
true
;
await
ServerConnection
.
StreamReader
.
ReadLineAsync
();
}
else
if
(
responseStatusCode
.
Equals
(
"417"
)
&&
responseStatusDescription
.
ToLower
().
Equals
(
"expectation failed"
))
{
this
.
Request
.
ExpectationFailed
=
true
;
Request
.
ExpectationFailed
=
true
;
await
ServerConnection
.
StreamReader
.
ReadLineAsync
();
}
}
...
...
@@ -148,7 +154,7 @@ namespace Titanium.Web.Proxy.Http
internal
async
Task
ReceiveResponse
()
{
//return if this is already read
if
(
this
.
Response
.
ResponseStatusCode
!=
null
)
return
;
if
(
Response
.
ResponseStatusCode
!=
null
)
return
;
var
httpResult
=
(
await
ServerConnection
.
StreamReader
.
ReadLineAsync
()).
Split
(
ProxyConstants
.
SpaceSplit
,
3
);
...
...
@@ -161,33 +167,33 @@ namespace Titanium.Web.Proxy.Http
var
httpVersion
=
httpResult
[
0
].
Trim
().
ToLower
();
var
version
=
new
Version
(
1
,
1
);
if
(
httpVersion
==
"http/1.0"
)
if
(
0
==
string
.
CompareOrdinal
(
httpVersion
,
"http/1.0"
)
)
{
version
=
new
Version
(
1
,
0
);
}
this
.
Response
.
HttpVersion
=
version
;
this
.
Response
.
ResponseStatusCode
=
httpResult
[
1
].
Trim
();
this
.
Response
.
ResponseStatusDescription
=
httpResult
[
2
].
Trim
();
Response
.
HttpVersion
=
version
;
Response
.
ResponseStatusCode
=
httpResult
[
1
].
Trim
();
Response
.
ResponseStatusDescription
=
httpResult
[
2
].
Trim
();
//For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request
if
(
this
.
Response
.
ResponseStatusCode
.
Equals
(
"100"
)
&&
this
.
Response
.
ResponseStatusDescription
.
ToLower
().
Equals
(
"continue"
))
if
(
Response
.
ResponseStatusCode
.
Equals
(
"100"
)
&&
Response
.
ResponseStatusDescription
.
ToLower
().
Equals
(
"continue"
))
{
//Read the next line after 100-continue
this
.
Response
.
Is100Continue
=
true
;
this
.
Response
.
ResponseStatusCode
=
null
;
Response
.
Is100Continue
=
true
;
Response
.
ResponseStatusCode
=
null
;
await
ServerConnection
.
StreamReader
.
ReadLineAsync
();
//now receive response
await
ReceiveResponse
();
return
;
}
else
if
(
this
.
Response
.
ResponseStatusCode
.
Equals
(
"417"
)
&&
this
.
Response
.
ResponseStatusDescription
.
ToLower
().
Equals
(
"expectation failed"
))
else
if
(
Response
.
ResponseStatusCode
.
Equals
(
"417"
)
&&
Response
.
ResponseStatusDescription
.
ToLower
().
Equals
(
"expectation failed"
))
{
//read next line after expectation failed response
this
.
Response
.
ExpectationFailed
=
true
;
this
.
Response
.
ResponseStatusCode
=
null
;
Response
.
ExpectationFailed
=
true
;
Response
.
ResponseStatusCode
=
null
;
await
ServerConnection
.
StreamReader
.
ReadLineAsync
();
//now receive response
await
ReceiveResponse
();
...
...
Titanium.Web.Proxy/Http/Request.cs
View file @
142cabcb
...
...
@@ -34,12 +34,7 @@ namespace Titanium.Web.Proxy.Http
get
{
var
hasHeader
=
RequestHeaders
.
ContainsKey
(
"host"
);
if
(
hasHeader
)
{
return
RequestHeaders
[
"host"
].
Value
;
}
return
null
;
return
hasHeader
?
RequestHeaders
[
"host"
].
Value
:
null
;
}
set
{
...
...
@@ -154,11 +149,11 @@ namespace Titanium.Web.Proxy.Http
if
(
hasHeader
)
{
var
header
=
RequestHeaders
[
"content-type"
];
header
.
Value
=
value
.
ToString
()
;
header
.
Value
=
value
;
}
else
{
RequestHeaders
.
Add
(
"content-type"
,
new
HttpHeader
(
"content-type"
,
value
.
ToString
()
));
RequestHeaders
.
Add
(
"content-type"
,
new
HttpHeader
(
"content-type"
,
value
));
}
}
...
...
@@ -219,15 +214,11 @@ namespace Titanium.Web.Proxy.Http
{
var
hasHeader
=
RequestHeaders
.
ContainsKey
(
"expect"
);
if
(
hasHeader
)
{
if
(!
hasHeader
)
return
false
;
var
header
=
RequestHeaders
[
"expect"
];
return
header
.
Value
.
Equals
(
"100-continue"
);
}
return
false
;
}
}
/// <summary>
...
...
@@ -275,13 +266,7 @@ namespace Titanium.Web.Proxy.Http
var
header
=
RequestHeaders
[
"upgrade"
];
if
(
header
.
Value
.
ToLower
()
==
"websocket"
)
{
return
true
;
}
return
false
;
return
header
.
Value
.
ToLower
()
==
"websocket"
;
}
}
...
...
@@ -305,6 +290,9 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public
bool
ExpectationFailed
{
get
;
internal
set
;
}
/// <summary>
/// Constructor.
/// </summary>
public
Request
()
{
RequestHeaders
=
new
Dictionary
<
string
,
HttpHeader
>(
StringComparer
.
OrdinalIgnoreCase
);
...
...
Titanium.Web.Proxy/Http/Response.cs
View file @
142cabcb
...
...
@@ -12,7 +12,13 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public
class
Response
{
/// <summary>
/// Response Status Code.
/// </summary>
public
string
ResponseStatusCode
{
get
;
set
;
}
/// <summary>
/// Response Status description.
/// </summary>
public
string
ResponseStatusDescription
{
get
;
set
;
}
internal
Encoding
Encoding
=>
this
.
GetResponseCharacterEncoding
();
...
...
@@ -26,15 +32,11 @@ namespace Titanium.Web.Proxy.Http
{
var
hasHeader
=
ResponseHeaders
.
ContainsKey
(
"content-encoding"
);
if
(
hasHeader
)
{
if
(!
hasHeader
)
return
null
;
var
header
=
ResponseHeaders
[
"content-encoding"
];
return
header
.
Value
.
Trim
();
}
return
null
;
}
}
internal
Version
HttpVersion
{
get
;
set
;
}
...
...
@@ -229,10 +231,13 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public
bool
ExpectationFailed
{
get
;
internal
set
;
}
/// <summary>
/// Constructor.
/// </summary>
public
Response
()
{
this
.
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>(
StringComparer
.
OrdinalIgnoreCase
);
this
.
NonUniqueResponseHeaders
=
new
Dictionary
<
string
,
List
<
HttpHeader
>>(
StringComparer
.
OrdinalIgnoreCase
);
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>(
StringComparer
.
OrdinalIgnoreCase
);
NonUniqueResponseHeaders
=
new
Dictionary
<
string
,
List
<
HttpHeader
>>(
StringComparer
.
OrdinalIgnoreCase
);
}
}
...
...
Titanium.Web.Proxy/Http/Responses/GenericResponse.cs
View file @
142cabcb
...
...
@@ -7,16 +7,25 @@ namespace Titanium.Web.Proxy.Http.Responses
/// </summary>
public
class
GenericResponse
:
Response
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="status"></param>
public
GenericResponse
(
HttpStatusCode
status
)
{
ResponseStatusCode
=
((
int
)
status
).
ToString
();
ResponseStatusDescription
=
status
.
ToString
();
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="statusCode"></param>
/// <param name="statusDescription"></param>
public
GenericResponse
(
string
statusCode
,
string
statusDescription
)
{
ResponseStatusCode
=
statusCode
;
ResponseStatusDescription
=
statusCode
;
ResponseStatusDescription
=
statusDescription
;
}
}
}
Titanium.Web.Proxy/Http/Responses/OkResponse.cs
View file @
142cabcb
...
...
@@ -5,6 +5,9 @@
/// </summary>
public
sealed
class
OkResponse
:
Response
{
/// <summary>
/// Constructor.
/// </summary>
public
OkResponse
()
{
ResponseStatusCode
=
"200"
;
...
...
Titanium.Web.Proxy/Http/Responses/RedirectResponse.cs
View file @
142cabcb
...
...
@@ -5,6 +5,9 @@
/// </summary>
public
sealed
class
RedirectResponse
:
Response
{
/// <summary>
/// Constructor.
/// </summary>
public
RedirectResponse
()
{
ResponseStatusCode
=
"302"
;
...
...
Titanium.Web.Proxy/Models/EndPoint.cs
View file @
142cabcb
...
...
@@ -10,20 +10,38 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public
abstract
class
ProxyEndPoint
{
public
ProxyEndPoint
(
IPAddress
IpAddress
,
int
Port
,
bool
EnableSsl
)
/// <summary>
/// Constructor.
/// </summary>
/// <param name="IpAddress"></param>
/// <param name="Port"></param>
/// <param name="EnableSsl"></param>
protected
ProxyEndPoint
(
IPAddress
IpAddress
,
int
Port
,
bool
EnableSsl
)
{
this
.
IpAddress
=
IpAddress
;
this
.
Port
=
Port
;
this
.
EnableSsl
=
EnableSsl
;
}
/// <summary>
/// Ip Address.
/// </summary>
public
IPAddress
IpAddress
{
get
;
internal
set
;
}
/// <summary>
/// Port.
/// </summary>
public
int
Port
{
get
;
internal
set
;
}
/// <summary>
/// Enable SSL?
/// </summary>
public
bool
EnableSsl
{
get
;
internal
set
;
}
public
bool
IpV6Enabled
=>
IpAddress
==
IPAddress
.
IPv6Any
||
IpAddress
==
IPAddress
.
IPv6Loopback
||
IpAddress
==
IPAddress
.
IPv6None
;
/// <summary>
/// Is IPv6 enabled?
/// </summary>
public
bool
IpV6Enabled
=>
Equals
(
IpAddress
,
IPAddress
.
IPv6Any
)
||
Equals
(
IpAddress
,
IPAddress
.
IPv6Loopback
)
||
Equals
(
IpAddress
,
IPAddress
.
IPv6None
);
internal
TcpListener
listener
{
get
;
set
;
}
}
...
...
@@ -37,10 +55,22 @@ namespace Titanium.Web.Proxy.Models
internal
bool
IsSystemHttpProxy
{
get
;
set
;
}
internal
bool
IsSystemHttpsProxy
{
get
;
set
;
}
/// <summary>
/// List of host names to exclude using Regular Expressions.
/// </summary>
public
List
<
string
>
ExcludedHttpsHostNameRegex
{
get
;
set
;
}
/// <summary>
/// Generic certificate to use for SSL decryption.
/// </summary>
public
X509Certificate2
GenericCertificate
{
get
;
set
;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="IpAddress"></param>
/// <param name="Port"></param>
/// <param name="EnableSsl"></param>
public
ExplicitProxyEndPoint
(
IPAddress
IpAddress
,
int
Port
,
bool
EnableSsl
)
:
base
(
IpAddress
,
Port
,
EnableSsl
)
{
...
...
@@ -54,13 +84,19 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public
class
TransparentProxyEndPoint
:
ProxyEndPoint
{
//Name of the Certificate need to be sent (same as the hostname we want to proxy)
//This is valid only when UseServerNameIndication is set to false
public
string
GenericCertificateName
{
get
;
set
;
}
// public bool UseServerNameIndication { get; set; }
/// <summary>
/// Name of the Certificate need to be sent (same as the hostname we want to proxy)
/// This is valid only when UseServerNameIndication is set to false
/// </summary>
public
string
GenericCertificateName
{
get
;
set
;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="IpAddress"></param>
/// <param name="Port"></param>
/// <param name="EnableSsl"></param>
public
TransparentProxyEndPoint
(
IPAddress
IpAddress
,
int
Port
,
bool
EnableSsl
)
:
base
(
IpAddress
,
Port
,
EnableSsl
)
{
...
...
Titanium.Web.Proxy/Models/ExternalProxy.cs
View file @
142cabcb
...
...
@@ -13,8 +13,14 @@ namespace Titanium.Web.Proxy.Models
private
string
userName
;
private
string
password
;
/// <summary>
/// Use default windows credentials?
/// </summary>
public
bool
UseDefaultCredentials
{
get
;
set
;
}
/// <summary>
/// Username.
/// </summary>
public
string
UserName
{
get
{
return
UseDefaultCredentials
?
DefaultCredentials
.
Value
.
UserName
:
userName
;
}
set
...
...
@@ -28,6 +34,9 @@ namespace Titanium.Web.Proxy.Models
}
}
/// <summary>
/// Password.
/// </summary>
public
string
Password
{
get
{
return
UseDefaultCredentials
?
DefaultCredentials
.
Value
.
Password
:
password
;
}
...
...
@@ -42,7 +51,13 @@ namespace Titanium.Web.Proxy.Models
}
}
/// <summary>
/// Host name.
/// </summary>
public
string
HostName
{
get
;
set
;
}
/// <summary>
/// Port.
/// </summary>
public
int
Port
{
get
;
set
;
}
}
}
Titanium.Web.Proxy/Models/HttpHeader.cs
View file @
142cabcb
...
...
@@ -7,6 +7,12 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public
class
HttpHeader
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <exception cref="Exception"></exception>
public
HttpHeader
(
string
name
,
string
value
)
{
if
(
string
.
IsNullOrEmpty
(
name
))
...
...
@@ -18,7 +24,13 @@ namespace Titanium.Web.Proxy.Models
Value
=
value
.
Trim
();
}
/// <summary>
/// Header Name.
/// </summary>
public
string
Name
{
get
;
set
;
}
/// <summary>
/// Header Value.
/// </summary>
public
string
Value
{
get
;
set
;
}
/// <summary>
...
...
@@ -27,7 +39,7 @@ namespace Titanium.Web.Proxy.Models
/// <returns></returns>
public
override
string
ToString
()
{
return
string
.
Format
(
"{0}: {1}"
,
Name
,
Value
)
;
return
$"
{
Name
}
:
{
Value
}
"
;
}
}
}
\ No newline at end of file
Titanium.Web.Proxy/Network/Certificate/BCCertificateMaker.cs
0 → 100644
View file @
142cabcb
using
System
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Threading
;
using
Org.BouncyCastle.Asn1
;
using
Org.BouncyCastle.Asn1.Pkcs
;
using
Org.BouncyCastle.Asn1.X509
;
using
Org.BouncyCastle.Crypto
;
using
Org.BouncyCastle.Crypto.Generators
;
using
Org.BouncyCastle.Crypto.Operators
;
using
Org.BouncyCastle.Crypto.Parameters
;
using
Org.BouncyCastle.Crypto.Prng
;
using
Org.BouncyCastle.Math
;
using
Org.BouncyCastle.OpenSsl
;
using
Org.BouncyCastle.Pkcs
;
using
Org.BouncyCastle.Security
;
using
Org.BouncyCastle.Utilities
;
using
Org.BouncyCastle.X509
;
namespace
Titanium.Web.Proxy.Network.Certificate
{
/// <summary>
/// Implements certificate generation operations.
/// </summary>
public
class
BCCertificateMaker
:
ICertificateMaker
{
private
const
int
CertificateValidDays
=
1825
;
private
const
int
CertificateGraceDays
=
366
;
/// <summary>
/// Makes the certificate.
/// </summary>
/// <param name="sSubjectCn">The s subject cn.</param>
/// <param name="isRoot">if set to <c>true</c> [is root].</param>
/// <param name="signingCert">The signing cert.</param>
/// <returns>X509Certificate2 instance.</returns>
public
X509Certificate2
MakeCertificate
(
string
sSubjectCn
,
bool
isRoot
,
X509Certificate2
signingCert
=
null
)
{
return
MakeCertificateInternal
(
sSubjectCn
,
isRoot
,
true
,
signingCert
);
}
/// <summary>
/// Generates the certificate.
/// </summary>
/// <param name="subjectName">Name of the subject.</param>
/// <param name="issuerName">Name of the issuer.</param>
/// <param name="validFrom">The valid from.</param>
/// <param name="validTo">The valid to.</param>
/// <param name="keyStrength">The key strength.</param>
/// <param name="signatureAlgorithm">The signature algorithm.</param>
/// <param name="issuerPrivateKey">The issuer private key.</param>
/// <returns>X509Certificate2 instance.</returns>
/// <exception cref="PemException">Malformed sequence in RSA private key</exception>
private
static
X509Certificate2
GenerateCertificate
(
string
subjectName
,
string
issuerName
,
DateTime
validFrom
,
DateTime
validTo
,
int
keyStrength
=
2048
,
string
signatureAlgorithm
=
"SHA256WithRSA"
,
AsymmetricKeyParameter
issuerPrivateKey
=
null
)
{
// Generating Random Numbers
var
randomGenerator
=
new
CryptoApiRandomGenerator
();
var
secureRandom
=
new
SecureRandom
(
randomGenerator
);
// The Certificate Generator
var
certificateGenerator
=
new
X509V3CertificateGenerator
();
// Serial Number
var
serialNumber
=
BigIntegers
.
CreateRandomInRange
(
BigInteger
.
One
,
BigInteger
.
ValueOf
(
long
.
MaxValue
),
secureRandom
);
certificateGenerator
.
SetSerialNumber
(
serialNumber
);
// Issuer and Subject Name
var
subjectDn
=
new
X509Name
(
subjectName
);
var
issuerDn
=
new
X509Name
(
issuerName
);
certificateGenerator
.
SetIssuerDN
(
issuerDn
);
certificateGenerator
.
SetSubjectDN
(
subjectDn
);
certificateGenerator
.
SetNotBefore
(
validFrom
);
certificateGenerator
.
SetNotAfter
(
validTo
);
// Subject Public Key
var
keyGenerationParameters
=
new
KeyGenerationParameters
(
secureRandom
,
keyStrength
);
var
keyPairGenerator
=
new
RsaKeyPairGenerator
();
keyPairGenerator
.
Init
(
keyGenerationParameters
);
var
subjectKeyPair
=
keyPairGenerator
.
GenerateKeyPair
();
certificateGenerator
.
SetPublicKey
(
subjectKeyPair
.
Public
);
// Set certificate intended purposes to only Server Authentication
certificateGenerator
.
AddExtension
(
X509Extensions
.
ExtendedKeyUsage
.
Id
,
false
,
new
ExtendedKeyUsage
(
KeyPurposeID
.
IdKPServerAuth
));
var
signatureFactory
=
new
Asn1SignatureFactory
(
signatureAlgorithm
,
issuerPrivateKey
??
subjectKeyPair
.
Private
,
secureRandom
);
// Self-sign the certificate
var
certificate
=
certificateGenerator
.
Generate
(
signatureFactory
);
var
x509Certificate
=
new
X509Certificate2
(
certificate
.
GetEncoded
());
// Corresponding private key
var
privateKeyInfo
=
PrivateKeyInfoFactory
.
CreatePrivateKeyInfo
(
subjectKeyPair
.
Private
);
var
seq
=
(
Asn1Sequence
)
Asn1Object
.
FromByteArray
(
privateKeyInfo
.
ParsePrivateKey
().
GetDerEncoded
());
if
(
seq
.
Count
!=
9
)
{
throw
new
PemException
(
"Malformed sequence in RSA private key"
);
}
var
rsa
=
RsaPrivateKeyStructure
.
GetInstance
(
seq
);
var
rsaparams
=
new
RsaPrivateCrtKeyParameters
(
rsa
.
Modulus
,
rsa
.
PublicExponent
,
rsa
.
PrivateExponent
,
rsa
.
Prime1
,
rsa
.
Prime2
,
rsa
.
Exponent1
,
rsa
.
Exponent2
,
rsa
.
Coefficient
);
// Set private key onto certificate instance
x509Certificate
.
PrivateKey
=
DotNetUtilities
.
ToRSA
(
rsaparams
);
x509Certificate
.
FriendlyName
=
subjectName
;
return
x509Certificate
;
}
/// <summary>
/// Makes the certificate internal.
/// </summary>
/// <param name="isRoot">if set to <c>true</c> [is root].</param>
/// <param name="fullSubject">The full subject.</param>
/// <param name="validFrom">The valid from.</param>
/// <param name="validTo">The valid to.</param>
/// <param name="signingCertificate">The signing certificate.</param>
/// <returns>X509Certificate2 instance.</returns>
/// <exception cref="System.ArgumentException">You must specify a Signing Certificate if and only if you are not creating a root.</exception>
private
X509Certificate2
MakeCertificateInternal
(
bool
isRoot
,
string
fullSubject
,
DateTime
validFrom
,
DateTime
validTo
,
X509Certificate2
signingCertificate
)
{
if
(
isRoot
!=
(
null
==
signingCertificate
))
{
throw
new
ArgumentException
(
"You must specify a Signing Certificate if and only if you are not creating a root."
,
nameof
(
signingCertificate
));
}
return
isRoot
?
GenerateCertificate
(
fullSubject
,
fullSubject
,
validFrom
,
validTo
)
:
GenerateCertificate
(
fullSubject
,
signingCertificate
.
Subject
,
validFrom
,
validTo
,
issuerPrivateKey
:
DotNetUtilities
.
GetKeyPair
(
signingCertificate
.
PrivateKey
).
Private
);
}
/// <summary>
/// Makes the certificate internal.
/// </summary>
/// <param name="subject">The s subject cn.</param>
/// <param name="isRoot">if set to <c>true</c> [is root].</param>
/// <param name="switchToMtaIfNeeded">if set to <c>true</c> [switch to MTA if needed].</param>
/// <param name="signingCert">The signing cert.</param>
/// <returns>X509Certificate2.</returns>
private
X509Certificate2
MakeCertificateInternal
(
string
subject
,
bool
isRoot
,
bool
switchToMtaIfNeeded
,
X509Certificate2
signingCert
=
null
,
CancellationToken
cancellationToken
=
default
(
CancellationToken
))
{
X509Certificate2
certificate
=
null
;
if
(
switchToMtaIfNeeded
&&
Thread
.
CurrentThread
.
GetApartmentState
()
!=
ApartmentState
.
MTA
)
{
using
(
var
manualResetEvent
=
new
ManualResetEventSlim
(
false
))
{
ThreadPool
.
QueueUserWorkItem
(
o
=>
{
certificate
=
MakeCertificateInternal
(
subject
,
isRoot
,
false
,
signingCert
);
if
(!
cancellationToken
.
IsCancellationRequested
)
{
manualResetEvent
?.
Set
();
}
});
manualResetEvent
.
Wait
(
TimeSpan
.
FromMinutes
(
1
),
cancellationToken
:
cancellationToken
);
}
return
certificate
;
}
return
MakeCertificateInternal
(
isRoot
,
$"CN=
{
subject
}
"
,
DateTime
.
UtcNow
.
AddDays
(-
CertificateGraceDays
),
DateTime
.
UtcNow
.
AddDays
(
CertificateValidDays
),
isRoot
?
null
:
signingCert
);
}
}
}
\ No newline at end of file
Titanium.Web.Proxy/Network/Certificate/ICertificateMaker.cs
0 → 100644
View file @
142cabcb
using
System.Security.Cryptography.X509Certificates
;
namespace
Titanium.Web.Proxy.Network.Certificate
{
/// <summary>
/// Abstract interface for different Certificate Maker Engines
/// </summary>
internal
interface
ICertificateMaker
{
X509Certificate2
MakeCertificate
(
string
sSubjectCn
,
bool
isRoot
,
X509Certificate2
signingCert
);
}
}
Titanium.Web.Proxy/Network/Certificate/WinCertificateMaker.cs
0 → 100644
View file @
142cabcb
using
System
;
using
System.Reflection
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Threading
;
namespace
Titanium.Web.Proxy.Network.Certificate
{
/// <summary>
/// Certificate Maker - uses MakeCert
/// </summary>
public
class
WinCertificateMaker
:
ICertificateMaker
{
private
readonly
Type
typeX500DN
;
private
readonly
Type
typeX509PrivateKey
;
private
readonly
Type
typeOID
;
private
readonly
Type
typeOIDS
;
private
readonly
Type
typeKUExt
;
private
readonly
Type
typeEKUExt
;
private
readonly
Type
typeRequestCert
;
private
readonly
Type
typeX509Extensions
;
private
readonly
Type
typeBasicConstraints
;
private
readonly
Type
typeSignerCertificate
;
private
readonly
Type
typeX509Enrollment
;
private
readonly
string
sProviderName
=
"Microsoft Enhanced Cryptographic Provider v1.0"
;
private
object
_SharedPrivateKey
;
/// <summary>
/// Constructor.
/// </summary>
public
WinCertificateMaker
()
{
typeX500DN
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX500DistinguishedName"
,
true
);
typeX509PrivateKey
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509PrivateKey"
,
true
);
typeOID
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CObjectId"
,
true
);
typeOIDS
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CObjectIds.1"
,
true
);
typeEKUExt
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509ExtensionEnhancedKeyUsage"
);
typeKUExt
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509ExtensionKeyUsage"
);
typeRequestCert
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509CertificateRequestCertificate"
);
typeX509Extensions
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509Extensions"
);
typeBasicConstraints
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509ExtensionBasicConstraints"
);
typeSignerCertificate
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CSignerCertificate"
);
typeX509Enrollment
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509Enrollment"
);
}
/// <summary>
/// Make certificate.
/// </summary>
/// <param name="sSubjectCN"></param>
/// <param name="isRoot"></param>
/// <param name="signingCert"></param>
/// <returns></returns>
public
X509Certificate2
MakeCertificate
(
string
sSubjectCN
,
bool
isRoot
,
X509Certificate2
signingCert
=
null
)
{
return
MakeCertificateInternal
(
sSubjectCN
,
isRoot
,
true
,
signingCert
);
}
private
X509Certificate2
MakeCertificate
(
bool
IsRoot
,
string
FullSubject
,
int
PrivateKeyLength
,
string
HashAlg
,
DateTime
ValidFrom
,
DateTime
ValidTo
,
X509Certificate2
SigningCertificate
)
{
if
(
IsRoot
!=
(
null
==
SigningCertificate
))
{
throw
new
ArgumentException
(
"You must specify a Signing Certificate if and only if you are not creating a root."
,
nameof
(
IsRoot
));
}
var
x500DN
=
Activator
.
CreateInstance
(
typeX500DN
);
var
subject
=
new
object
[]
{
FullSubject
,
0
};
typeX500DN
.
InvokeMember
(
"Encode"
,
BindingFlags
.
InvokeMethod
,
null
,
x500DN
,
subject
);
var
x500DN2
=
Activator
.
CreateInstance
(
typeX500DN
);
if
(!
IsRoot
)
{
subject
[
0
]
=
SigningCertificate
.
Subject
;
}
typeX500DN
.
InvokeMember
(
"Encode"
,
BindingFlags
.
InvokeMethod
,
null
,
x500DN2
,
subject
);
object
sharedPrivateKey
=
null
;
if
(!
IsRoot
)
{
sharedPrivateKey
=
_SharedPrivateKey
;
}
if
(
sharedPrivateKey
==
null
)
{
sharedPrivateKey
=
Activator
.
CreateInstance
(
typeX509PrivateKey
);
subject
=
new
object
[]
{
sProviderName
};
typeX509PrivateKey
.
InvokeMember
(
"ProviderName"
,
BindingFlags
.
PutDispProperty
,
null
,
sharedPrivateKey
,
subject
);
subject
[
0
]
=
2
;
typeX509PrivateKey
.
InvokeMember
(
"ExportPolicy"
,
BindingFlags
.
PutDispProperty
,
null
,
sharedPrivateKey
,
subject
);
subject
=
new
object
[]
{
(
IsRoot
?
2
:
1
)
};
typeX509PrivateKey
.
InvokeMember
(
"KeySpec"
,
BindingFlags
.
PutDispProperty
,
null
,
sharedPrivateKey
,
subject
);
if
(!
IsRoot
)
{
subject
=
new
object
[]
{
176
};
typeX509PrivateKey
.
InvokeMember
(
"KeyUsage"
,
BindingFlags
.
PutDispProperty
,
null
,
sharedPrivateKey
,
subject
);
}
subject
[
0
]
=
PrivateKeyLength
;
typeX509PrivateKey
.
InvokeMember
(
"Length"
,
BindingFlags
.
PutDispProperty
,
null
,
sharedPrivateKey
,
subject
);
typeX509PrivateKey
.
InvokeMember
(
"Create"
,
BindingFlags
.
InvokeMethod
,
null
,
sharedPrivateKey
,
null
);
if
(!
IsRoot
)
{
_SharedPrivateKey
=
sharedPrivateKey
;
}
}
subject
=
new
object
[
1
];
var
obj3
=
Activator
.
CreateInstance
(
typeOID
);
subject
[
0
]
=
"1.3.6.1.5.5.7.3.1"
;
typeOID
.
InvokeMember
(
"InitializeFromValue"
,
BindingFlags
.
InvokeMethod
,
null
,
obj3
,
subject
);
var
obj4
=
Activator
.
CreateInstance
(
typeOIDS
);
subject
[
0
]
=
obj3
;
typeOIDS
.
InvokeMember
(
"Add"
,
BindingFlags
.
InvokeMethod
,
null
,
obj4
,
subject
);
var
obj5
=
Activator
.
CreateInstance
(
typeEKUExt
);
subject
[
0
]
=
obj4
;
typeEKUExt
.
InvokeMember
(
"InitializeEncode"
,
BindingFlags
.
InvokeMethod
,
null
,
obj5
,
subject
);
var
obj6
=
Activator
.
CreateInstance
(
typeRequestCert
);
subject
=
new
[]
{
1
,
sharedPrivateKey
,
string
.
Empty
};
typeRequestCert
.
InvokeMember
(
"InitializeFromPrivateKey"
,
BindingFlags
.
InvokeMethod
,
null
,
obj6
,
subject
);
subject
=
new
[]
{
x500DN
};
typeRequestCert
.
InvokeMember
(
"Subject"
,
BindingFlags
.
PutDispProperty
,
null
,
obj6
,
subject
);
subject
[
0
]
=
x500DN
;
typeRequestCert
.
InvokeMember
(
"Issuer"
,
BindingFlags
.
PutDispProperty
,
null
,
obj6
,
subject
);
subject
[
0
]
=
ValidFrom
;
typeRequestCert
.
InvokeMember
(
"NotBefore"
,
BindingFlags
.
PutDispProperty
,
null
,
obj6
,
subject
);
subject
[
0
]
=
ValidTo
;
typeRequestCert
.
InvokeMember
(
"NotAfter"
,
BindingFlags
.
PutDispProperty
,
null
,
obj6
,
subject
);
var
obj7
=
Activator
.
CreateInstance
(
typeKUExt
);
subject
[
0
]
=
176
;
typeKUExt
.
InvokeMember
(
"InitializeEncode"
,
BindingFlags
.
InvokeMethod
,
null
,
obj7
,
subject
);
var
obj8
=
typeRequestCert
.
InvokeMember
(
"X509Extensions"
,
BindingFlags
.
GetProperty
,
null
,
obj6
,
null
);
subject
=
new
object
[
1
];
if
(!
IsRoot
)
{
subject
[
0
]
=
obj7
;
typeX509Extensions
.
InvokeMember
(
"Add"
,
BindingFlags
.
InvokeMethod
,
null
,
obj8
,
subject
);
}
subject
[
0
]
=
obj5
;
typeX509Extensions
.
InvokeMember
(
"Add"
,
BindingFlags
.
InvokeMethod
,
null
,
obj8
,
subject
);
if
(!
IsRoot
)
{
var
obj12
=
Activator
.
CreateInstance
(
typeSignerCertificate
);
subject
=
new
object
[]
{
0
,
0
,
12
,
SigningCertificate
.
Thumbprint
};
typeSignerCertificate
.
InvokeMember
(
"Initialize"
,
BindingFlags
.
InvokeMethod
,
null
,
obj12
,
subject
);
subject
=
new
[]
{
obj12
};
typeRequestCert
.
InvokeMember
(
"SignerCertificate"
,
BindingFlags
.
PutDispProperty
,
null
,
obj6
,
subject
);
}
else
{
var
obj13
=
Activator
.
CreateInstance
(
typeBasicConstraints
);
subject
=
new
object
[]
{
"true"
,
"0"
};
typeBasicConstraints
.
InvokeMember
(
"InitializeEncode"
,
BindingFlags
.
InvokeMethod
,
null
,
obj13
,
subject
);
subject
=
new
[]
{
obj13
};
typeX509Extensions
.
InvokeMember
(
"Add"
,
BindingFlags
.
InvokeMethod
,
null
,
obj8
,
subject
);
}
var
obj14
=
Activator
.
CreateInstance
(
typeOID
);
subject
=
new
object
[]
{
1
,
0
,
0
,
HashAlg
};
typeOID
.
InvokeMember
(
"InitializeFromAlgorithmName"
,
BindingFlags
.
InvokeMethod
,
null
,
obj14
,
subject
);
subject
=
new
[]
{
obj14
};
typeRequestCert
.
InvokeMember
(
"HashAlgorithm"
,
BindingFlags
.
PutDispProperty
,
null
,
obj6
,
subject
);
typeRequestCert
.
InvokeMember
(
"Encode"
,
BindingFlags
.
InvokeMethod
,
null
,
obj6
,
null
);
var
obj15
=
Activator
.
CreateInstance
(
typeX509Enrollment
);
subject
[
0
]
=
obj6
;
typeX509Enrollment
.
InvokeMember
(
"InitializeFromRequest"
,
BindingFlags
.
InvokeMethod
,
null
,
obj15
,
subject
);
if
(
IsRoot
)
{
subject
[
0
]
=
"DO_NOT_TRUST_TitaniumProxy-CE"
;
typeX509Enrollment
.
InvokeMember
(
"CertificateFriendlyName"
,
BindingFlags
.
PutDispProperty
,
null
,
obj15
,
subject
);
}
subject
[
0
]
=
0
;
var
obj16
=
typeX509Enrollment
.
InvokeMember
(
"CreateRequest"
,
BindingFlags
.
InvokeMethod
,
null
,
obj15
,
subject
);
subject
=
new
[]
{
2
,
obj16
,
0
,
string
.
Empty
};
typeX509Enrollment
.
InvokeMember
(
"InstallResponse"
,
BindingFlags
.
InvokeMethod
,
null
,
obj15
,
subject
);
subject
=
new
object
[]
{
null
,
0
,
1
};
try
{
var
empty
=
(
string
)
typeX509Enrollment
.
InvokeMember
(
"CreatePFX"
,
BindingFlags
.
InvokeMethod
,
null
,
obj15
,
subject
);
return
new
X509Certificate2
(
Convert
.
FromBase64String
(
empty
),
string
.
Empty
,
X509KeyStorageFlags
.
Exportable
);
}
catch
(
Exception
)
{
// ignored
}
return
null
;
}
private
X509Certificate2
MakeCertificateInternal
(
string
sSubjectCN
,
bool
isRoot
,
bool
switchToMTAIfNeeded
,
X509Certificate2
signingCert
=
null
)
{
X509Certificate2
rCert
=
null
;
if
(
switchToMTAIfNeeded
&&
Thread
.
CurrentThread
.
GetApartmentState
()
!=
ApartmentState
.
MTA
)
{
var
manualResetEvent
=
new
ManualResetEvent
(
false
);
ThreadPool
.
QueueUserWorkItem
(
o
=>
{
rCert
=
MakeCertificateInternal
(
sSubjectCN
,
isRoot
,
false
,
signingCert
);
manualResetEvent
.
Set
();
});
manualResetEvent
.
WaitOne
();
manualResetEvent
.
Close
();
return
rCert
;
}
//Subject
var
fullSubject
=
$"CN=
{
sSubjectCN
}
"
;
//Sig Algo
var
HashAlgo
=
"SHA256"
;
//Grace Days
var
GraceDays
=
-
366
;
//ValiDays
var
ValidDays
=
1825
;
//KeyLength
var
keyLength
=
2048
;
var
graceTime
=
DateTime
.
Now
.
AddDays
(
GraceDays
);
var
now
=
DateTime
.
Now
;
rCert
=
!
isRoot
?
MakeCertificate
(
false
,
fullSubject
,
keyLength
,
HashAlgo
,
graceTime
,
now
.
AddDays
(
ValidDays
),
signingCert
)
:
MakeCertificate
(
true
,
fullSubject
,
keyLength
,
HashAlgo
,
graceTime
,
now
.
AddDays
(
ValidDays
),
null
);
return
rCert
;
}
}
}
Titanium.Web.Proxy/Network/CertificateMaker.cs
deleted
100644 → 0
View file @
43895859
using
System
;
using
System.Reflection
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Threading
;
namespace
Titanium.Web.Proxy.Network
{
public
class
CertificateMaker
{
private
Type
typeX500DN
;
private
Type
typeX509PrivateKey
;
private
Type
typeOID
;
private
Type
typeOIDS
;
private
Type
typeKUExt
;
private
Type
typeEKUExt
;
private
Type
typeRequestCert
;
private
Type
typeX509Extensions
;
private
Type
typeBasicConstraints
;
private
Type
typeSignerCertificate
;
private
Type
typeX509Enrollment
;
private
Type
typeAlternativeName
;
private
Type
typeAlternativeNames
;
private
Type
typeAlternativeNamesExt
;
private
string
sProviderName
=
"Microsoft Enhanced Cryptographic Provider v1.0"
;
private
object
_SharedPrivateKey
;
public
CertificateMaker
()
{
this
.
typeX500DN
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX500DistinguishedName"
,
true
);
this
.
typeX509PrivateKey
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509PrivateKey"
,
true
);
this
.
typeOID
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CObjectId"
,
true
);
this
.
typeOIDS
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CObjectIds.1"
,
true
);
this
.
typeEKUExt
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509ExtensionEnhancedKeyUsage"
);
this
.
typeKUExt
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509ExtensionKeyUsage"
);
this
.
typeRequestCert
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509CertificateRequestCertificate"
);
this
.
typeX509Extensions
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509Extensions"
);
this
.
typeBasicConstraints
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509ExtensionBasicConstraints"
);
this
.
typeSignerCertificate
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CSignerCertificate"
);
this
.
typeX509Enrollment
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509Enrollment"
);
this
.
typeAlternativeName
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CAlternativeName"
);
this
.
typeAlternativeNames
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CAlternativeNames"
);
this
.
typeAlternativeNamesExt
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509ExtensionAlternativeNames"
);
}
public
X509Certificate2
MakeCertificate
(
string
sSubjectCN
,
bool
isRoot
,
X509Certificate2
signingCert
=
null
)
{
return
this
.
MakeCertificateInternal
(
sSubjectCN
,
isRoot
,
true
,
signingCert
);
}
private
X509Certificate2
MakeCertificate
(
bool
IsRoot
,
string
SubjectCN
,
string
FullSubject
,
int
PrivateKeyLength
,
string
HashAlg
,
DateTime
ValidFrom
,
DateTime
ValidTo
,
X509Certificate2
SigningCertificate
)
{
X509Certificate2
cert
;
if
(
IsRoot
!=
(
null
==
SigningCertificate
))
{
throw
new
ArgumentException
(
"You must specify a Signing Certificate if and only if you are not creating a root."
,
"oSigningCertificate"
);
}
object
x500DN
=
Activator
.
CreateInstance
(
this
.
typeX500DN
);
object
[]
subject
=
new
object
[]
{
FullSubject
,
0
};
this
.
typeX500DN
.
InvokeMember
(
"Encode"
,
BindingFlags
.
InvokeMethod
,
null
,
x500DN
,
subject
);
object
x500DN2
=
Activator
.
CreateInstance
(
this
.
typeX500DN
);
if
(!
IsRoot
)
{
subject
[
0
]
=
SigningCertificate
.
Subject
;
}
this
.
typeX500DN
.
InvokeMember
(
"Encode"
,
BindingFlags
.
InvokeMethod
,
null
,
x500DN2
,
subject
);
object
sharedPrivateKey
=
null
;
if
(!
IsRoot
)
{
sharedPrivateKey
=
this
.
_SharedPrivateKey
;
}
if
(
sharedPrivateKey
==
null
)
{
sharedPrivateKey
=
Activator
.
CreateInstance
(
this
.
typeX509PrivateKey
);
subject
=
new
object
[]
{
this
.
sProviderName
};
this
.
typeX509PrivateKey
.
InvokeMember
(
"ProviderName"
,
BindingFlags
.
PutDispProperty
,
null
,
sharedPrivateKey
,
subject
);
subject
[
0
]
=
2
;
this
.
typeX509PrivateKey
.
InvokeMember
(
"ExportPolicy"
,
BindingFlags
.
PutDispProperty
,
null
,
sharedPrivateKey
,
subject
);
subject
=
new
object
[]
{
(
IsRoot
?
2
:
1
)
};
this
.
typeX509PrivateKey
.
InvokeMember
(
"KeySpec"
,
BindingFlags
.
PutDispProperty
,
null
,
sharedPrivateKey
,
subject
);
if
(!
IsRoot
)
{
subject
=
new
object
[]
{
176
};
this
.
typeX509PrivateKey
.
InvokeMember
(
"KeyUsage"
,
BindingFlags
.
PutDispProperty
,
null
,
sharedPrivateKey
,
subject
);
}
subject
[
0
]
=
PrivateKeyLength
;
this
.
typeX509PrivateKey
.
InvokeMember
(
"Length"
,
BindingFlags
.
PutDispProperty
,
null
,
sharedPrivateKey
,
subject
);
this
.
typeX509PrivateKey
.
InvokeMember
(
"Create"
,
BindingFlags
.
InvokeMethod
,
null
,
sharedPrivateKey
,
null
);
if
(!
IsRoot
)
{
this
.
_SharedPrivateKey
=
sharedPrivateKey
;
}
}
subject
=
new
object
[
1
];
object
obj3
=
Activator
.
CreateInstance
(
this
.
typeOID
);
subject
[
0
]
=
"1.3.6.1.5.5.7.3.1"
;
this
.
typeOID
.
InvokeMember
(
"InitializeFromValue"
,
BindingFlags
.
InvokeMethod
,
null
,
obj3
,
subject
);
object
obj4
=
Activator
.
CreateInstance
(
this
.
typeOIDS
);
subject
[
0
]
=
obj3
;
this
.
typeOIDS
.
InvokeMember
(
"Add"
,
BindingFlags
.
InvokeMethod
,
null
,
obj4
,
subject
);
object
obj5
=
Activator
.
CreateInstance
(
this
.
typeEKUExt
);
subject
[
0
]
=
obj4
;
this
.
typeEKUExt
.
InvokeMember
(
"InitializeEncode"
,
BindingFlags
.
InvokeMethod
,
null
,
obj5
,
subject
);
object
obj6
=
Activator
.
CreateInstance
(
this
.
typeRequestCert
);
subject
=
new
object
[]
{
1
,
sharedPrivateKey
,
string
.
Empty
};
this
.
typeRequestCert
.
InvokeMember
(
"InitializeFromPrivateKey"
,
BindingFlags
.
InvokeMethod
,
null
,
obj6
,
subject
);
subject
=
new
object
[]
{
x500DN
};
this
.
typeRequestCert
.
InvokeMember
(
"Subject"
,
BindingFlags
.
PutDispProperty
,
null
,
obj6
,
subject
);
subject
[
0
]
=
x500DN
;
this
.
typeRequestCert
.
InvokeMember
(
"Issuer"
,
BindingFlags
.
PutDispProperty
,
null
,
obj6
,
subject
);
subject
[
0
]
=
ValidFrom
;
this
.
typeRequestCert
.
InvokeMember
(
"NotBefore"
,
BindingFlags
.
PutDispProperty
,
null
,
obj6
,
subject
);
subject
[
0
]
=
ValidTo
;
this
.
typeRequestCert
.
InvokeMember
(
"NotAfter"
,
BindingFlags
.
PutDispProperty
,
null
,
obj6
,
subject
);
object
obj7
=
Activator
.
CreateInstance
(
this
.
typeKUExt
);
subject
[
0
]
=
176
;
this
.
typeKUExt
.
InvokeMember
(
"InitializeEncode"
,
BindingFlags
.
InvokeMethod
,
null
,
obj7
,
subject
);
object
obj8
=
this
.
typeRequestCert
.
InvokeMember
(
"X509Extensions"
,
BindingFlags
.
GetProperty
,
null
,
obj6
,
null
);
subject
=
new
object
[
1
];
if
(!
IsRoot
)
{
subject
[
0
]
=
obj7
;
this
.
typeX509Extensions
.
InvokeMember
(
"Add"
,
BindingFlags
.
InvokeMethod
,
null
,
obj8
,
subject
);
}
subject
[
0
]
=
obj5
;
this
.
typeX509Extensions
.
InvokeMember
(
"Add"
,
BindingFlags
.
InvokeMethod
,
null
,
obj8
,
subject
);
if
(!
IsRoot
)
{
object
obj12
=
Activator
.
CreateInstance
(
this
.
typeSignerCertificate
);
subject
=
new
object
[]
{
0
,
0
,
12
,
SigningCertificate
.
Thumbprint
};
this
.
typeSignerCertificate
.
InvokeMember
(
"Initialize"
,
BindingFlags
.
InvokeMethod
,
null
,
obj12
,
subject
);
subject
=
new
object
[]
{
obj12
};
this
.
typeRequestCert
.
InvokeMember
(
"SignerCertificate"
,
BindingFlags
.
PutDispProperty
,
null
,
obj6
,
subject
);
}
else
{
object
obj13
=
Activator
.
CreateInstance
(
this
.
typeBasicConstraints
);
subject
=
new
object
[]
{
"true"
,
"0"
};
this
.
typeBasicConstraints
.
InvokeMember
(
"InitializeEncode"
,
BindingFlags
.
InvokeMethod
,
null
,
obj13
,
subject
);
subject
=
new
object
[]
{
obj13
};
this
.
typeX509Extensions
.
InvokeMember
(
"Add"
,
BindingFlags
.
InvokeMethod
,
null
,
obj8
,
subject
);
}
object
obj14
=
Activator
.
CreateInstance
(
this
.
typeOID
);
subject
=
new
object
[]
{
1
,
0
,
0
,
HashAlg
};
this
.
typeOID
.
InvokeMember
(
"InitializeFromAlgorithmName"
,
BindingFlags
.
InvokeMethod
,
null
,
obj14
,
subject
);
subject
=
new
object
[]
{
obj14
};
this
.
typeRequestCert
.
InvokeMember
(
"HashAlgorithm"
,
BindingFlags
.
PutDispProperty
,
null
,
obj6
,
subject
);
this
.
typeRequestCert
.
InvokeMember
(
"Encode"
,
BindingFlags
.
InvokeMethod
,
null
,
obj6
,
null
);
object
obj15
=
Activator
.
CreateInstance
(
this
.
typeX509Enrollment
);
subject
[
0
]
=
obj6
;
this
.
typeX509Enrollment
.
InvokeMember
(
"InitializeFromRequest"
,
BindingFlags
.
InvokeMethod
,
null
,
obj15
,
subject
);
if
(
IsRoot
)
{
subject
[
0
]
=
"DO_NOT_TRUST_TitaniumProxy-CE"
;
this
.
typeX509Enrollment
.
InvokeMember
(
"CertificateFriendlyName"
,
BindingFlags
.
PutDispProperty
,
null
,
obj15
,
subject
);
}
subject
[
0
]
=
0
;
object
obj16
=
this
.
typeX509Enrollment
.
InvokeMember
(
"CreateRequest"
,
BindingFlags
.
InvokeMethod
,
null
,
obj15
,
subject
);
subject
=
new
object
[]
{
2
,
obj16
,
0
,
string
.
Empty
};
this
.
typeX509Enrollment
.
InvokeMember
(
"InstallResponse"
,
BindingFlags
.
InvokeMethod
,
null
,
obj15
,
subject
);
subject
=
new
object
[]
{
null
,
0
,
1
};
string
empty
=
string
.
Empty
;
try
{
empty
=
(
string
)
this
.
typeX509Enrollment
.
InvokeMember
(
"CreatePFX"
,
BindingFlags
.
InvokeMethod
,
null
,
obj15
,
subject
);
return
new
X509Certificate2
(
Convert
.
FromBase64String
(
empty
),
string
.
Empty
,
X509KeyStorageFlags
.
Exportable
);
}
catch
(
Exception
exception1
)
{
Exception
exception
=
exception1
;
cert
=
null
;
}
return
cert
;
}
private
X509Certificate2
MakeCertificateInternal
(
string
sSubjectCN
,
bool
isRoot
,
bool
switchToMTAIfNeeded
,
X509Certificate2
signingCert
=
null
)
{
X509Certificate2
rCert
=
null
;
if
(
switchToMTAIfNeeded
&&
Thread
.
CurrentThread
.
GetApartmentState
()
!=
ApartmentState
.
MTA
)
{
ManualResetEvent
manualResetEvent
=
new
ManualResetEvent
(
false
);
ThreadPool
.
QueueUserWorkItem
((
object
o
)
=>
{
rCert
=
this
.
MakeCertificateInternal
(
sSubjectCN
,
isRoot
,
false
,
signingCert
);
manualResetEvent
.
Set
();
});
manualResetEvent
.
WaitOne
();
manualResetEvent
.
Close
();
return
rCert
;
}
string
fullSubject
=
string
.
Format
(
"CN={0}{1}"
,
sSubjectCN
,
""
);
//Subject
string
HashAlgo
=
"SHA256"
;
//Sig Algo
int
GraceDays
=
-
366
;
//Grace Days
int
ValidDays
=
1825
;
//ValiDays
int
keyLength
=
2048
;
//KeyLength
DateTime
graceTime
=
DateTime
.
Now
.
AddDays
((
double
)
GraceDays
);
DateTime
now
=
DateTime
.
Now
;
try
{
if
(!
isRoot
)
{
rCert
=
this
.
MakeCertificate
(
false
,
sSubjectCN
,
fullSubject
,
keyLength
,
HashAlgo
,
graceTime
,
now
.
AddDays
((
double
)
ValidDays
),
signingCert
);
}
else
{
rCert
=
this
.
MakeCertificate
(
true
,
sSubjectCN
,
fullSubject
,
keyLength
,
HashAlgo
,
graceTime
,
now
.
AddDays
((
double
)
ValidDays
),
null
);
}
}
catch
(
Exception
e
)
{
throw
e
;
}
return
rCert
;
}
}
}
Titanium.Web.Proxy/Network/CertificateManager.cs
View file @
142cabcb
...
...
@@ -5,15 +5,33 @@ using System.Threading.Tasks;
using
System.Linq
;
using
System.Collections.Concurrent
;
using
System.IO
;
using
Titanium.Web.Proxy.Network.Certificate
;
using
Titanium.Web.Proxy.Helpers
;
namespace
Titanium.Web.Proxy.Network
{
/// <summary>
/// Certificate Engine option
/// </summary>
public
enum
CertificateEngine
{
/// <summary>
/// Uses Windows Certification Generation API
/// </summary>
DefaultWindows
=
0
,
/// <summary>
/// Uses BouncyCastle 3rd party library
/// </summary>
BouncyCastle
=
1
}
/// <summary>
/// A class to manage SSL certificates used by this proxy server
/// </summary>
internal
class
CertificateManager
:
IDisposable
{
private
CertificateMaker
certEngine
=
null
;
private
readonly
ICertificateMaker
certEngine
;
private
bool
clearCertificates
{
get
;
set
;
}
/// <summary>
...
...
@@ -21,18 +39,30 @@ namespace Titanium.Web.Proxy.Network
/// </summary>
private
readonly
IDictionary
<
string
,
CachedCertificate
>
certificateCache
;
private
Action
<
Exception
>
exceptionFunc
;
private
readonly
Action
<
Exception
>
exceptionFunc
;
internal
string
Issuer
{
get
;
private
set
;
}
internal
string
RootCertificateName
{
get
;
private
set
;
}
internal
string
Issuer
{
get
;
}
internal
string
RootCertificateName
{
get
;
}
internal
X509Certificate2
rootCertificate
{
get
;
set
;
}
internal
CertificateManager
(
string
issuer
,
string
rootCertificateName
,
Action
<
Exception
>
exceptionFunc
)
internal
CertificateManager
(
CertificateEngine
engine
,
string
issuer
,
string
rootCertificateName
,
Action
<
Exception
>
exceptionFunc
)
{
this
.
exceptionFunc
=
exceptionFunc
;
certEngine
=
new
CertificateMaker
();
//For Mono only Bouncy Castle is supported
if
(
RunTime
.
IsRunningOnMono
()
||
engine
==
CertificateEngine
.
BouncyCastle
)
{
certEngine
=
new
BCCertificateMaker
();
}
else
{
certEngine
=
new
WinCertificateMaker
();
}
Issuer
=
issuer
;
RootCertificateName
=
rootCertificateName
;
...
...
@@ -42,10 +72,11 @@ namespace Titanium.Web.Proxy.Network
internal
X509Certificate2
GetRootCertificate
()
{
var
fileName
=
Path
.
Combine
(
System
.
IO
.
Path
.
GetDirectoryName
(
System
.
Reflection
.
Assembly
.
GetExecutingAssembly
().
Location
),
"rootCert.pfx"
);
var
path
=
Path
.
GetDirectoryName
(
System
.
Reflection
.
Assembly
.
GetExecutingAssembly
().
Location
);
if
(
null
==
path
)
throw
new
NullReferenceException
();
var
fileName
=
Path
.
Combine
(
path
,
"rootCert.pfx"
);
if
(
File
.
Exists
(
fileName
))
{
if
(!
File
.
Exists
(
fileName
))
return
null
;
try
{
return
new
X509Certificate2
(
fileName
,
string
.
Empty
,
X509KeyStorageFlags
.
Exportable
);
...
...
@@ -57,8 +88,6 @@ namespace Titanium.Web.Proxy.Network
return
null
;
}
}
return
null
;
}
/// <summary>
/// Attempts to create a RootCertificate
/// </summary>
...
...
@@ -75,7 +104,7 @@ namespace Titanium.Web.Proxy.Network
{
rootCertificate
=
CreateCertificate
(
RootCertificateName
,
true
);
}
catch
(
Exception
e
)
catch
(
Exception
e
)
{
exceptionFunc
(
e
);
}
...
...
@@ -83,38 +112,36 @@ namespace Titanium.Web.Proxy.Network
{
try
{
var
fileName
=
Path
.
Combine
(
System
.
IO
.
Path
.
GetDirectoryName
(
System
.
Reflection
.
Assembly
.
GetExecutingAssembly
().
Location
),
"rootCert.pfx"
);
var
path
=
Path
.
GetDirectoryName
(
System
.
Reflection
.
Assembly
.
GetExecutingAssembly
().
Location
);
if
(
null
==
path
)
throw
new
NullReferenceException
();
var
fileName
=
Path
.
Combine
(
path
,
"rootCert.pfx"
);
File
.
WriteAllBytes
(
fileName
,
rootCertificate
.
Export
(
X509ContentType
.
Pkcs12
));
}
catch
(
Exception
e
)
catch
(
Exception
e
)
{
exceptionFunc
(
e
);
}
}
return
rootCertificate
!=
null
;
}
/// <summary>
/// Create an SSL certificate
/// </summary>
/// <param name="store"></param>
/// <param name="certificateName"></param>
/// <param name="isRootCertificate"></param>
/// <returns></returns>
internal
virtual
X509Certificate2
CreateCertificate
(
string
certificateName
,
bool
isRootCertificate
)
{
try
{
if
(
certificateCache
.
ContainsKey
(
certificateName
))
{
var
cached
=
certificateCache
[
certificateName
];
cached
.
LastAccess
=
DateTime
.
Now
;
return
cached
.
Certificate
;
}
}
catch
{
}
X509Certificate2
certificate
=
null
;
lock
(
string
.
Intern
(
certificateName
))
{
...
...
@@ -124,7 +151,7 @@ namespace Titanium.Web.Proxy.Network
{
certificate
=
certEngine
.
MakeCertificate
(
certificateName
,
isRootCertificate
,
rootCertificate
);
}
catch
(
Exception
e
)
catch
(
Exception
e
)
{
exceptionFunc
(
e
);
}
...
...
@@ -165,9 +192,6 @@ namespace Titanium.Web.Proxy.Network
{
clearCertificates
=
true
;
while
(
clearCertificates
)
{
try
{
var
cutOff
=
DateTime
.
Now
.
AddMinutes
(-
1
*
certificateCacheTimeOutMinutes
);
...
...
@@ -177,46 +201,52 @@ namespace Titanium.Web.Proxy.Network
foreach
(
var
cache
in
outdated
)
certificateCache
.
Remove
(
cache
.
Key
);
}
finally
{
}
//after a minute come back to check for outdated certificates in cache
await
Task
.
Delay
(
1000
*
60
);
}
}
internal
bool
TrustRootCertificate
()
/// <summary>
/// Make current machine trust the Root Certificate used by this proxy
/// </summary>
/// <param name="storeLocation"></param>
/// <param name="exceptionFunc"></param>
/// <returns></returns>
internal
void
TrustRootCertificate
(
StoreLocation
storeLocation
,
Action
<
Exception
>
exceptionFunc
)
{
if
(
rootCertificate
==
null
)
{
return
false
;
exceptionFunc
(
new
Exception
(
"Could not set root certificate"
+
" as system proxy since it is null or empty."
));
return
;
}
X509Store
x509RootStore
=
new
X509Store
(
StoreName
.
Root
,
storeLocation
);
var
x509PersonalStore
=
new
X509Store
(
StoreName
.
My
,
storeLocation
);
try
{
X509Store
x509RootStore
=
new
X509Store
(
StoreName
.
Root
,
StoreLocation
.
CurrentUser
);
var
x509PersonalStore
=
new
X509Store
(
StoreName
.
My
,
StoreLocation
.
CurrentUser
);
x509RootStore
.
Open
(
OpenFlags
.
ReadWrite
);
x509PersonalStore
.
Open
(
OpenFlags
.
ReadWrite
);
try
{
x509RootStore
.
Add
(
rootCertificate
);
x509PersonalStore
.
Add
(
rootCertificate
);
}
catch
(
Exception
e
)
{
exceptionFunc
(
new
Exception
(
"Failed to make system trust root certificate "
+
$" for
{
storeLocation
}
store location. You may need admin rights."
,
e
));
}
finally
{
x509RootStore
.
Close
();
x509PersonalStore
.
Close
();
}
return
true
;
}
catch
{
return
false
;
}
}
public
void
Dispose
()
...
...
Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs
View file @
142cabcb
...
...
@@ -47,6 +47,9 @@ namespace Titanium.Web.Proxy.Network.Tcp
LastAccess
=
DateTime
.
Now
;
}
/// <summary>
/// Dispose.
/// </summary>
public
void
Dispose
()
{
Stream
.
Close
();
...
...
Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
View file @
142cabcb
...
...
@@ -19,14 +19,12 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
internal
class
TcpConnectionFactory
{
/// <summary>
/// Creates a TCP connection to server
/// </summary>
/// <param name="bufferSize"></param>
/// <param name="connectionTimeOutSeconds"></param>
/// <param name="remoteHostName"></param>
/// <param name="httpCmd"></param>
/// <param name="httpVersion"></param>
/// <param name="isHttps"></param>
/// <param name="remotePort"></param>
...
...
@@ -57,7 +55,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
{
client
=
new
TcpClient
();
client
.
Client
.
Bind
(
upStreamEndPoint
);
client
.
Client
.
Connect
(
externalHttpsProxy
.
HostName
,
externalHttpsProxy
.
Port
);
await
client
.
ConnectAsync
(
externalHttpsProxy
.
HostName
,
externalHttpsProxy
.
Port
);
stream
=
client
.
GetStream
();
using
(
var
writer
=
new
StreamWriter
(
stream
,
Encoding
.
ASCII
,
bufferSize
,
true
)
{
NewLine
=
ProxyConstants
.
NewLine
})
...
...
@@ -69,7 +67,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
if
(!
string
.
IsNullOrEmpty
(
externalHttpsProxy
.
UserName
)
&&
externalHttpsProxy
.
Password
!=
null
)
{
await
writer
.
WriteLineAsync
(
"Proxy-Connection: keep-alive"
);
await
writer
.
WriteLineAsync
(
"Proxy-Authorization"
+
": Basic "
+
Convert
.
ToBase64String
(
System
.
Text
.
Encoding
.
UTF8
.
GetBytes
(
externalHttpsProxy
.
UserName
+
":"
+
externalHttpsProxy
.
Password
)));
await
writer
.
WriteLineAsync
(
"Proxy-Authorization"
+
": Basic "
+
Convert
.
ToBase64String
(
Encoding
.
UTF8
.
GetBytes
(
externalHttpsProxy
.
UserName
+
":"
+
externalHttpsProxy
.
Password
)));
}
await
writer
.
WriteLineAsync
();
await
writer
.
FlushAsync
();
...
...
@@ -81,7 +79,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
var
result
=
await
reader
.
ReadLineAsync
();
if
(!
new
string
[]
{
"200 OK"
,
"connection established"
}.
Any
(
s
=>
result
.
ToLower
().
Contains
(
s
.
ToLower
())))
if
(!
new
[]
{
"200 OK"
,
"connection established"
}.
Any
(
s
=>
result
.
ToLower
().
Contains
(
s
.
ToLower
())))
{
throw
new
Exception
(
"Upstream proxy failed to create a secure tunnel"
);
}
...
...
@@ -93,7 +91,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
{
client
=
new
TcpClient
();
client
.
Client
.
Bind
(
upStreamEndPoint
);
client
.
Client
.
Connect
(
remoteHostName
,
remotePort
);
await
client
.
ConnectAsync
(
remoteHostName
,
remotePort
);
stream
=
client
.
GetStream
();
}
...
...
@@ -119,14 +117,14 @@ namespace Titanium.Web.Proxy.Network.Tcp
{
client
=
new
TcpClient
();
client
.
Client
.
Bind
(
upStreamEndPoint
);
client
.
Client
.
Connect
(
externalHttpProxy
.
HostName
,
externalHttpProxy
.
Port
);
await
client
.
ConnectAsync
(
externalHttpProxy
.
HostName
,
externalHttpProxy
.
Port
);
stream
=
client
.
GetStream
();
}
else
{
client
=
new
TcpClient
();
client
.
Client
.
Bind
(
upStreamEndPoint
);
client
.
Client
.
Connect
(
remoteHostName
,
remotePort
);
await
client
.
ConnectAsync
(
remoteHostName
,
remotePort
);
stream
=
client
.
GetStream
();
}
}
...
...
Titanium.Web.Proxy/Network/Tcp/TcpRow.cs
View file @
142cabcb
...
...
@@ -29,16 +29,16 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary>
/// Gets the local end point.
/// </summary>
public
IPEndPoint
LocalEndPoint
{
get
;
private
set
;
}
public
IPEndPoint
LocalEndPoint
{
get
;
}
/// <summary>
/// Gets the remote end point.
/// </summary>
public
IPEndPoint
RemoteEndPoint
{
get
;
private
set
;
}
public
IPEndPoint
RemoteEndPoint
{
get
;
}
/// <summary>
/// Gets the process identifier.
/// </summary>
public
int
ProcessId
{
get
;
private
set
;
}
public
int
ProcessId
{
get
;
}
}
}
\ No newline at end of file
Titanium.Web.Proxy/Network/Tcp/TcpTable.cs
View file @
142cabcb
...
...
@@ -6,7 +6,9 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary>
/// Represents collection of TcpRows
/// </summary>
/// <seealso cref="System.Collections.Generic.IEnumerable{Proxy.Tcp.TcpRow}" />
/// <seealso>
/// <cref>System.Collections.Generic.IEnumerable{Proxy.Tcp.TcpRow}</cref>
/// </seealso>
internal
class
TcpTable
:
IEnumerable
<
TcpRow
>
{
private
readonly
IEnumerable
<
TcpRow
>
tcpRows
;
...
...
Titanium.Web.Proxy/ProxyAuthorizationHandler.cs
View file @
142cabcb
...
...
@@ -24,32 +24,46 @@ namespace Titanium.Web.Proxy
try
{
if
(
!
httpHeaders
.
Where
(
t
=>
t
.
Name
==
"Proxy-Authorization"
).
Any
(
))
if
(
httpHeaders
.
All
(
t
=>
t
.
Name
!=
"Proxy-Authorization"
))
{
await
WriteResponseStatus
(
new
Version
(
1
,
1
),
"407"
,
"Proxy Authentication Required"
,
clientStreamWriter
);
var
response
=
new
Response
();
response
.
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>();
response
.
ResponseHeaders
.
Add
(
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
));
response
.
ResponseHeaders
.
Add
(
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
));
var
response
=
new
Response
{
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>
{
{
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
)
},
{
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
)}
}
};
await
WriteResponseHeaders
(
clientStreamWriter
,
response
);
await
clientStreamWriter
.
WriteLineAsync
();
return
false
;
}
else
{
var
headerValue
=
httpHeaders
.
Where
(
t
=>
t
.
Name
==
"Proxy-Authorization"
).
FirstOrDefault
()
.
Value
.
Trim
();
var
header
=
httpHeaders
.
FirstOrDefault
(
t
=>
t
.
Name
==
"Proxy-Authorization"
);
if
(
null
==
header
)
throw
new
NullReferenceException
();
var
headerValue
=
header
.
Value
.
Trim
();
if
(!
headerValue
.
ToLower
().
StartsWith
(
"basic"
))
{
//Return not authorized
await
WriteResponseStatus
(
new
Version
(
1
,
1
),
"407"
,
"Proxy Authentication Invalid"
,
clientStreamWriter
);
var
response
=
new
Response
();
response
.
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>();
response
.
ResponseHeaders
.
Add
(
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
));
response
.
ResponseHeaders
.
Add
(
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
));
var
response
=
new
Response
{
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>
{
{
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
)
},
{
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
)}
}
};
await
WriteResponseHeaders
(
clientStreamWriter
,
response
);
await
clientStreamWriter
.
WriteLineAsync
();
...
...
@@ -63,10 +77,17 @@ namespace Titanium.Web.Proxy
//Return not authorized
await
WriteResponseStatus
(
new
Version
(
1
,
1
),
"407"
,
"Proxy Authentication Invalid"
,
clientStreamWriter
);
var
response
=
new
Response
();
response
.
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>();
response
.
ResponseHeaders
.
Add
(
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
));
response
.
ResponseHeaders
.
Add
(
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
));
var
response
=
new
Response
{
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>
{
{
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
)
},
{
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
)}
}
};
await
WriteResponseHeaders
(
clientStreamWriter
,
response
);
await
clientStreamWriter
.
WriteLineAsync
();
...
...
@@ -76,17 +97,20 @@ namespace Titanium.Web.Proxy
var
password
=
decoded
.
Substring
(
decoded
.
IndexOf
(
':'
)
+
1
);
return
await
AuthenticateUserFunc
(
username
,
password
).
ConfigureAwait
(
false
);
}
}
catch
(
Exception
e
)
{
ExceptionFunc
(
new
ProxyAuthorizationException
(
"Error whilst authorizing request"
,
e
,
httpHeaders
));
//Return not authorized
await
WriteResponseStatus
(
new
Version
(
1
,
1
),
"407"
,
"Proxy Authentication Invalid"
,
clientStreamWriter
);
var
response
=
new
Response
();
response
.
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>();
response
.
ResponseHeaders
.
Add
(
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
));
response
.
ResponseHeaders
.
Add
(
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
));
var
response
=
new
Response
{
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>
{
{
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
)},
{
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
)}
}
};
await
WriteResponseHeaders
(
clientStreamWriter
,
response
);
await
clientStreamWriter
.
WriteLineAsync
();
...
...
Titanium.Web.Proxy/ProxyServer.cs
View file @
142cabcb
...
...
@@ -10,6 +10,7 @@ using Titanium.Web.Proxy.Network;
using
System.Linq
;
using
System.Security.Authentication
;
using
Titanium.Web.Proxy.Network.Tcp
;
using
System.Security.Cryptography.X509Certificates
;
namespace
Titanium.Web.Proxy
{
...
...
@@ -47,14 +48,20 @@ namespace Titanium.Web.Proxy
/// <summary>
/// A object that creates tcp connection to server
/// </summary>
private
TcpConnectionFactory
tcpConnectionFactory
{
get
;
set
;
}
private
TcpConnectionFactory
tcpConnectionFactory
{
get
;
}
/// <summary>
/// Manage system proxy settings
/// </summary>
private
SystemProxyManager
systemProxySettingsManager
{
get
;
set
;
}
private
SystemProxyManager
systemProxySettingsManager
{
get
;
}
private
FireFoxProxySettingsManager
firefoxProxySettingsManager
{
get
;
set
;
}
#if !DEBUG
/// <summary>
/// Set firefox to use default system proxy
/// </summary>
private
FireFoxProxySettingsManager
firefoxProxySettingsManager
=
new
FireFoxProxySettingsManager
();
#endif
/// <summary>
/// Buffer size used throughout this proxy
...
...
@@ -81,6 +88,13 @@ namespace Titanium.Web.Proxy
/// </summary>
public
bool
TrustRootCertificate
{
get
;
set
;
}
/// <summary>
/// Select Certificate Engine
/// Optionally set to BouncyCastle
/// Mono only support BouncyCastle and it is the default
/// </summary>
public
CertificateEngine
CertificateEngine
{
get
;
set
;
}
/// <summary>
/// Does this proxy uses the HTTP protocol 100 continue behaviour strictly?
/// Broken 100 contunue implementations on server/client may cause problems if enabled
...
...
@@ -123,6 +137,16 @@ namespace Titanium.Web.Proxy
/// </summary>
public
IPEndPoint
UpStreamEndPoint
{
get
;
set
;
}
=
new
IPEndPoint
(
IPAddress
.
Any
,
0
);
/// <summary>
/// Is the proxy currently running
/// </summary>
public
bool
ProxyRunning
=>
proxyRunning
;
/// <summary>
/// Gets or sets a value indicating whether requests will be chained to upstream gateway.
/// </summary>
public
bool
ForwardToUpstreamGateway
{
get
;
set
;
}
/// <summary>
/// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication
/// </summary>
...
...
@@ -138,14 +162,8 @@ namespace Titanium.Web.Proxy
/// </summary>
public
Action
<
Exception
>
ExceptionFunc
{
get
{
return
exceptionFunc
??
defaultExceptionFunc
.
Value
;
}
set
{
exceptionFunc
=
value
;
}
get
{
return
exceptionFunc
??
defaultExceptionFunc
.
Value
;
}
set
{
exceptionFunc
=
value
;
}
}
/// <summary>
...
...
@@ -172,6 +190,7 @@ namespace Titanium.Web.Proxy
/// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTPS requests
/// return the ExternalProxy object with valid credentials
/// </summary>
public
Func
<
SessionEventArgs
,
Task
<
ExternalProxy
>>
GetCustomUpStreamHttpsProxyFunc
{
get
;
...
...
@@ -188,25 +207,21 @@ namespace Titanium.Web.Proxy
/// </summary>
public
SslProtocols
SupportedSslProtocols
{
get
;
set
;
}
=
SslProtocols
.
Tls
|
SslProtocols
.
Tls11
|
SslProtocols
.
Tls12
|
SslProtocols
.
Ssl3
;
/// <summary>
/// Is the proxy currently running
/// </summary>
public
bool
ProxyRunning
=>
proxyRunning
;
/// <summary>
/// Gets or sets a value indicating whether requests will be chained to upstream gateway.
/// </summary>
public
bool
ForwardToUpstreamGateway
{
get
;
set
;
}
/// <summary>
/// Constructor
/// </summary>
public
ProxyServer
()
:
this
(
null
,
null
)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="rootCertificateName">Name of root certificate.</param>
/// <param name="rootCertificateIssuerName">Name of root certificate issuer.</param>
public
ProxyServer
(
string
rootCertificateName
,
string
rootCertificateIssuerName
)
{
RootCertificateName
=
rootCertificateName
;
RootCertificateIssuerName
=
rootCertificateIssuerName
;
//default values
ConnectionTimeOutSeconds
=
120
;
CertificateCacheTimeOutMinutes
=
60
;
...
...
@@ -214,8 +229,9 @@ namespace Titanium.Web.Proxy
ProxyEndPoints
=
new
List
<
ProxyEndPoint
>();
tcpConnectionFactory
=
new
TcpConnectionFactory
();
systemProxySettingsManager
=
new
SystemProxyManager
();
firefoxProxySettingsManager
=
new
FireFoxProxySettingsManager
();
#if !DEBUG
new
FireFoxProxySettingsManager
();
#endif
RootCertificateName
=
RootCertificateName
??
"Titanium Root Certificate Authority"
;
RootCertificateIssuerName
=
RootCertificateIssuerName
??
"Titanium"
;
}
...
...
@@ -277,7 +293,7 @@ namespace Titanium.Web.Proxy
#if !DEBUG
firefoxProxySettingsManager
.
AddFirefox
();
#endif
Console
.
WriteLine
(
"Set endpoint at Ip {
1} and port: {2} as System HTTP Proxy"
,
endPoint
.
GetType
().
Name
,
endPoint
.
IpAddress
,
endPoint
.
Port
);
Console
.
WriteLine
(
"Set endpoint at Ip {
0} and port: {1} as System HTTP Proxy"
,
endPoint
.
IpAddress
,
endPoint
.
Port
);
}
...
...
@@ -313,7 +329,7 @@ namespace Titanium.Web.Proxy
#if !DEBUG
firefoxProxySettingsManager
.
AddFirefox
();
#endif
Console
.
WriteLine
(
"Set endpoint at Ip {
1} and port: {2} as System HTTPS Proxy"
,
endPoint
.
GetType
().
Name
,
endPoint
.
IpAddress
,
endPoint
.
Port
);
Console
.
WriteLine
(
"Set endpoint at Ip {
0} and port: {1} as System HTTPS Proxy"
,
endPoint
.
IpAddress
,
endPoint
.
Port
);
}
/// <summary>
...
...
@@ -350,14 +366,21 @@ namespace Titanium.Web.Proxy
throw
new
Exception
(
"Proxy is already running."
);
}
certificateCacheManager
=
new
CertificateManager
(
RootCertificateIssuerName
,
certificateCacheManager
=
new
CertificateManager
(
CertificateEngine
,
RootCertificateIssuerName
,
RootCertificateName
,
ExceptionFunc
);
certValidated
=
certificateCacheManager
.
CreateTrustedRootCertificate
();
if
(
TrustRootCertificate
)
{
certificateCacheManager
.
TrustRootCertificate
();
//current user
certificateCacheManager
.
TrustRootCertificate
(
StoreLocation
.
CurrentUser
,
exceptionFunc
);
//current system
certificateCacheManager
.
TrustRootCertificate
(
StoreLocation
.
LocalMachine
,
exceptionFunc
);
}
if
(
ForwardToUpstreamGateway
&&
GetCustomUpStreamHttpProxyFunc
==
null
&&
GetCustomUpStreamHttpsProxyFunc
==
null
)
...
...
@@ -461,6 +484,7 @@ namespace Titanium.Web.Proxy
/// <param name="endPoint"></param>
private
void
ValidateEndPointAsSystemProxy
(
ExplicitProxyEndPoint
endPoint
)
{
if
(
endPoint
==
null
)
throw
new
ArgumentNullException
(
nameof
(
endPoint
));
if
(
ProxyEndPoints
.
Contains
(
endPoint
)
==
false
)
{
throw
new
Exception
(
"Cannot set endPoints not added to proxy as system proxy"
);
...
...
@@ -542,6 +566,9 @@ namespace Titanium.Web.Proxy
endPoint
.
listener
.
BeginAcceptTcpClient
(
OnAcceptConnection
,
endPoint
);
}
/// <summary>
/// Dispose Proxy.
/// </summary>
public
void
Dispose
()
{
if
(
proxyRunning
)
...
...
@@ -551,5 +578,45 @@ namespace Titanium.Web.Proxy
certificateCacheManager
?.
Dispose
();
}
/// <summary>
/// Invocator for BeforeRequest event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected
virtual
void
OnBeforeRequest
(
object
sender
,
SessionEventArgs
e
)
{
BeforeRequest
?.
Invoke
(
sender
,
e
);
}
/// <summary>
/// Invocator for BeforeResponse event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <returns></returns>
protected
virtual
void
OnBeforeResponse
(
object
sender
,
SessionEventArgs
e
)
{
BeforeResponse
?.
Invoke
(
sender
,
e
);
}
/// <summary>
/// Invocator for ServerCertificateValidationCallback event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected
virtual
void
OnServerCertificateValidationCallback
(
object
sender
,
CertificateValidationEventArgs
e
)
{
ServerCertificateValidationCallback
?.
Invoke
(
sender
,
e
);
}
/// <summary>
/// Invocator for ClientCertifcateSelectionCallback event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected
virtual
void
OnClientCertificateSelectionCallback
(
object
sender
,
CertificateSelectionEventArgs
e
)
{
ClientCertificateSelectionCallback
?.
Invoke
(
sender
,
e
);
}
}
}
\ No newline at end of file
Titanium.Web.Proxy/RequestHandler.cs
View file @
142cabcb
...
...
@@ -11,7 +11,6 @@ using Titanium.Web.Proxy.Exceptions;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Models
;
using
System.Security.Cryptography.X509Certificates
;
using
Titanium.Web.Proxy.Shared
;
using
Titanium.Web.Proxy.Http
;
using
System.Threading.Tasks
;
...
...
@@ -31,7 +30,6 @@ namespace Titanium.Web.Proxy
private
async
Task
HandleClient
(
ExplicitProxyEndPoint
endPoint
,
TcpClient
tcpClient
)
{
Stream
clientStream
=
tcpClient
.
GetStream
();
clientStream
.
ReadTimeout
=
ConnectionTimeOutSeconds
*
1000
;
...
...
@@ -58,30 +56,24 @@ namespace Titanium.Web.Proxy
//Find the request Verb
var
httpVerb
=
httpCmdSplit
[
0
];
if
(
httpVerb
.
ToUpper
()
==
"CONNECT"
)
{
httpRemoteUri
=
new
Uri
(
"http://"
+
httpCmdSplit
[
1
]);
}
else
{
httpRemoteUri
=
new
Uri
(
httpCmdSplit
[
1
]);
}
httpRemoteUri
=
httpVerb
.
ToUpper
()
==
"CONNECT"
?
new
Uri
(
"http://"
+
httpCmdSplit
[
1
])
:
new
Uri
(
httpCmdSplit
[
1
]);
//parse the HTTP version
Version
version
=
new
Version
(
1
,
1
);
var
version
=
new
Version
(
1
,
1
);
if
(
httpCmdSplit
.
Length
==
3
)
{
string
httpVersion
=
httpCmdSplit
[
2
].
Trim
();
var
httpVersion
=
httpCmdSplit
[
2
].
Trim
();
if
(
httpVersion
==
"http/1.0"
)
if
(
0
==
string
.
CompareOrdinal
(
httpVersion
,
"http/1.0"
)
)
{
version
=
new
Version
(
1
,
0
);
}
}
//filter out excluded host names
var
excluded
=
endPoint
.
ExcludedHttpsHostNameRegex
!=
null
?
endPoint
.
ExcludedHttpsHostNameRegex
.
Any
(
x
=>
Regex
.
IsMatch
(
httpRemoteUri
.
Host
,
x
))
:
false
;
//filter out excluded host names
var
excluded
=
endPoint
.
ExcludedHttpsHostNameRegex
!=
null
&&
endPoint
.
ExcludedHttpsHostNameRegex
.
Any
(
x
=>
Regex
.
IsMatch
(
httpRemoteUri
.
Host
,
x
));
List
<
HttpHeader
>
connectRequestHeaders
=
null
;
...
...
@@ -89,7 +81,7 @@ namespace Titanium.Web.Proxy
if
(
httpVerb
.
ToUpper
()
==
"CONNECT"
&&
!
excluded
&&
httpRemoteUri
.
Port
!=
80
)
{
httpRemoteUri
=
new
Uri
(
"https://"
+
httpCmdSplit
[
1
]);
string
tmpLine
=
null
;
string
tmpLine
;
connectRequestHeaders
=
new
List
<
HttpHeader
>();
while
(!
string
.
IsNullOrEmpty
(
tmpLine
=
await
clientStreamReader
.
ReadLineAsync
()))
{
...
...
@@ -114,15 +106,7 @@ namespace Titanium.Web.Proxy
sslStream
=
new
SslStream
(
clientStream
,
true
);
X509Certificate2
certificate
;
if
(
endPoint
.
GenericCertificate
!=
null
)
{
certificate
=
endPoint
.
GenericCertificate
;
}
else
{
certificate
=
certificateCacheManager
.
CreateCertificate
(
httpRemoteUri
.
Host
,
false
);
}
var
certificate
=
endPoint
.
GenericCertificate
??
certificateCacheManager
.
CreateCertificate
(
httpRemoteUri
.
Host
,
false
);
//Successfully managed to authenticate the client using the fake certificate
await
sslStream
.
AuthenticateAsServerAsync
(
certificate
,
false
,
...
...
@@ -136,10 +120,7 @@ namespace Titanium.Web.Proxy
}
catch
{
if
(
sslStream
!=
null
)
{
sslStream
.
Dispose
();
}
sslStream
?.
Dispose
();
Dispose
(
clientStream
,
clientStreamReader
,
clientStreamWriter
,
null
);
return
;
...
...
@@ -160,8 +141,8 @@ namespace Titanium.Web.Proxy
await
TcpHelper
.
SendRaw
(
BUFFER_SIZE
,
ConnectionTimeOutSeconds
,
httpRemoteUri
.
Host
,
httpRemoteUri
.
Port
,
httpCmd
,
version
,
null
,
false
,
SupportedSslProtocols
,
new
RemoteCertificateValidationCallback
(
ValidateServerCertificate
)
,
new
LocalCertificateSelectionCallback
(
SelectClientCertificate
)
,
ValidateServerCertificate
,
SelectClientCertificate
,
clientStream
,
tcpConnectionFactory
,
UpStreamEndPoint
);
Dispose
(
clientStream
,
clientStreamReader
,
clientStreamWriter
,
null
);
...
...
@@ -169,7 +150,7 @@ namespace Titanium.Web.Proxy
}
//Now create the request
await
HandleHttpSessionRequest
(
tcpClient
,
httpCmd
,
clientStream
,
clientStreamReader
,
clientStreamWriter
,
httpRemoteUri
.
Scheme
==
Uri
.
UriSchemeHttps
?
httpRemoteUri
.
Host
:
null
,
endPoint
,
connectRequestHeaders
,
null
,
null
);
httpRemoteUri
.
Scheme
==
Uri
.
UriSchemeHttps
?
httpRemoteUri
.
Host
:
null
,
endPoint
,
connectRequestHeaders
);
}
catch
(
Exception
)
{
...
...
@@ -189,14 +170,13 @@ namespace Titanium.Web.Proxy
CustomBinaryReader
clientStreamReader
=
null
;
StreamWriter
clientStreamWriter
=
null
;
X509Certificate2
certificate
=
null
;
if
(
endPoint
.
EnableSsl
)
{
var
sslStream
=
new
SslStream
(
clientStream
,
true
);
//implement in future once SNI supported by SSL stream, for now use the same certificate
certificate
=
certificateCacheManager
.
CreateCertificate
(
endPoint
.
GenericCertificateName
,
false
);
var
certificate
=
certificateCacheManager
.
CreateCertificate
(
endPoint
.
GenericCertificateName
,
false
);
try
{
...
...
@@ -259,8 +239,8 @@ namespace Titanium.Web.Proxy
connection
=
await
tcpConnectionFactory
.
CreateClient
(
BUFFER_SIZE
,
ConnectionTimeOutSeconds
,
args
.
WebSession
.
Request
.
RequestUri
.
Host
,
args
.
WebSession
.
Request
.
RequestUri
.
Port
,
args
.
WebSession
.
Request
.
HttpVersion
,
args
.
IsHttps
,
SupportedSslProtocols
,
new
RemoteCertificateValidationCallback
(
ValidateServerCertificate
)
,
new
LocalCertificateSelectionCallback
(
SelectClientCertificate
)
,
ValidateServerCertificate
,
SelectClientCertificate
,
customUpStreamHttpProxy
??
UpStreamHttpProxy
,
customUpStreamHttpsProxy
??
UpStreamHttpsProxy
,
args
.
ProxyClient
.
ClientStream
,
UpStreamEndPoint
);
}
...
...
@@ -367,9 +347,14 @@ namespace Titanium.Web.Proxy
/// <param name="clientStreamReader"></param>
/// <param name="clientStreamWriter"></param>
/// <param name="httpsHostName"></param>
/// <param name="endPoint"></param>
/// <param name="connectHeaders"></param>
/// <param name="customUpStreamHttpProxy"></param>
/// <param name="customUpStreamHttpsProxy"></param>
/// <returns></returns>
private
async
Task
HandleHttpSessionRequest
(
TcpClient
client
,
string
httpCmd
,
Stream
clientStream
,
CustomBinaryReader
clientStreamReader
,
StreamWriter
clientStreamWriter
,
string
httpsHostName
,
ProxyEndPoint
endPoint
,
List
<
HttpHeader
>
connectHeaders
,
ExternalProxy
customUpStreamHttpProxy
=
null
,
ExternalProxy
customUpStreamHttpsProxy
=
null
)
CustomBinaryReader
clientStreamReader
,
StreamWriter
clientStreamWriter
,
string
httpsHostName
,
ProxyEndPoint
endPoint
,
List
<
HttpHeader
>
connectHeaders
,
ExternalProxy
customUpStreamHttpProxy
=
null
,
ExternalProxy
customUpStreamHttpsProxy
=
null
)
{
TcpConnection
connection
=
null
;
...
...
@@ -383,9 +368,12 @@ namespace Titanium.Web.Proxy
break
;
}
var
args
=
new
SessionEventArgs
(
BUFFER_SIZE
,
HandleHttpSessionResponse
);
args
.
ProxyClient
.
TcpClient
=
client
;
args
.
WebSession
.
ConnectHeaders
=
connectHeaders
;
var
args
=
new
SessionEventArgs
(
BUFFER_SIZE
,
HandleHttpSessionResponse
)
{
ProxyClient
=
{
TcpClient
=
client
},
WebSession
=
{
ConnectHeaders
=
connectHeaders
}
};
args
.
WebSession
.
ProcessId
=
new
Lazy
<
int
>(()
=>
{
...
...
@@ -409,12 +397,12 @@ namespace Titanium.Web.Proxy
var
httpMethod
=
httpCmdSplit
[
0
];
//find the request HTTP version
Version
httpVersion
=
new
Version
(
1
,
1
);
var
httpVersion
=
new
Version
(
1
,
1
);
if
(
httpCmdSplit
.
Length
==
3
)
{
var
httpVersionString
=
httpCmdSplit
[
2
].
ToLower
().
Trim
();
if
(
httpVersionString
==
"http/1.0"
)
if
(
0
==
string
.
CompareOrdinal
(
httpVersionString
,
"http/1.0"
)
)
{
httpVersion
=
new
Version
(
1
,
0
);
}
...
...
@@ -439,10 +427,8 @@ namespace Titanium.Web.Proxy
{
var
existing
=
args
.
WebSession
.
Request
.
RequestHeaders
[
newHeader
.
Name
];
var
nonUniqueHeaders
=
new
List
<
HttpHeader
>
()
;
var
nonUniqueHeaders
=
new
List
<
HttpHeader
>
{
existing
,
newHeader
}
;
nonUniqueHeaders
.
Add
(
existing
);
nonUniqueHeaders
.
Add
(
newHeader
);
args
.
WebSession
.
Request
.
NonUniqueRequestHeaders
.
Add
(
newHeader
.
Name
,
nonUniqueHeaders
);
args
.
WebSession
.
Request
.
RequestHeaders
.
Remove
(
newHeader
.
Name
);
...
...
@@ -455,8 +441,7 @@ namespace Titanium.Web.Proxy
}
var
httpRemoteUri
=
new
Uri
(
httpsHostName
==
null
?
httpCmdSplit
[
1
]
:
(
string
.
Concat
(
"https://"
,
args
.
WebSession
.
Request
.
Host
==
null
?
httpsHostName
:
args
.
WebSession
.
Request
.
Host
,
httpCmdSplit
[
1
])));
:
(
string
.
Concat
(
"https://"
,
args
.
WebSession
.
Request
.
Host
??
httpsHostName
,
httpCmdSplit
[
1
])));
args
.
WebSession
.
Request
.
RequestUri
=
httpRemoteUri
;
...
...
@@ -479,10 +464,10 @@ namespace Titanium.Web.Proxy
//If user requested interception do it
if
(
BeforeRequest
!=
null
)
{
Delegate
[]
invocationList
=
BeforeRequest
.
GetInvocationList
();
Task
[]
handlerTasks
=
new
Task
[
invocationList
.
Length
];
var
invocationList
=
BeforeRequest
.
GetInvocationList
();
var
handlerTasks
=
new
Task
[
invocationList
.
Length
];
for
(
int
i
=
0
;
i
<
invocationList
.
Length
;
i
++)
for
(
var
i
=
0
;
i
<
invocationList
.
Length
;
i
++)
{
handlerTasks
[
i
]
=
((
Func
<
object
,
SessionEventArgs
,
Task
>)
invocationList
[
i
])(
null
,
args
);
}
...
...
@@ -495,8 +480,8 @@ namespace Titanium.Web.Proxy
{
await
TcpHelper
.
SendRaw
(
BUFFER_SIZE
,
ConnectionTimeOutSeconds
,
httpRemoteUri
.
Host
,
httpRemoteUri
.
Port
,
httpCmd
,
httpVersion
,
args
.
WebSession
.
Request
.
RequestHeaders
,
args
.
IsHttps
,
SupportedSslProtocols
,
new
RemoteCertificateValidationCallback
(
ValidateServerCertificate
)
,
new
LocalCertificateSelectionCallback
(
SelectClientCertificate
)
,
SupportedSslProtocols
,
ValidateServerCertificate
,
SelectClientCertificate
,
clientStream
,
tcpConnectionFactory
,
UpStreamEndPoint
);
Dispose
(
clientStream
,
clientStreamReader
,
clientStreamWriter
,
args
);
...
...
@@ -504,7 +489,7 @@ namespace Titanium.Web.Proxy
}
//construct the web request that we are going to issue on behalf of the client.
await
HandleHttpSessionRequestInternal
(
connection
,
args
,
customUpStreamHttpProxy
,
customUpStreamHttpsProxy
,
false
).
ConfigureAwait
(
false
);
await
HandleHttpSessionRequestInternal
(
null
,
args
,
customUpStreamHttpProxy
,
customUpStreamHttpsProxy
,
false
).
ConfigureAwait
(
false
);
if
(
args
.
WebSession
.
Request
.
CancelRequest
)
...
...
@@ -531,12 +516,8 @@ namespace Titanium.Web.Proxy
}
if
(
connection
!=
null
)
{
//dispose
connection
.
Dispose
();
}
connection
?.
Dispose
();
}
/// <summary>
...
...
@@ -547,8 +528,9 @@ namespace Titanium.Web.Proxy
/// <returns></returns>
private
async
Task
WriteConnectResponse
(
StreamWriter
clientStreamWriter
,
Version
httpVersion
)
{
await
clientStreamWriter
.
WriteLineAsync
(
string
.
Format
(
"HTTP/{0}.{1} {2}"
,
httpVersion
.
Major
,
httpVersion
.
Minor
,
"200 Connection established"
));
await
clientStreamWriter
.
WriteLineAsync
(
string
.
Format
(
"Timestamp: {0}"
,
DateTime
.
Now
));
await
clientStreamWriter
.
WriteLineAsync
(
$"HTTP/
{
httpVersion
.
Major
}
.
{
httpVersion
.
Minor
}
200 Connection established"
);
await
clientStreamWriter
.
WriteLineAsync
(
$"Timestamp:
{
DateTime
.
Now
}
"
);
await
clientStreamWriter
.
WriteLineAsync
();
await
clientStreamWriter
.
FlushAsync
();
}
...
...
@@ -568,10 +550,7 @@ namespace Titanium.Web.Proxy
{
//these are the only encoding this proxy can read
case
"accept-encoding"
:
header
.
Value
=
"gzip,deflate,zlib"
;
break
;
default
:
header
.
Value
=
"gzip,deflate"
;
break
;
}
}
...
...
Titanium.Web.Proxy/ResponseHandler.cs
View file @
142cabcb
...
...
@@ -17,7 +17,11 @@ namespace Titanium.Web.Proxy
/// </summary>
partial
class
ProxyServer
{
//Called asynchronously when a request was successfully and we received the response
/// <summary>
/// Called asynchronously when a request was successfully and we received the response
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public
async
Task
HandleHttpSessionResponse
(
SessionEventArgs
args
)
{
//read response & headers from server
...
...
@@ -156,14 +160,14 @@ namespace Titanium.Web.Proxy
private
async
Task
WriteResponseStatus
(
Version
version
,
string
code
,
string
description
,
StreamWriter
responseWriter
)
{
await
responseWriter
.
WriteLineAsync
(
string
.
Format
(
"HTTP/{0}.{1} {2} {3}"
,
version
.
Major
,
version
.
Minor
,
code
,
description
)
);
await
responseWriter
.
WriteLineAsync
(
$"HTTP/
{
version
.
Major
}
.
{
version
.
Minor
}
{
code
}
{
description
}
"
);
}
/// <summary>
/// Write response headers to client
/// </summary>
/// <param name="responseWriter"></param>
/// <param name="
headers
"></param>
/// <param name="
response
"></param>
/// <returns></returns>
private
async
Task
WriteResponseHeaders
(
StreamWriter
responseWriter
,
Response
response
)
{
...
...
@@ -220,7 +224,6 @@ namespace Titanium.Web.Proxy
/// <summary>
/// Handle dispose of a client/server session
/// </summary>
/// <param name="tcpClient"></param>
/// <param name="clientStream"></param>
/// <param name="clientStreamReader"></param>
/// <param name="clientStreamWriter"></param>
...
...
@@ -235,21 +238,13 @@ namespace Titanium.Web.Proxy
clientStream
.
Dispose
();
}
if
(
args
!=
null
)
{
args
.
Dispose
();
}
args
?.
Dispose
();
if
(
clientStreamReader
!=
null
)
{
clientStreamReader
.
Dispose
();
}
clientStreamReader
?.
Dispose
();
if
(
clientStreamWriter
!=
null
)
{
if
(
clientStreamWriter
==
null
)
return
;
clientStreamWriter
.
Close
();
clientStreamWriter
.
Dispose
();
}
}
}
}
\ No newline at end of file
Titanium.Web.Proxy/Shared/ProxyConstants.cs
View file @
142cabcb
using
System
;
using
System.Text
;
using
System.Text
;
namespace
Titanium.Web.Proxy.Shared
{
...
...
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
View file @
142cabcb
...
...
@@ -34,6 +34,8 @@
<DefineConstants>
NET45
</DefineConstants>
<Prefer32Bit>
false
</Prefer32Bit>
<DocumentationFile>
bin\Release\Titanium.Web.Proxy.XML
</DocumentationFile>
<DebugType>
none
</DebugType>
<DebugSymbols>
false
</DebugSymbols>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>
true
</SignAssembly>
...
...
@@ -42,8 +44,8 @@
<AssemblyOriginatorKeyFile>
StrongNameKey.snk
</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference
Include=
"
Ionic.Zip, Version=1.9.8.0, Culture=neutral, PublicKeyToken=6583c7c814667745, processorArchitecture=MSIL
"
>
<HintPath>
..\packages\
DotNetZip.1.9.8\lib\net20\Ionic.Zip
.dll
</HintPath>
<Reference
Include=
"
BouncyCastle.Crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942
"
>
<HintPath>
..\packages\
BouncyCastle.1.8.1\lib\BouncyCastle.Crypto
.dll
</HintPath>
<Private>
True
</Private>
</Reference>
<Reference
Include=
"System"
/>
...
...
@@ -62,20 +64,21 @@
<Compile
Include=
"Compression\DeflateCompression.cs"
/>
<Compile
Include=
"Compression\GZipCompression.cs"
/>
<Compile
Include=
"Compression\ICompression.cs"
/>
<Compile
Include=
"Compression\ZlibCompression.cs"
/>
<Compile
Include=
"Decompression\DecompressionFactory.cs"
/>
<Compile
Include=
"Decompression\DefaultDecompression.cs"
/>
<Compile
Include=
"Decompression\DeflateDecompression.cs"
/>
<Compile
Include=
"Decompression\GZipDecompression.cs"
/>
<Compile
Include=
"Decompression\IDecompression.cs"
/>
<Compile
Include=
"Decompression\ZlibDecompression.cs"
/>
<Compile
Include=
"EventArguments\CertificateSelectionEventArgs.cs"
/>
<Compile
Include=
"EventArguments\CertificateValidationEventArgs.cs"
/>
<Compile
Include=
"Extensions\ByteArrayExtensions.cs"
/>
<Compile
Include=
"Helpers\Network.cs"
/>
<Compile
Include=
"Helpers\RunTime.cs"
/>
<Compile
Include=
"Http\Responses\GenericResponse.cs"
/>
<Compile
Include=
"Network\CachedCertificate.cs"
/>
<Compile
Include=
"Network\CertificateMaker.cs"
/>
<Compile
Include=
"Network\Certificate\WinCertificateMaker.cs"
/>
<Compile
Include=
"Network\Certificate\BCCertificateMaker.cs"
/>
<Compile
Include=
"Network\Certificate\ICertificateMaker.cs"
/>
<Compile
Include=
"Network\ProxyClient.cs"
/>
<Compile
Include=
"Exceptions\BodyNotFoundException.cs"
/>
<Compile
Include=
"Exceptions\ProxyAuthorizationException.cs"
/>
...
...
Titanium.Web.Proxy/Titanium.Web.Proxy.nuspec
View file @
142cabcb
...
...
@@ -14,7 +14,7 @@
<copyright>
Copyright
©
Titanium. All rights reserved.
</copyright>
<tags></tags>
<dependencies>
<dependency
id=
"
DotNetZip"
version=
"1.9.8
"
/>
<dependency
id=
"
BouncyCastle"
version=
"1.8.1
"
/>
</dependencies>
</metadata>
<files>
...
...
Titanium.Web.Proxy/packages.config
View file @
142cabcb
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
packages
>
<
package
id
=
"
DotNetZip"
version
=
"1.9.8
"
targetFramework
=
"net45"
/>
<
package
id
=
"
BouncyCastle"
version
=
"1.8.1
"
targetFramework
=
"net45"
/>
</
packages
>
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment