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
75e13cd1
Commit
75e13cd1
authored
Jun 16, 2016
by
justcoding121
Committed by
justcoding121
Jun 16, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add comments
parent
e0bdf695
Hide whitespace changes
Inline
Side-by-side
Showing
34 changed files
with
680 additions
and
345 deletions
+680
-345
ProxyTestController.cs
.../Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
+4
-4
CompressionFactory.cs
Titanium.Web.Proxy/Compression/CompressionFactory.cs
+4
-1
DeflateCompression.cs
Titanium.Web.Proxy/Compression/DeflateCompression.cs
+4
-1
GZipCompression.cs
Titanium.Web.Proxy/Compression/GZipCompression.cs
+4
-1
ICompression.cs
Titanium.Web.Proxy/Compression/ICompression.cs
+3
-0
ZlibCompression.cs
Titanium.Web.Proxy/Compression/ZlibCompression.cs
+4
-1
DecompressionFactory.cs
Titanium.Web.Proxy/Decompression/DecompressionFactory.cs
+3
-0
DefaultDecompression.cs
Titanium.Web.Proxy/Decompression/DefaultDecompression.cs
+4
-0
DeflateDecompression.cs
Titanium.Web.Proxy/Decompression/DeflateDecompression.cs
+4
-1
GZipDecompression.cs
Titanium.Web.Proxy/Decompression/GZipDecompression.cs
+4
-1
IDecompression.cs
Titanium.Web.Proxy/Decompression/IDecompression.cs
+4
-2
ZlibDecompression.cs
Titanium.Web.Proxy/Decompression/ZlibDecompression.cs
+4
-1
CertificateSelectionEventArgs.cs
...Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs
+3
-0
CertificateValidationEventArgs.cs
...eb.Proxy/EventArguments/CertificateValidationEventArgs.cs
+3
-0
SessionEventArgs.cs
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+0
-1
ByteArrayExtensions.cs
Titanium.Web.Proxy/Extensions/ByteArrayExtensions.cs
+0
-4
StreamExtensions.cs
Titanium.Web.Proxy/Extensions/StreamExtensions.cs
+5
-3
CustomBinaryReader.cs
Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs
+16
-3
SystemProxy.cs
Titanium.Web.Proxy/Helpers/SystemProxy.cs
+40
-7
Tcp.cs
Titanium.Web.Proxy/Helpers/Tcp.cs
+15
-1
HttpWebClient.cs
Titanium.Web.Proxy/Http/HttpWebClient.cs
+22
-2
Request.cs
Titanium.Web.Proxy/Http/Request.cs
+3
-0
Response.cs
Titanium.Web.Proxy/Http/Response.cs
+3
-1
OkResponse.cs
Titanium.Web.Proxy/Http/Responses/OkResponse.cs
+4
-3
RedirectResponse.cs
Titanium.Web.Proxy/Http/Responses/RedirectResponse.cs
+3
-0
EndPoint.cs
Titanium.Web.Proxy/Models/EndPoint.cs
+11
-0
ExternalProxy.cs
Titanium.Web.Proxy/Models/ExternalProxy.cs
+4
-8
CachedCertificate.cs
Titanium.Web.Proxy/Network/CachedCertificate.cs
+25
-0
CertificateManager.cs
Titanium.Web.Proxy/Network/CertificateManager.cs
+290
-263
TcpConnection.cs
Titanium.Web.Proxy/Network/TcpConnection.cs
+45
-0
TcpConnectionManager.cs
Titanium.Web.Proxy/Network/TcpConnectionManager.cs
+50
-24
ProxyServer.cs
Titanium.Web.Proxy/ProxyServer.cs
+86
-8
RequestHandler.cs
Titanium.Web.Proxy/RequestHandler.cs
+3
-3
Titanium.Web.Proxy.csproj
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
+3
-1
No files found.
Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
View file @
75e13cd1
...
...
@@ -12,10 +12,10 @@ namespace Titanium.Web.Proxy.Examples.Basic
{
public
void
StartProxy
()
{
ProxyServer
.
BeforeRequest
+=
OnRequest
;
ProxyServer
.
BeforeResponse
+=
OnResponse
;
ProxyServer
.
ServerCertificateValidationCallback
+=
OnCertificateValidation
;
ProxyServer
.
ClientCertificateSelectionCallback
+=
OnCertificateSelection
;
//
ProxyServer.BeforeRequest += OnRequest;
//
ProxyServer.BeforeResponse += OnResponse;
//
ProxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
//
ProxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
//Exclude Https addresses you don't want to proxy
//Usefull for clients that use certificate pinning
...
...
Titanium.Web.Proxy/Compression/CompressionFactory.cs
View file @
75e13cd1
namespace
Titanium.Web.Proxy.Compression
{
class
CompressionFactory
/// <summary>
/// A factory to generate the compression methods based on the type of compression
/// </summary>
internal
class
CompressionFactory
{
public
ICompression
Create
(
string
type
)
{
...
...
Titanium.Web.Proxy/Compression/DeflateCompression.cs
View file @
75e13cd1
...
...
@@ -4,7 +4,10 @@ using System.Threading.Tasks;
namespace
Titanium.Web.Proxy.Compression
{
class
DeflateCompression
:
ICompression
/// <summary>
/// Concrete implementation of deflate compression
/// </summary>
internal
class
DeflateCompression
:
ICompression
{
public
async
Task
<
byte
[
]>
Compress
(
byte
[]
responseBody
)
{
...
...
Titanium.Web.Proxy/Compression/GZipCompression.cs
View file @
75e13cd1
...
...
@@ -4,7 +4,10 @@ using System.Threading.Tasks;
namespace
Titanium.Web.Proxy.Compression
{
class
GZipCompression
:
ICompression
/// <summary>
/// concreate implementation of gzip compression
/// </summary>
internal
class
GZipCompression
:
ICompression
{
public
async
Task
<
byte
[
]>
Compress
(
byte
[]
responseBody
)
{
...
...
Titanium.Web.Proxy/Compression/ICompression.cs
View file @
75e13cd1
...
...
@@ -2,6 +2,9 @@
namespace
Titanium.Web.Proxy.Compression
{
/// <summary>
/// An inteface for http compression
/// </summary>
interface
ICompression
{
Task
<
byte
[
]>
Compress
(
byte
[]
responseBody
);
...
...
Titanium.Web.Proxy/Compression/ZlibCompression.cs
View file @
75e13cd1
...
...
@@ -4,7 +4,10 @@ using System.Threading.Tasks;
namespace
Titanium.Web.Proxy.Compression
{
class
ZlibCompression
:
ICompression
/// <summary>
/// concrete implementation of zlib compression
/// </summary>
internal
class
ZlibCompression
:
ICompression
{
public
async
Task
<
byte
[
]>
Compress
(
byte
[]
responseBody
)
{
...
...
Titanium.Web.Proxy/Decompression/DecompressionFactory.cs
View file @
75e13cd1
namespace
Titanium.Web.Proxy.Decompression
{
/// <summary>
/// A factory to generate the de-compression methods based on the type of compression
/// </summary>
internal
class
DecompressionFactory
{
internal
IDecompression
Create
(
string
type
)
...
...
Titanium.Web.Proxy/Decompression/DefaultDecompression.cs
View file @
75e13cd1
...
...
@@ -2,6 +2,10 @@
namespace
Titanium.Web.Proxy.Decompression
{
/// <summary>
/// When no compression is specified just return the byte array
/// </summary>
internal
class
DefaultDecompression
:
IDecompression
{
public
Task
<
byte
[
]>
Decompress
(
byte
[]
compressedArray
)
...
...
Titanium.Web.Proxy/Decompression/DeflateDecompression.cs
View file @
75e13cd1
...
...
@@ -5,6 +5,9 @@ using Titanium.Web.Proxy.Shared;
namespace
Titanium.Web.Proxy.Decompression
{
/// <summary>
/// concrete implementation of deflate de-compression
/// </summary>
internal
class
DeflateDecompression
:
IDecompression
{
public
async
Task
<
byte
[
]>
Decompress
(
byte
[]
compressedArray
)
...
...
@@ -18,7 +21,7 @@ namespace Titanium.Web.Proxy.Decompression
using
(
var
output
=
new
MemoryStream
())
{
int
read
;
while
((
read
=
await
decompressor
.
ReadAsync
(
buffer
,
0
,
buffer
.
Length
)
.
ConfigureAwait
(
false
)
)
>
0
)
while
((
read
=
await
decompressor
.
ReadAsync
(
buffer
,
0
,
buffer
.
Length
))
>
0
)
{
await
output
.
WriteAsync
(
buffer
,
0
,
read
);
}
...
...
Titanium.Web.Proxy/Decompression/GZipDecompression.cs
View file @
75e13cd1
...
...
@@ -5,6 +5,9 @@ using Titanium.Web.Proxy.Shared;
namespace
Titanium.Web.Proxy.Decompression
{
/// <summary>
/// concrete implementation of gzip de-compression
/// </summary>
internal
class
GZipDecompression
:
IDecompression
{
public
async
Task
<
byte
[
]>
Decompress
(
byte
[]
compressedArray
)
...
...
@@ -15,7 +18,7 @@ namespace Titanium.Web.Proxy.Decompression
using
(
var
output
=
new
MemoryStream
())
{
int
read
;
while
((
read
=
await
decompressor
.
ReadAsync
(
buffer
,
0
,
buffer
.
Length
)
.
ConfigureAwait
(
false
)
)
>
0
)
while
((
read
=
await
decompressor
.
ReadAsync
(
buffer
,
0
,
buffer
.
Length
))
>
0
)
{
await
output
.
WriteAsync
(
buffer
,
0
,
read
);
}
...
...
Titanium.Web.Proxy/Decompression/IDecompression.cs
View file @
75e13cd1
using
System.IO
;
using
System.Threading.Tasks
;
using
System.Threading.Tasks
;
namespace
Titanium.Web.Proxy.Decompression
{
/// <summary>
/// An interface for decompression
/// </summary>
internal
interface
IDecompression
{
Task
<
byte
[
]>
Decompress
(
byte
[]
compressedArray
);
...
...
Titanium.Web.Proxy/Decompression/ZlibDecompression.cs
View file @
75e13cd1
...
...
@@ -5,6 +5,9 @@ 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
)
...
...
@@ -17,7 +20,7 @@ namespace Titanium.Web.Proxy.Decompression
using
(
var
output
=
new
MemoryStream
())
{
int
read
;
while
((
read
=
await
decompressor
.
ReadAsync
(
buffer
,
0
,
buffer
.
Length
)
.
ConfigureAwait
(
false
)
)
>
0
)
while
((
read
=
await
decompressor
.
ReadAsync
(
buffer
,
0
,
buffer
.
Length
))
>
0
)
{
await
output
.
WriteAsync
(
buffer
,
0
,
read
);
}
...
...
Titanium.Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs
View file @
75e13cd1
...
...
@@ -3,6 +3,9 @@ using System.Security.Cryptography.X509Certificates;
namespace
Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// An argument passed on to user for client certificate selection during mutual SSL authentication
/// </summary>
public
class
CertificateSelectionEventArgs
:
EventArgs
,
IDisposable
{
public
object
sender
{
get
;
internal
set
;
}
...
...
Titanium.Web.Proxy/EventArguments/CertificateValidationEventArgs.cs
View file @
75e13cd1
...
...
@@ -4,6 +4,9 @@ using System.Security.Cryptography.X509Certificates;
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
X509Certificate
Certificate
{
get
;
internal
set
;
}
...
...
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
View file @
75e13cd1
...
...
@@ -9,7 +9,6 @@ using Titanium.Web.Proxy.Extensions;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Network
;
using
System.Net
;
using
System.Net.Sockets
;
namespace
Titanium.Web.Proxy.EventArguments
{
...
...
Titanium.Web.Proxy/Extensions/ByteArrayExtensions.cs
View file @
75e13cd1
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Threading.Tasks
;
namespace
Titanium.Web.Proxy.Extensions
{
...
...
Titanium.Web.Proxy/Extensions/StreamExtensions.cs
View file @
75e13cd1
using
System
;
using
System.Globalization
;
using
System.Globalization
;
using
System.IO
;
using
System.Text
;
using
System.Threading.Tasks
;
...
...
@@ -8,6 +7,9 @@ using Titanium.Web.Proxy.Shared;
namespace
Titanium.Web.Proxy.Extensions
{
/// <summary>
/// Extensions used for Stream and CustomBinaryReader objects
/// </summary>
internal
static
class
StreamExtensions
{
/// <summary>
...
...
@@ -134,7 +136,7 @@ namespace Titanium.Web.Proxy.Extensions
var
bytesRead
=
0
;
var
totalBytesRead
=
0
;
while
((
bytesRead
+=
await
inStreamReader
.
BaseStream
.
ReadAsync
(
buffer
,
0
,
bytesToRead
)
.
ConfigureAwait
(
false
)
)
>
0
)
while
((
bytesRead
+=
await
inStreamReader
.
BaseStream
.
ReadAsync
(
buffer
,
0
,
bytesToRead
))
>
0
)
{
await
outStream
.
WriteAsync
(
buffer
,
0
,
bytesRead
);
totalBytesRead
+=
bytesRead
;
...
...
Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs
View file @
75e13cd1
...
...
@@ -18,9 +18,12 @@ namespace Titanium.Web.Proxy.Helpers
{
private
Stream
stream
;
private
Encoding
encoding
;
internal
CustomBinaryReader
(
Stream
stream
)
{
this
.
stream
=
stream
;
//default to UTF-8
this
.
encoding
=
Encoding
.
UTF8
;
}
...
...
@@ -41,16 +44,21 @@ namespace Titanium.Web.Proxy.Helpers
while
((
await
this
.
stream
.
ReadAsync
(
buffer
,
0
,
1
))
>
0
)
{
//if new line
if
(
lastChar
==
'\r'
&&
buffer
[
0
]
==
'\n'
)
{
var
result
=
readBuffer
.
ToArray
();
return
encoding
.
GetString
(
result
.
SubArray
(
0
,
result
.
Length
-
1
));
}
//end of stream
if
(
buffer
[
0
]
==
'\0'
)
{
return
encoding
.
GetString
(
readBuffer
.
ToArray
());
}
await
readBuffer
.
WriteAsync
(
buffer
,
0
,
1
);
//store last char for new line comparison
lastChar
=
(
char
)
buffer
[
0
];
}
...
...
@@ -58,7 +66,7 @@ namespace Titanium.Web.Proxy.Helpers
}
catch
(
IOException
)
{
return
encoding
.
GetString
(
readBuffer
.
ToArray
())
;
throw
;
}
}
}
...
...
@@ -71,13 +79,18 @@ namespace Titanium.Web.Proxy.Helpers
{
string
tmpLine
;
var
requestLines
=
new
List
<
string
>();
while
(!
string
.
IsNullOrEmpty
(
tmpLine
=
await
ReadLineAsync
()
.
ConfigureAwait
(
false
)
))
while
(!
string
.
IsNullOrEmpty
(
tmpLine
=
await
ReadLineAsync
()))
{
requestLines
.
Add
(
tmpLine
);
}
return
requestLines
;
}
/// <summary>
/// Read the specified number of raw bytes from the base stream
/// </summary>
/// <param name="totalBytesToRead"></param>
/// <returns></returns>
internal
async
Task
<
byte
[
]>
ReadBytesAsync
(
long
totalBytesToRead
)
{
int
bytesToRead
=
ProxyConstants
.
BUFFER_SIZE
;
...
...
@@ -92,7 +105,7 @@ namespace Titanium.Web.Proxy.Helpers
using
(
var
outStream
=
new
MemoryStream
())
{
while
((
bytesRead
+=
await
this
.
stream
.
ReadAsync
(
buffer
,
0
,
bytesToRead
)
.
ConfigureAwait
(
false
)
)
>
0
)
while
((
bytesRead
+=
await
this
.
stream
.
ReadAsync
(
buffer
,
0
,
bytesToRead
))
>
0
)
{
await
outStream
.
WriteAsync
(
buffer
,
0
,
bytesRead
);
totalBytesRead
+=
bytesRead
;
...
...
Titanium.Web.Proxy/Helpers/SystemProxy.cs
View file @
75e13cd1
...
...
@@ -5,8 +5,12 @@ using System.Text.RegularExpressions;
using
System.Collections.Generic
;
using
System.Linq
;
/// <summary>
/// Helper classes for setting system proxy settings
/// </summary>
namespace
Titanium.Web.Proxy.Helpers
{
internal
static
class
NativeMethods
{
[
DllImport
(
"wininet.dll"
)]
...
...
@@ -14,7 +18,7 @@ namespace Titanium.Web.Proxy.Helpers
int
dwBufferLength
);
}
internal
class
HttpSystemProxyValue
internal
class
HttpSystemProxyValue
{
internal
string
HostName
{
get
;
set
;
}
internal
int
Port
{
get
;
set
;
}
...
...
@@ -59,14 +63,16 @@ namespace Titanium.Web.Proxy.Helpers
Refresh
();
}
/// <summary>
/// Remove the http proxy setting from current machine
/// </summary>
internal
static
void
RemoveHttpProxy
()
{
var
reg
=
Registry
.
CurrentUser
.
OpenSubKey
(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"
,
true
);
if
(
reg
!=
null
)
{
if
(
reg
.
GetValue
(
"ProxyServer"
)
!=
null
)
if
(
reg
.
GetValue
(
"ProxyServer"
)
!=
null
)
{
var
exisitingContent
=
reg
.
GetValue
(
"ProxyServer"
)
as
string
;
...
...
@@ -89,11 +95,16 @@ namespace Titanium.Web.Proxy.Helpers
Refresh
();
}
/// <summary>
/// Set the HTTPS proxy server for current machine
/// </summary>
/// <param name="hostname"></param>
/// <param name="port"></param>
internal
static
void
SetHttpsProxy
(
string
hostname
,
int
port
)
{
var
reg
=
Registry
.
CurrentUser
.
OpenSubKey
(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"
,
true
);
if
(
reg
!=
null
)
{
prepareRegistry
(
reg
);
...
...
@@ -116,6 +127,9 @@ namespace Titanium.Web.Proxy.Helpers
Refresh
();
}
/// <summary>
/// Removes the https proxy setting to nothing
/// </summary>
internal
static
void
RemoveHttpsProxy
()
{
var
reg
=
Registry
.
CurrentUser
.
OpenSubKey
(
...
...
@@ -139,13 +153,16 @@ namespace Titanium.Web.Proxy.Helpers
reg
.
SetValue
(
"ProxyEnable"
,
0
);
reg
.
SetValue
(
"ProxyServer"
,
string
.
Empty
);
}
}
}
Refresh
();
}
/// <summary>
/// Removes all types of proxy settings (both http & https)
/// </summary>
internal
static
void
DisableAllProxy
()
{
var
reg
=
Registry
.
CurrentUser
.
OpenSubKey
(
...
...
@@ -159,6 +176,12 @@ namespace Titanium.Web.Proxy.Helpers
Refresh
();
}
/// <summary>
/// Get the current system proxy setting values
/// </summary>
/// <param name="prevServerValue"></param>
/// <returns></returns>
private
static
List
<
HttpSystemProxyValue
>
GetSystemProxyValues
(
string
prevServerValue
)
{
var
result
=
new
List
<
HttpSystemProxyValue
>();
...
...
@@ -187,6 +210,11 @@ namespace Titanium.Web.Proxy.Helpers
return
result
;
}
/// <summary>
/// Parses the system proxy setting string
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private
static
HttpSystemProxyValue
parseProxyValue
(
string
value
)
{
var
tmp
=
Regex
.
Replace
(
value
,
@"\s+"
,
" "
).
Trim
().
ToLower
();
...
...
@@ -213,7 +241,10 @@ namespace Titanium.Web.Proxy.Helpers
return
null
;
}
/// <summary>
/// Prepares the proxy server registry (create empty values if they don't exist)
/// </summary>
/// <param name="reg"></param>
private
static
void
prepareRegistry
(
RegistryKey
reg
)
{
if
(
reg
.
GetValue
(
"ProxyEnable"
)
==
null
)
...
...
@@ -227,8 +258,10 @@ namespace Titanium.Web.Proxy.Helpers
}
}
/// <summary>
/// Refresh the settings so that the system know about a change in proxy setting
/// </summary>
private
static
void
Refresh
()
{
NativeMethods
.
InternetSetOption
(
IntPtr
.
Zero
,
InternetOptionSettingsChanged
,
IntPtr
.
Zero
,
0
);
...
...
Titanium.Web.Proxy/Helpers/Tcp.cs
View file @
75e13cd1
...
...
@@ -12,11 +12,24 @@ using Titanium.Web.Proxy.Shared;
namespace
Titanium.Web.Proxy.Helpers
{
internal
class
TcpHelper
{
/// <summary>
/// relays the input clientStream to the server at the specified host name & port with the given httpCmd & headers as prefix
/// Usefull for websocket requests
/// </summary>
/// <param name="clientStream"></param>
/// <param name="httpCmd"></param>
/// <param name="requestHeaders"></param>
/// <param name="hostName"></param>
/// <param name="tunnelPort"></param>
/// <param name="isHttps"></param>
/// <returns></returns>
internal
async
static
Task
SendRaw
(
Stream
clientStream
,
string
httpCmd
,
List
<
HttpHeader
>
requestHeaders
,
string
hostName
,
int
tunnelPort
,
bool
isHttps
)
{
//prepare the prefix content
StringBuilder
sb
=
null
;
if
(
httpCmd
!=
null
||
requestHeaders
!=
null
)
{
...
...
@@ -38,7 +51,7 @@ namespace Titanium.Web.Proxy.Helpers
TcpClient
tunnelClient
=
null
;
Stream
tunnelStream
=
null
;
//create the TcpClient to the server
try
{
tunnelClient
=
new
TcpClient
(
hostName
,
tunnelPort
);
...
...
@@ -64,6 +77,7 @@ namespace Titanium.Web.Proxy.Helpers
Task
sendRelay
;
//Now async relay all server=>client & client=>server data
if
(
sb
!=
null
)
sendRelay
=
clientStream
.
CopyToAsync
(
sb
.
ToString
(),
tunnelStream
);
else
...
...
Titanium.Web.Proxy/Http/HttpWebClient.cs
View file @
75e13cd1
...
...
@@ -9,6 +9,9 @@ using Titanium.Web.Proxy.Shared;
namespace
Titanium.Web.Proxy.Http
{
/// <summary>
/// Used to communicate with the server over HTTP(S)
/// </summary>
public
class
HttpWebClient
{
/// <summary>
...
...
@@ -19,6 +22,9 @@ namespace Titanium.Web.Proxy.Http
public
Request
Request
{
get
;
set
;
}
public
Response
Response
{
get
;
set
;
}
/// <summary>
/// Is Https?
/// </summary>
public
bool
IsHttps
{
get
...
...
@@ -27,6 +33,10 @@ namespace Titanium.Web.Proxy.Http
}
}
/// <summary>
/// Set the tcp connection to server used by this webclient
/// </summary>
/// <param name="Connection"></param>
internal
void
SetConnection
(
TcpConnection
Connection
)
{
Connection
.
LastAccess
=
DateTime
.
Now
;
...
...
@@ -39,19 +49,24 @@ namespace Titanium.Web.Proxy.Http
this
.
Response
=
new
Response
();
}
/// <summary>
/// Prepare & send the http(s) request
/// </summary>
/// <returns></returns>
internal
async
Task
SendRequest
()
{
Stream
stream
=
ServerConnection
.
Stream
;
StringBuilder
requestLines
=
new
StringBuilder
();
//prepare the request & headers
requestLines
.
AppendLine
(
string
.
Join
(
" "
,
new
string
[
3
]
{
this
.
Request
.
Method
,
this
.
Request
.
RequestUri
.
PathAndQuery
,
string
.
Format
(
"HTTP/{0}.{1}"
,
this
.
Request
.
HttpVersion
.
Major
,
this
.
Request
.
HttpVersion
.
Minor
)
}));
//write request headers
foreach
(
HttpHeader
httpHeader
in
this
.
Request
.
RequestHeaders
)
{
requestLines
.
AppendLine
(
httpHeader
.
Name
+
':'
+
httpHeader
.
Value
);
...
...
@@ -87,6 +102,10 @@ namespace Titanium.Web.Proxy.Http
}
}
/// <summary>
/// Receive & parse the http response from server
/// </summary>
/// <returns></returns>
internal
async
Task
ReceiveResponse
()
{
//return if this is already read
...
...
@@ -130,6 +149,7 @@ namespace Titanium.Web.Proxy.Http
return
;
}
//read response headers
List
<
string
>
responseLines
=
await
ServerConnection
.
StreamReader
.
ReadAllLinesAsync
();
for
(
int
index
=
0
;
index
<
responseLines
.
Count
;
++
index
)
...
...
Titanium.Web.Proxy/Http/Request.cs
View file @
75e13cd1
...
...
@@ -7,6 +7,9 @@ using Titanium.Web.Proxy.Extensions;
namespace
Titanium.Web.Proxy.Http
{
/// <summary>
/// A HTTP(S) request object
/// </summary>
public
class
Request
{
public
string
Method
{
get
;
set
;
}
...
...
Titanium.Web.Proxy/Http/Response.cs
View file @
75e13cd1
...
...
@@ -8,7 +8,9 @@ using System;
namespace
Titanium.Web.Proxy.Http
{
/// <summary>
/// Http(s) response object
/// </summary>
public
class
Response
{
public
string
ResponseStatusCode
{
get
;
set
;
}
...
...
Titanium.Web.Proxy/Http/Responses/OkResponse.cs
View file @
75e13cd1
using
Titanium.Web.Proxy.Network
;
namespace
Titanium.Web.Proxy.Http.Responses
namespace
Titanium.Web.Proxy.Http.Responses
{
/// <summary>
/// 200 Ok response
/// </summary>
public
class
OkResponse
:
Response
{
public
OkResponse
()
...
...
Titanium.Web.Proxy/Http/Responses/RedirectResponse.cs
View file @
75e13cd1
...
...
@@ -2,6 +2,9 @@
namespace
Titanium.Web.Proxy.Http.Responses
{
/// <summary>
/// Redirect response
/// </summary>
public
class
RedirectResponse
:
Response
{
public
RedirectResponse
()
...
...
Titanium.Web.Proxy/Models/EndPoint.cs
View file @
75e13cd1
...
...
@@ -4,6 +4,9 @@ using System.Net.Sockets;
namespace
Titanium.Web.Proxy.Models
{
/// <summary>
/// An abstract endpoint where the proxy listens
/// </summary>
public
abstract
class
ProxyEndPoint
{
public
ProxyEndPoint
(
IPAddress
IpAddress
,
int
Port
,
bool
EnableSsl
)
...
...
@@ -20,6 +23,10 @@ namespace Titanium.Web.Proxy.Models
internal
TcpListener
listener
{
get
;
set
;
}
}
/// <summary>
/// A proxy endpoint that the client is aware of
/// So client application know that it is communicating with a proxy server
/// </summary>
public
class
ExplicitProxyEndPoint
:
ProxyEndPoint
{
internal
bool
IsSystemHttpProxy
{
get
;
set
;
}
...
...
@@ -34,6 +41,10 @@ namespace Titanium.Web.Proxy.Models
}
}
/// <summary>
/// A proxy end point client is not aware of
/// Usefull when requests are redirected to this proxy end point through port forwarding
/// </summary>
public
class
TransparentProxyEndPoint
:
ProxyEndPoint
{
//Name of the Certificate need to be sent (same as the hostname we want to proxy)
...
...
Titanium.Web.Proxy/Models/ExternalProxy.cs
View file @
75e13cd1
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Net
;
using
System.Text
;
using
System.Threading.Tasks
;
namespace
Titanium.Web.Proxy.Models
namespace
Titanium.Web.Proxy.Models
{
/// <summary>
/// An upstream proxy this proxy uses if any
/// </summary>
public
class
ExternalProxy
{
public
string
HostName
{
get
;
set
;
}
...
...
Titanium.Web.Proxy/Network/CachedCertificate.cs
0 → 100644
View file @
75e13cd1
using
System
;
using
System.Security.Cryptography.X509Certificates
;
namespace
Titanium.Web.Proxy.Network
{
/// <summary>
/// An object that holds the cached certificate
/// </summary>
internal
class
CachedCertificate
{
internal
X509Certificate2
Certificate
{
get
;
set
;
}
/// <summary>
/// last time this certificate was used
/// Usefull in determining its cache lifetime
/// </summary>
internal
DateTime
LastAccess
{
get
;
set
;
}
internal
CachedCertificate
()
{
LastAccess
=
DateTime
.
Now
;
}
}
}
Titanium.Web.Proxy/
Helpers
/CertificateManager.cs
→
Titanium.Web.Proxy/
Network
/CertificateManager.cs
View file @
75e13cd1
using
System
;
using
System.IO
;
using
System.Diagnostics
;
using
System.Collections.Generic
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Reflection
;
using
System.Threading.Tasks
;
using
System.Threading
;
using
System.Linq
;
namespace
Titanium.Web.Proxy.Helpers
{
internal
class
CachedCertificate
{
internal
X509Certificate2
Certificate
{
get
;
set
;
}
internal
DateTime
LastAccess
{
get
;
set
;
}
internal
CachedCertificate
()
{
LastAccess
=
DateTime
.
Now
;
}
}
internal
class
CertificateManager
:
IDisposable
{
private
const
string
CertCreateFormat
=
"-ss {0} -n \"CN={1}, O={2}\" -sky {3} -cy {4} -m 120 -a sha256 -eku 1.3.6.1.5.5.7.3.1 {5}"
;
private
readonly
IDictionary
<
string
,
CachedCertificate
>
certificateCache
;
private
static
SemaphoreSlim
semaphoreLock
=
new
SemaphoreSlim
(
1
);
internal
string
Issuer
{
get
;
private
set
;
}
internal
string
RootCertificateName
{
get
;
private
set
;
}
internal
X509Store
MyStore
{
get
;
private
set
;
}
internal
X509Store
RootStore
{
get
;
private
set
;
}
internal
CertificateManager
(
string
issuer
,
string
rootCertificateName
)
{
Issuer
=
issuer
;
RootCertificateName
=
rootCertificateName
;
MyStore
=
new
X509Store
(
StoreName
.
My
,
StoreLocation
.
CurrentUser
);
RootStore
=
new
X509Store
(
StoreName
.
Root
,
StoreLocation
.
CurrentUser
);
certificateCache
=
new
Dictionary
<
string
,
CachedCertificate
>();
}
/// <summary>
/// Attempts to move a self-signed certificate to the root store.
/// </summary>
/// <returns>true if succeeded, else false</returns>
internal
async
Task
<
bool
>
CreateTrustedRootCertificate
()
{
X509Certificate2
rootCertificate
=
await
CreateCertificate
(
RootStore
,
RootCertificateName
,
true
);
return
rootCertificate
!=
null
;
}
/// <summary>
/// Attempts to remove the self-signed certificate from the root store.
/// </summary>
/// <returns>true if succeeded, else false</returns>
internal
bool
DestroyTrustedRootCertificate
()
{
return
DestroyCertificate
(
RootStore
,
RootCertificateName
,
false
);
}
internal
X509Certificate2Collection
FindCertificates
(
string
certificateSubject
)
{
return
FindCertificates
(
MyStore
,
certificateSubject
);
}
protected
virtual
X509Certificate2Collection
FindCertificates
(
X509Store
store
,
string
certificateSubject
)
{
X509Certificate2Collection
discoveredCertificates
=
store
.
Certificates
.
Find
(
X509FindType
.
FindBySubjectDistinguishedName
,
certificateSubject
,
false
);
return
discoveredCertificates
.
Count
>
0
?
discoveredCertificates
:
null
;
}
internal
async
Task
<
X509Certificate2
>
CreateCertificate
(
string
certificateName
,
bool
isRootCertificate
)
{
return
await
CreateCertificate
(
MyStore
,
certificateName
,
isRootCertificate
);
}
protected
async
virtual
Task
<
X509Certificate2
>
CreateCertificate
(
X509Store
store
,
string
certificateName
,
bool
isRootCertificate
)
{
await
semaphoreLock
.
WaitAsync
();
try
{
if
(
certificateCache
.
ContainsKey
(
certificateName
))
{
var
cached
=
certificateCache
[
certificateName
];
cached
.
LastAccess
=
DateTime
.
Now
;
return
cached
.
Certificate
;
}
X509Certificate2
certificate
=
null
;
store
.
Open
(
OpenFlags
.
ReadWrite
);
string
certificateSubject
=
string
.
Format
(
"CN={0}, O={1}"
,
certificateName
,
Issuer
);
X509Certificate2Collection
certificates
;
if
(
isRootCertificate
)
{
certificates
=
FindCertificates
(
store
,
certificateSubject
);
if
(
certificates
!=
null
)
certificate
=
certificates
[
0
];
}
if
(
certificate
==
null
)
{
string
[]
args
=
new
[]
{
GetCertificateCreateArgs
(
store
,
certificateName
)
};
await
CreateCertificate
(
args
);
certificates
=
FindCertificates
(
store
,
certificateSubject
);
//remove it from store
if
(!
isRootCertificate
)
DestroyCertificate
(
certificateName
);
if
(
certificates
!=
null
)
certificate
=
certificates
[
0
];
}
store
.
Close
();
if
(
certificate
!=
null
&&
!
certificateCache
.
ContainsKey
(
certificateName
))
certificateCache
.
Add
(
certificateName
,
new
CachedCertificate
()
{
Certificate
=
certificate
});
return
certificate
;
}
finally
{
semaphoreLock
.
Release
();
}
}
protected
virtual
Task
<
int
>
CreateCertificate
(
string
[]
args
)
{
// there is no non-generic TaskCompletionSource
var
tcs
=
new
TaskCompletionSource
<
int
>();
var
process
=
new
Process
();
string
file
=
Path
.
Combine
(
Path
.
GetDirectoryName
(
Assembly
.
GetExecutingAssembly
().
Location
),
"makecert.exe"
);
if
(!
File
.
Exists
(
file
))
throw
new
Exception
(
"Unable to locate 'makecert.exe'."
);
process
.
StartInfo
.
Verb
=
"runas"
;
process
.
StartInfo
.
Arguments
=
args
!=
null
?
args
[
0
]
:
string
.
Empty
;
process
.
StartInfo
.
CreateNoWindow
=
true
;
process
.
StartInfo
.
UseShellExecute
=
false
;
process
.
StartInfo
.
FileName
=
file
;
process
.
EnableRaisingEvents
=
true
;
process
.
Exited
+=
(
sender
,
processArgs
)
=>
{
tcs
.
SetResult
(
process
.
ExitCode
);
process
.
Dispose
();
};
bool
started
=
process
.
Start
();
if
(!
started
)
{
//you may allow for the process to be re-used (started = false)
//but I'm not sure about the guarantees of the Exited event in such a case
throw
new
InvalidOperationException
(
"Could not start process: "
+
process
);
}
return
tcs
.
Task
;
}
internal
bool
DestroyCertificate
(
string
certificateName
)
{
return
DestroyCertificate
(
MyStore
,
certificateName
,
false
);
}
protected
virtual
bool
DestroyCertificate
(
X509Store
store
,
string
certificateName
,
bool
removeFromCache
)
{
X509Certificate2Collection
certificates
=
null
;
store
.
Open
(
OpenFlags
.
ReadWrite
);
string
certificateSubject
=
string
.
Format
(
"CN={0}, O={1}"
,
certificateName
,
Issuer
);
certificates
=
FindCertificates
(
store
,
certificateSubject
);
if
(
certificates
!=
null
)
{
store
.
RemoveRange
(
certificates
);
certificates
=
FindCertificates
(
store
,
certificateSubject
);
}
store
.
Close
();
if
(
removeFromCache
&&
certificateCache
.
ContainsKey
(
certificateName
))
{
certificateCache
.
Remove
(
certificateName
);
}
return
certificates
==
null
;
}
protected
virtual
string
GetCertificateCreateArgs
(
X509Store
store
,
string
certificateName
)
{
bool
isRootCertificate
=
(
certificateName
==
RootCertificateName
);
string
certCreatArgs
=
string
.
Format
(
CertCreateFormat
,
store
.
Name
,
certificateName
,
Issuer
,
isRootCertificate
?
"signature"
:
"exchange"
,
isRootCertificate
?
"authority"
:
"end"
,
isRootCertificate
?
"-h 1 -r"
:
string
.
Format
(
"-pe -in \"{0}\" -is Root"
,
RootCertificateName
));
return
certCreatArgs
;
}
private
static
bool
clearCertificates
{
get
;
set
;
}
internal
void
StopClearIdleCertificates
()
{
clearCertificates
=
false
;
}
internal
async
void
ClearIdleCertificates
()
{
clearCertificates
=
true
;
while
(
clearCertificates
)
{
await
semaphoreLock
.
WaitAsync
();
try
{
var
cutOff
=
DateTime
.
Now
.
AddMinutes
(-
1
*
ProxyServer
.
CertificateCacheTimeOutMinutes
);
var
outdated
=
certificateCache
.
Where
(
x
=>
x
.
Value
.
LastAccess
<
cutOff
)
.
ToList
();
foreach
(
var
cache
in
outdated
)
certificateCache
.
Remove
(
cache
.
Key
);
}
finally
{
semaphoreLock
.
Release
();
}
await
Task
.
Delay
(
1000
*
60
*
3
);
}
}
public
void
Dispose
()
{
if
(
MyStore
!=
null
)
MyStore
.
Close
();
if
(
RootStore
!=
null
)
RootStore
.
Close
();
}
}
using
System
;
using
System.IO
;
using
System.Diagnostics
;
using
System.Collections.Generic
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Reflection
;
using
System.Threading.Tasks
;
using
System.Threading
;
using
System.Linq
;
namespace
Titanium.Web.Proxy.Network
{
/// <summary>
/// A class to manage SSL certificates used by this proxy server
/// </summary>
internal
class
CertificateManager
:
IDisposable
{
private
const
string
CertCreateFormat
=
"-ss {0} -n \"CN={1}, O={2}\" -sky {3} -cy {4} -m 120 -a sha256 -eku 1.3.6.1.5.5.7.3.1 {5}"
;
private
readonly
IDictionary
<
string
,
CachedCertificate
>
certificateCache
;
private
static
SemaphoreSlim
semaphoreLock
=
new
SemaphoreSlim
(
1
);
internal
string
Issuer
{
get
;
private
set
;
}
internal
string
RootCertificateName
{
get
;
private
set
;
}
internal
X509Store
MyStore
{
get
;
private
set
;
}
internal
X509Store
RootStore
{
get
;
private
set
;
}
internal
CertificateManager
(
string
issuer
,
string
rootCertificateName
)
{
Issuer
=
issuer
;
RootCertificateName
=
rootCertificateName
;
MyStore
=
new
X509Store
(
StoreName
.
My
,
StoreLocation
.
CurrentUser
);
RootStore
=
new
X509Store
(
StoreName
.
Root
,
StoreLocation
.
CurrentUser
);
certificateCache
=
new
Dictionary
<
string
,
CachedCertificate
>();
}
/// <summary>
/// Attempts to move a self-signed certificate to the root store.
/// </summary>
/// <returns>true if succeeded, else false</returns>
internal
async
Task
<
bool
>
CreateTrustedRootCertificate
()
{
X509Certificate2
rootCertificate
=
await
CreateCertificate
(
RootStore
,
RootCertificateName
,
true
);
return
rootCertificate
!=
null
;
}
/// <summary>
/// Attempts to remove the self-signed certificate from the root store.
/// </summary>
/// <returns>true if succeeded, else false</returns>
internal
bool
DestroyTrustedRootCertificate
()
{
return
DestroyCertificate
(
RootStore
,
RootCertificateName
,
false
);
}
internal
X509Certificate2Collection
FindCertificates
(
string
certificateSubject
)
{
return
FindCertificates
(
MyStore
,
certificateSubject
);
}
protected
virtual
X509Certificate2Collection
FindCertificates
(
X509Store
store
,
string
certificateSubject
)
{
X509Certificate2Collection
discoveredCertificates
=
store
.
Certificates
.
Find
(
X509FindType
.
FindBySubjectDistinguishedName
,
certificateSubject
,
false
);
return
discoveredCertificates
.
Count
>
0
?
discoveredCertificates
:
null
;
}
internal
async
Task
<
X509Certificate2
>
CreateCertificate
(
string
certificateName
,
bool
isRootCertificate
)
{
return
await
CreateCertificate
(
MyStore
,
certificateName
,
isRootCertificate
);
}
/// <summary>
/// Create an SSL certificate
/// </summary>
/// <param name="store"></param>
/// <param name="certificateName"></param>
/// <param name="isRootCertificate"></param>
/// <returns></returns>
protected
async
virtual
Task
<
X509Certificate2
>
CreateCertificate
(
X509Store
store
,
string
certificateName
,
bool
isRootCertificate
)
{
await
semaphoreLock
.
WaitAsync
();
try
{
if
(
certificateCache
.
ContainsKey
(
certificateName
))
{
var
cached
=
certificateCache
[
certificateName
];
cached
.
LastAccess
=
DateTime
.
Now
;
return
cached
.
Certificate
;
}
X509Certificate2
certificate
=
null
;
store
.
Open
(
OpenFlags
.
ReadWrite
);
string
certificateSubject
=
string
.
Format
(
"CN={0}, O={1}"
,
certificateName
,
Issuer
);
X509Certificate2Collection
certificates
;
if
(
isRootCertificate
)
{
certificates
=
FindCertificates
(
store
,
certificateSubject
);
if
(
certificates
!=
null
)
certificate
=
certificates
[
0
];
}
if
(
certificate
==
null
)
{
string
[]
args
=
new
[]
{
GetCertificateCreateArgs
(
store
,
certificateName
)
};
await
CreateCertificate
(
args
);
certificates
=
FindCertificates
(
store
,
certificateSubject
);
//remove it from store
if
(!
isRootCertificate
)
DestroyCertificate
(
certificateName
);
if
(
certificates
!=
null
)
certificate
=
certificates
[
0
];
}
store
.
Close
();
if
(
certificate
!=
null
&&
!
certificateCache
.
ContainsKey
(
certificateName
))
certificateCache
.
Add
(
certificateName
,
new
CachedCertificate
()
{
Certificate
=
certificate
});
return
certificate
;
}
finally
{
semaphoreLock
.
Release
();
}
}
/// <summary>
/// Create certificate using makecert.exe
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
protected
virtual
Task
<
int
>
CreateCertificate
(
string
[]
args
)
{
// there is no non-generic TaskCompletionSource
var
tcs
=
new
TaskCompletionSource
<
int
>();
var
process
=
new
Process
();
string
file
=
Path
.
Combine
(
Path
.
GetDirectoryName
(
Assembly
.
GetExecutingAssembly
().
Location
),
"makecert.exe"
);
if
(!
File
.
Exists
(
file
))
throw
new
Exception
(
"Unable to locate 'makecert.exe'."
);
process
.
StartInfo
.
Verb
=
"runas"
;
process
.
StartInfo
.
Arguments
=
args
!=
null
?
args
[
0
]
:
string
.
Empty
;
process
.
StartInfo
.
CreateNoWindow
=
true
;
process
.
StartInfo
.
UseShellExecute
=
false
;
process
.
StartInfo
.
FileName
=
file
;
process
.
EnableRaisingEvents
=
true
;
process
.
Exited
+=
(
sender
,
processArgs
)
=>
{
tcs
.
SetResult
(
process
.
ExitCode
);
process
.
Dispose
();
};
bool
started
=
process
.
Start
();
if
(!
started
)
{
//you may allow for the process to be re-used (started = false)
//but I'm not sure about the guarantees of the Exited event in such a case
throw
new
InvalidOperationException
(
"Could not start process: "
+
process
);
}
return
tcs
.
Task
;
}
/// <summary>
/// Destroy an SSL certificate from local store
/// </summary>
/// <param name="certificateName"></param>
/// <returns></returns>
internal
bool
DestroyCertificate
(
string
certificateName
)
{
return
DestroyCertificate
(
MyStore
,
certificateName
,
false
);
}
/// <summary>
/// Destroy certificate from the specified store
/// optionally also remove from proxy certificate cache
/// </summary>
/// <param name="store"></param>
/// <param name="certificateName"></param>
/// <param name="removeFromCache"></param>
/// <returns></returns>
protected
virtual
bool
DestroyCertificate
(
X509Store
store
,
string
certificateName
,
bool
removeFromCache
)
{
X509Certificate2Collection
certificates
=
null
;
store
.
Open
(
OpenFlags
.
ReadWrite
);
string
certificateSubject
=
string
.
Format
(
"CN={0}, O={1}"
,
certificateName
,
Issuer
);
certificates
=
FindCertificates
(
store
,
certificateSubject
);
if
(
certificates
!=
null
)
{
store
.
RemoveRange
(
certificates
);
certificates
=
FindCertificates
(
store
,
certificateSubject
);
}
store
.
Close
();
if
(
removeFromCache
&&
certificateCache
.
ContainsKey
(
certificateName
))
{
certificateCache
.
Remove
(
certificateName
);
}
return
certificates
==
null
;
}
/// <summary>
/// Create the neccessary arguments for makecert.exe to create the required certificate
/// </summary>
/// <param name="store"></param>
/// <param name="certificateName"></param>
/// <returns></returns>
protected
virtual
string
GetCertificateCreateArgs
(
X509Store
store
,
string
certificateName
)
{
bool
isRootCertificate
=
(
certificateName
==
RootCertificateName
);
string
certCreatArgs
=
string
.
Format
(
CertCreateFormat
,
store
.
Name
,
certificateName
,
Issuer
,
isRootCertificate
?
"signature"
:
"exchange"
,
isRootCertificate
?
"authority"
:
"end"
,
isRootCertificate
?
"-h 1 -r"
:
string
.
Format
(
"-pe -in \"{0}\" -is Root"
,
RootCertificateName
));
return
certCreatArgs
;
}
private
static
bool
clearCertificates
{
get
;
set
;
}
/// <summary>
/// Stops the certificate cache clear process
/// </summary>
internal
void
StopClearIdleCertificates
()
{
clearCertificates
=
false
;
}
/// <summary>
/// A method to clear outdated certificates
/// </summary>
internal
async
void
ClearIdleCertificates
()
{
clearCertificates
=
true
;
while
(
clearCertificates
)
{
await
semaphoreLock
.
WaitAsync
();
try
{
var
cutOff
=
DateTime
.
Now
.
AddMinutes
(-
1
*
ProxyServer
.
CertificateCacheTimeOutMinutes
);
var
outdated
=
certificateCache
.
Where
(
x
=>
x
.
Value
.
LastAccess
<
cutOff
)
.
ToList
();
foreach
(
var
cache
in
outdated
)
certificateCache
.
Remove
(
cache
.
Key
);
}
finally
{
semaphoreLock
.
Release
();
}
//after a minute come back to check for outdated certificates in cache
await
Task
.
Delay
(
1000
*
60
);
}
}
public
void
Dispose
()
{
if
(
MyStore
!=
null
)
MyStore
.
Close
();
if
(
RootStore
!=
null
)
RootStore
.
Close
();
}
}
}
\ No newline at end of file
Titanium.Web.Proxy/Network/TcpConnection.cs
0 → 100644
View file @
75e13cd1
using
System
;
using
System.IO
;
using
System.Net.Sockets
;
using
Titanium.Web.Proxy.Helpers
;
namespace
Titanium.Web.Proxy.Network
{
/// <summary>
/// An object that holds TcpConnection to a particular server & port
/// </summary>
public
class
TcpConnection
{
internal
string
HostName
{
get
;
set
;
}
internal
int
port
{
get
;
set
;
}
internal
bool
IsHttps
{
get
;
set
;
}
/// <summary>
/// Http version
/// </summary>
internal
Version
Version
{
get
;
set
;
}
internal
TcpClient
TcpClient
{
get
;
set
;
}
/// <summary>
/// used to read lines from server
/// </summary>
internal
CustomBinaryReader
StreamReader
{
get
;
set
;
}
/// <summary>
/// Server stream
/// </summary>
internal
Stream
Stream
{
get
;
set
;
}
/// <summary>
/// Last time this connection was used
/// </summary>
internal
DateTime
LastAccess
{
get
;
set
;
}
internal
TcpConnection
()
{
LastAccess
=
DateTime
.
Now
;
}
}
}
Titanium.Web.Proxy/Network/TcpConnectionManager.cs
View file @
75e13cd1
...
...
@@ -10,44 +10,41 @@ using Titanium.Web.Proxy.Helpers;
using
System.Threading
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Shared
;
using
System.Security.Cryptography.X509Certificates
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Models
;
namespace
Titanium.Web.Proxy.Network
{
public
class
TcpConnection
{
internal
string
HostName
{
get
;
set
;
}
internal
int
port
{
get
;
set
;
}
internal
bool
IsHttps
{
get
;
set
;
}
internal
Version
Version
{
get
;
set
;
}
internal
TcpClient
TcpClient
{
get
;
set
;
}
internal
CustomBinaryReader
StreamReader
{
get
;
set
;
}
internal
Stream
Stream
{
get
;
set
;
}
internal
DateTime
LastAccess
{
get
;
set
;
}
internal
TcpConnection
()
{
LastAccess
=
DateTime
.
Now
;
}
}
/// <summary>
/// A class that manages Tcp Connection to server used by this proxy server
/// </summary>
internal
class
TcpConnectionManager
{
/// <summary>
/// Connection cache
/// </summary>
static
Dictionary
<
string
,
List
<
TcpConnection
>>
connectionCache
=
new
Dictionary
<
string
,
List
<
TcpConnection
>>();
static
SemaphoreSlim
connectionAccessLock
=
new
SemaphoreSlim
(
1
);
/// <summary>
/// A lock to manage concurrency
/// </summary>
static
SemaphoreSlim
connectionAccessLock
=
new
SemaphoreSlim
(
1
);
/// <summary>
/// Get a TcpConnection to the specified host, port optionally HTTPS and a particular HTTP version
/// </summary>
/// <param name="hostname"></param>
/// <param name="port"></param>
/// <param name="isHttps"></param>
/// <param name="version"></param>
/// <returns></returns>
internal
static
async
Task
<
TcpConnection
>
GetClient
(
string
hostname
,
int
port
,
bool
isHttps
,
Version
version
)
{
List
<
TcpConnection
>
cachedConnections
=
null
;
TcpConnection
cached
=
null
;
//Get a unique string to identify this connection
var
key
=
GetConnectionKey
(
hostname
,
port
,
isHttps
,
version
);
while
(
true
)
{
await
connectionAccessLock
.
WaitAsync
();
...
...
@@ -76,6 +73,7 @@ namespace Titanium.Web.Proxy.Network
if
(
cached
==
null
)
break
;
}
if
(
cached
==
null
)
...
...
@@ -90,11 +88,27 @@ namespace Titanium.Web.Proxy.Network
return
cached
;
}
/// <summary>
/// Get a string to identfiy the connection to a particular host, port
/// </summary>
/// <param name="hostname"></param>
/// <param name="port"></param>
/// <param name="isHttps"></param>
/// <param name="version"></param>
/// <returns></returns>
internal
static
string
GetConnectionKey
(
string
hostname
,
int
port
,
bool
isHttps
,
Version
version
)
{
return
string
.
Format
(
"{0}:{1}:{2}:{3}:{4}"
,
hostname
.
ToLower
(),
port
,
isHttps
,
version
.
Major
,
version
.
Minor
);
}
/// <summary>
/// Create connection to a particular host/port optionally with SSL and a particular HTTP version
/// </summary>
/// <param name="hostname"></param>
/// <param name="port"></param>
/// <param name="isHttps"></param>
/// <param name="version"></param>
/// <returns></returns>
private
static
async
Task
<
TcpConnection
>
CreateClient
(
string
hostname
,
int
port
,
bool
isHttps
,
Version
version
)
{
TcpClient
client
;
...
...
@@ -104,6 +118,7 @@ namespace Titanium.Web.Proxy.Network
{
SslStream
sslStream
=
null
;
//If this proxy uses another external proxy then create a tunnel request for HTTPS connections
if
(
ProxyServer
.
UpStreamHttpsProxy
!=
null
)
{
client
=
new
TcpClient
(
ProxyServer
.
UpStreamHttpsProxy
.
HostName
,
ProxyServer
.
UpStreamHttpsProxy
.
Port
);
...
...
@@ -175,7 +190,11 @@ namespace Titanium.Web.Proxy.Network
};
}
/// <summary>
/// Returns a Tcp Connection back to cache for reuse by other requests
/// </summary>
/// <param name="connection"></param>
/// <returns></returns>
internal
static
async
Task
ReleaseClient
(
TcpConnection
connection
)
{
...
...
@@ -199,11 +218,17 @@ namespace Titanium.Web.Proxy.Network
private
static
bool
clearConenctions
{
get
;
set
;
}
/// <summary>
/// Stop clearing idle connections
/// </summary>
internal
static
void
StopClearIdleConnections
()
{
clearConenctions
=
false
;
}
/// <summary>
/// A method to clear idle connections
/// </summary>
internal
async
static
void
ClearIdleConnections
()
{
clearConenctions
=
true
;
...
...
@@ -224,6 +249,7 @@ namespace Titanium.Web.Proxy.Network
}
finally
{
connectionAccessLock
.
Release
();
}
//every minute run this
await
Task
.
Delay
(
1000
*
60
);
}
...
...
Titanium.Web.Proxy/ProxyServer.cs
View file @
75e13cd1
...
...
@@ -22,17 +22,41 @@ namespace Titanium.Web.Proxy
static
ProxyServer
()
{
ProxyEndPoints
=
new
List
<
ProxyEndPoint
>();
//default values
ConnectionCacheTimeOutMinutes
=
3
;
CertificateCacheTimeOutMinutes
=
60
;
}
/// <summary>
/// Manages certificates used by this proxy
/// </summary>
private
static
CertificateManager
CertManager
{
get
;
set
;
}
/// <summary>
/// Does the root certificate used by this proxy is trusted by the machine?
/// </summary>
private
static
bool
certTrusted
{
get
;
set
;
}
/// <summary>
/// Is the proxy currently running
/// </summary>
private
static
bool
proxyRunning
{
get
;
set
;
}
/// <summary>
/// Name of the root certificate issuer
/// </summary>
public
static
string
RootCertificateIssuerName
{
get
;
set
;
}
/// <summary>
/// Name of the root certificate
/// </summary>
public
static
string
RootCertificateName
{
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
/// </summary>
public
static
bool
Enable100ContinueBehaviour
{
get
;
set
;
}
/// <summary>
...
...
@@ -45,13 +69,11 @@ namespace Titanium.Web.Proxy
/// </summary>
public
static
int
CertificateCacheTimeOutMinutes
{
get
;
set
;
}
/// <summary>
/// Intercept request to server
/// </summary>
public
static
event
Func
<
object
,
SessionEventArgs
,
Task
>
BeforeRequest
;
/// <summary>
/// Intercept response from server
/// </summary>
...
...
@@ -77,20 +99,33 @@ namespace Titanium.Web.Proxy
/// </summary>
public
static
event
Func
<
object
,
CertificateSelectionEventArgs
,
Task
>
ClientCertificateSelectionCallback
;
/// <summary>
/// A list of IpAddress & port this proxy is listening to
/// </summary>
public
static
List
<
ProxyEndPoint
>
ProxyEndPoints
{
get
;
set
;
}
/// <summary>
/// Initialize the proxy
/// </summary>
public
static
void
Initialize
()
{
TcpConnectionManager
.
ClearIdleConnections
();
CertManager
.
ClearIdleCertificates
();
}
/// <summary>
/// Quit the proxy
/// </summary>
public
static
void
Quit
()
{
TcpConnectionManager
.
StopClearIdleConnections
();
CertManager
.
StopClearIdleCertificates
();
}
/// <summary>
/// Add a proxy end point
/// </summary>
/// <param name="endPoint"></param>
public
static
void
AddEndPoint
(
ProxyEndPoint
endPoint
)
{
ProxyEndPoints
.
Add
(
endPoint
);
...
...
@@ -99,6 +134,11 @@ namespace Titanium.Web.Proxy
Listen
(
endPoint
);
}
/// <summary>
/// Remove a proxy end point
/// Will throw error if the end point does'nt exist
/// </summary>
/// <param name="endPoint"></param>
public
static
void
RemoveEndPoint
(
ProxyEndPoint
endPoint
)
{
...
...
@@ -111,10 +151,13 @@ namespace Titanium.Web.Proxy
QuitListen
(
endPoint
);
}
/// <summary>
/// Set the given explicit end point as the default proxy server for current machine
/// </summary>
/// <param name="endPoint"></param>
public
static
void
SetAsSystemHttpProxy
(
ExplicitProxyEndPoint
endPoint
)
{
V
erify
Proxy
(
endPoint
);
V
alidateEndPointAsSystem
Proxy
(
endPoint
);
//clear any settings previously added
ProxyEndPoints
.
OfType
<
ExplicitProxyEndPoint
>().
ToList
().
ForEach
(
x
=>
x
.
IsSystemHttpProxy
=
false
);
...
...
@@ -130,14 +173,21 @@ namespace Titanium.Web.Proxy
}
/// <summary>
/// Remove any HTTP proxy setting of current machien
/// </summary>
public
static
void
DisableSystemHttpProxy
()
{
SystemProxyHelper
.
RemoveHttpProxy
();
}
/// <summary>
/// Set the given explicit end point as the default proxy server for current machine
/// </summary>
/// <param name="endPoint"></param>
public
static
void
SetAsSystemHttpsProxy
(
ExplicitProxyEndPoint
endPoint
)
{
V
erify
Proxy
(
endPoint
);
V
alidateEndPointAsSystem
Proxy
(
endPoint
);
if
(!
endPoint
.
EnableSsl
)
{
...
...
@@ -164,16 +214,25 @@ namespace Titanium.Web.Proxy
Console
.
WriteLine
(
"Set endpoint at Ip {1} and port: {2} as System HTTPS Proxy"
,
endPoint
.
GetType
().
Name
,
endPoint
.
IpAddress
,
endPoint
.
Port
);
}
/// <summary>
/// Remove any HTTPS proxy setting for current machine
/// </summary>
public
static
void
DisableSystemHttpsProxy
()
{
SystemProxyHelper
.
RemoveHttpsProxy
();
}
/// <summary>
/// Clear all proxy settings for current machine
/// </summary>
public
static
void
DisableAllSystemProxies
()
{
SystemProxyHelper
.
DisableAllProxy
();
}
/// <summary>
/// Start this proxy server
/// </summary>
public
static
void
Start
()
{
if
(
proxyRunning
)
...
...
@@ -197,6 +256,9 @@ namespace Titanium.Web.Proxy
proxyRunning
=
true
;
}
/// <summary>
/// Stop this proxy server
/// </summary>
public
static
void
Stop
()
{
if
(!
proxyRunning
)
...
...
@@ -224,6 +286,10 @@ namespace Titanium.Web.Proxy
proxyRunning
=
false
;
}
/// <summary>
/// Listen on the given end point on local machine
/// </summary>
/// <param name="endPoint"></param>
private
static
void
Listen
(
ProxyEndPoint
endPoint
)
{
endPoint
.
listener
=
new
TcpListener
(
endPoint
.
IpAddress
,
endPoint
.
Port
);
...
...
@@ -234,13 +300,20 @@ namespace Titanium.Web.Proxy
endPoint
.
listener
.
BeginAcceptTcpClient
(
OnAcceptConnection
,
endPoint
);
}
/// <summary>
/// Quit listening on the given end point
/// </summary>
/// <param name="endPoint"></param>
private
static
void
QuitListen
(
ProxyEndPoint
endPoint
)
{
endPoint
.
listener
.
Stop
();
}
private
static
void
VerifyProxy
(
ExplicitProxyEndPoint
endPoint
)
/// <summary>
/// Verifiy if its safe to set this end point as System proxy
/// </summary>
/// <param name="endPoint"></param>
private
static
void
ValidateEndPointAsSystemProxy
(
ExplicitProxyEndPoint
endPoint
)
{
if
(
ProxyEndPoints
.
Contains
(
endPoint
)
==
false
)
throw
new
Exception
(
"Cannot set endPoints not added to proxy as system proxy"
);
...
...
@@ -249,12 +322,17 @@ namespace Titanium.Web.Proxy
throw
new
Exception
(
"Cannot set system proxy settings before proxy has been started."
);
}
/// <summary>
/// When a connection is received from client act
/// </summary>
/// <param name="asyn"></param>
private
static
void
OnAcceptConnection
(
IAsyncResult
asyn
)
{
var
endPoint
=
(
ProxyEndPoint
)
asyn
.
AsyncState
;
try
{
//based on end point type call appropriate request handlers
var
client
=
endPoint
.
listener
.
EndAcceptTcpClient
(
asyn
);
if
(
endPoint
.
GetType
()
==
typeof
(
TransparentProxyEndPoint
))
HandleClient
(
endPoint
as
TransparentProxyEndPoint
,
client
);
...
...
@@ -273,7 +351,7 @@ namespace Titanium.Web.Proxy
}
catch
{
//Other errors are discarded to keep the proxy running
}
}
...
...
Titanium.Web.Proxy/RequestHandler.cs
View file @
75e13cd1
...
...
@@ -59,10 +59,10 @@ namespace Titanium.Web.Proxy
{
version
=
new
Version
(
1
,
0
);
}
}
var
excluded
=
endPoint
.
ExcludedHttpsHostNameRegex
!=
null
?
endPoint
.
ExcludedHttpsHostNameRegex
.
Any
(
x
=>
Regex
.
IsMatch
(
httpRemoteUri
.
Host
,
x
))
:
false
;
var
excluded
=
endPoint
.
ExcludedHttpsHostNameRegex
!=
null
?
endPoint
.
ExcludedHttpsHostNameRegex
.
Any
(
x
=>
Regex
.
IsMatch
(
httpRemoteUri
.
Host
,
x
))
:
false
;
//Client wants to create a secure tcp tunnel (its a HTTPS request)
if
(
httpVerb
.
ToUpper
()
==
"CONNECT"
&&
!
excluded
&&
httpRemoteUri
.
Port
!=
80
)
...
...
@@ -217,7 +217,7 @@ namespace Titanium.Web.Proxy
args
.
WebSession
.
Request
.
RequestHeaders
=
new
List
<
HttpHeader
>();
string
tmpLine
;
while
(!
string
.
IsNullOrEmpty
(
tmpLine
=
await
clientStreamReader
.
ReadLineAsync
()
.
ConfigureAwait
(
false
)
))
while
(!
string
.
IsNullOrEmpty
(
tmpLine
=
await
clientStreamReader
.
ReadLineAsync
()))
{
var
header
=
tmpLine
.
Split
(
ProxyConstants
.
ColonSplit
,
2
);
args
.
WebSession
.
Request
.
RequestHeaders
.
Add
(
new
HttpHeader
(
header
[
0
],
header
[
1
]));
...
...
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
View file @
75e13cd1
...
...
@@ -63,11 +63,12 @@
<Compile
Include=
"EventArguments\CertificateSelectionEventArgs.cs"
/>
<Compile
Include=
"EventArguments\CertificateValidationEventArgs.cs"
/>
<Compile
Include=
"Extensions\ByteArrayExtensions.cs"
/>
<Compile
Include=
"Network\CachedCertificate.cs"
/>
<Compile
Include=
"Network\ProxyClient.cs"
/>
<Compile
Include=
"Exceptions\BodyNotFoundException.cs"
/>
<Compile
Include=
"Extensions\HttpWebResponseExtensions.cs"
/>
<Compile
Include=
"Extensions\HttpWebRequestExtensions.cs"
/>
<Compile
Include=
"
Helpers
\CertificateManager.cs"
/>
<Compile
Include=
"
Network
\CertificateManager.cs"
/>
<Compile
Include=
"Helpers\Firefox.cs"
/>
<Compile
Include=
"Helpers\SystemProxy.cs"
/>
<Compile
Include=
"Models\EndPoint.cs"
/>
...
...
@@ -75,6 +76,7 @@
<Compile
Include=
"Http\Request.cs"
/>
<Compile
Include=
"Http\Response.cs"
/>
<Compile
Include=
"Models\ExternalProxy.cs"
/>
<Compile
Include=
"Network\TcpConnection.cs"
/>
<Compile
Include=
"Network\TcpConnectionManager.cs"
/>
<Compile
Include=
"Models\HttpHeader.cs"
/>
<Compile
Include=
"Http\HttpWebClient.cs"
/>
...
...
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