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
0d8dbf74
Commit
0d8dbf74
authored
Dec 28, 2017
by
Honfika
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Forget to commit some files
parent
20df2e6e
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
2104 additions
and
14 deletions
+2104
-14
ClientHelloInfo.cs
Titanium.Web.Proxy/ClientHelloInfo.cs
+117
-0
StreamExtensions.cs
Titanium.Web.Proxy/Extensions/StreamExtensions.cs
+2
-2
HttpResponseWriter.cs
Titanium.Web.Proxy/Helpers/HttpResponseWriter.cs
+2
-2
HeaderParser.cs
Titanium.Web.Proxy/Http/HeaderParser.cs
+1
-1
SslCiphers.cs
Titanium.Web.Proxy/Models/SslCiphers.cs
+337
-0
SslExtension.cs
Titanium.Web.Proxy/Models/SslExtension.cs
+21
-0
CustomBinaryReader.cs
Titanium.Web.Proxy/Network/CustomBinaryReader.cs
+140
-0
CustomBufferedPeekStream.cs
Titanium.Web.Proxy/Network/CustomBufferedPeekStream.cs
+73
-0
CustomBufferedStream.cs
Titanium.Web.Proxy/Network/CustomBufferedStream.cs
+534
-0
IBufferedStream.cs
Titanium.Web.Proxy/Network/IBufferedStream.cs
+15
-0
ProxyServer.cs
Titanium.Web.Proxy/ProxyServer.cs
+1
-2
RequestHandler.cs
Titanium.Web.Proxy/RequestHandler.cs
+91
-7
ServerHelloInfo.cs
Titanium.Web.Proxy/ServerHelloInfo.cs
+107
-0
SslExtensions.cs
Titanium.Web.Proxy/SslExtensions.cs
+397
-0
SslTools.cs
Titanium.Web.Proxy/SslTools.cs
+266
-0
No files found.
Titanium.Web.Proxy/ClientHelloInfo.cs
0 → 100644
View file @
0d8dbf74
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
Titanium.Web.Proxy.Models
;
namespace
Titanium.Web.Proxy
{
public
class
ClientHelloInfo
{
private
static
readonly
string
[]
compressions
=
{
"null"
,
"DEFLATE"
};
public
int
MajorVersion
{
get
;
set
;
}
public
int
MinorVersion
{
get
;
set
;
}
public
byte
[]
Random
{
get
;
set
;
}
public
DateTime
Time
{
get
{
DateTime
time
=
DateTime
.
MinValue
;
if
(
Random
.
Length
>
3
)
{
time
=
new
DateTime
(
1970
,
1
,
1
,
0
,
0
,
0
,
0
,
DateTimeKind
.
Utc
)
.
AddSeconds
(((
uint
)
Random
[
3
]
<<
24
)
+
((
uint
)
Random
[
2
]
<<
16
)
+
((
uint
)
Random
[
1
]
<<
8
)
+
(
uint
)
Random
[
0
]).
ToLocalTime
();
}
return
time
;
}
}
public
byte
[]
SessionId
{
get
;
set
;
}
public
int
[]
Ciphers
{
get
;
set
;
}
public
byte
[]
CompressionData
{
get
;
set
;
}
internal
int
ClientHelloLength
{
get
;
set
;
}
internal
int
EntensionsStartPosition
{
get
;
set
;
}
public
Dictionary
<
string
,
SslExtension
>
Extensions
{
get
;
set
;
}
private
static
string
SslVersionToString
(
int
major
,
int
minor
)
{
string
str
=
"Unknown"
;
if
(
major
==
3
&&
minor
==
3
)
str
=
"TLS/1.2"
;
else
if
(
major
==
3
&&
minor
==
2
)
str
=
"TLS/1.1"
;
else
if
(
major
==
3
&&
minor
==
1
)
str
=
"TLS/1.0"
;
else
if
(
major
==
3
&&
minor
==
0
)
str
=
"SSL/3.0"
;
else
if
(
major
==
2
&&
minor
==
0
)
str
=
"SSL/2.0"
;
return
$"
{
major
}
.
{
minor
}
(
{
str
}
)"
;
}
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public
override
string
ToString
()
{
var
sb
=
new
StringBuilder
();
sb
.
AppendLine
(
"A SSLv3-compatible ClientHello handshake was found. Titanium extracted the parameters below."
);
sb
.
AppendLine
();
sb
.
AppendLine
(
$"Version:
{
SslVersionToString
(
MajorVersion
,
MinorVersion
)}
"
);
sb
.
AppendLine
(
$"Random:
{
string
.
Join
(
" "
,
Random
.
Select
(
x
=>
x
.
ToString
(
"X2"
)))}
"
);
sb
.
AppendLine
(
$"\"Time\":
{
Time
}
"
);
sb
.
AppendLine
(
$"SessionID:
{
string
.
Join
(
" "
,
SessionId
.
Select
(
x
=>
x
.
ToString
(
"X2"
)))}
"
);
if
(
Extensions
!=
null
)
{
sb
.
AppendLine
(
"Extensions:"
);
foreach
(
var
extension
in
Extensions
.
Values
.
OrderBy
(
x
=>
x
.
Position
))
{
sb
.
AppendLine
(
$"
{
extension
.
Name
}
:
{
extension
.
Data
}
"
);
}
}
if
(
CompressionData
.
Length
>
0
)
{
int
compressionMethod
=
CompressionData
[
0
];
string
compression
=
compressions
.
Length
>
compressionMethod
?
compressions
[
compressionMethod
]
:
$"unknown [0x
{
compressionMethod
:
X2
}
]"
;
sb
.
AppendLine
(
$"Compression:
{
compression
}
"
);
}
if
(
Ciphers
.
Length
>
0
)
{
sb
.
AppendLine
(
"Ciphers:"
);
foreach
(
int
cipherSuite
in
Ciphers
)
{
if
(!
SslCiphers
.
Ciphers
.
TryGetValue
(
cipherSuite
,
out
string
cipherStr
))
{
cipherStr
=
"unknown"
;
}
sb
.
AppendLine
(
$"[0x
{
cipherSuite
:
X4
}
]
{
cipherStr
}
"
);
}
}
return
sb
.
ToString
();
}
}
}
\ No newline at end of file
Titanium.Web.Proxy/Extensions/StreamExtensions.cs
View file @
0d8dbf74
using
StreamExtended.Network
;
using
System
;
using
System
;
using
System.Globalization
;
using
System.Globalization
;
using
System.IO
;
using
System.IO
;
using
System.Threading.Tasks
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Network
;
namespace
Titanium.Web.Proxy.Extensions
namespace
Titanium.Web.Proxy.Extensions
{
{
...
...
Titanium.Web.Proxy/Helpers/HttpResponseWriter.cs
View file @
0d8dbf74
using
StreamExtended.Network
;
using
System
;
using
System
;
using
System.Globalization
;
using
System.Globalization
;
using
System.IO
;
using
System.IO
;
using
System.Text
;
using
System.Text
;
using
System.Threading.Tasks
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Network
;
using
Titanium.Web.Proxy.Shared
;
using
Titanium.Web.Proxy.Shared
;
namespace
Titanium.Web.Proxy.Helpers
namespace
Titanium.Web.Proxy.Helpers
...
...
Titanium.Web.Proxy/Http/HeaderParser.cs
View file @
0d8dbf74
using
System
;
using
System
;
using
StreamExtended.Network
;
using
System.Collections.Generic
;
using
System.Collections.Generic
;
using
System.Threading.Tasks
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Network
;
using
Titanium.Web.Proxy.Shared
;
using
Titanium.Web.Proxy.Shared
;
namespace
Titanium.Web.Proxy.Http
namespace
Titanium.Web.Proxy.Http
...
...
Titanium.Web.Proxy/Models/SslCiphers.cs
0 → 100644
View file @
0d8dbf74
This diff is collapsed.
Click to expand it.
Titanium.Web.Proxy/Models/SslExtension.cs
0 → 100644
View file @
0d8dbf74
namespace
Titanium.Web.Proxy.Models
{
public
class
SslExtension
{
public
int
Value
{
get
;
}
public
string
Name
{
get
;
}
public
string
Data
{
get
;
}
public
int
Position
{
get
;
}
public
SslExtension
(
int
value
,
string
name
,
string
data
,
int
position
)
{
Value
=
value
;
Name
=
name
;
Data
=
data
;
Position
=
position
;
}
}
}
\ No newline at end of file
Titanium.Web.Proxy/Network/CustomBinaryReader.cs
0 → 100644
View file @
0d8dbf74
using
System
;
using
System.Collections.Generic
;
using
System.Text
;
using
System.Threading.Tasks
;
using
StreamExtended.Helpers
;
namespace
Titanium.Web.Proxy.Network
{
/// <summary>
/// A custom binary reader that would allo us to read string line by line
/// using the specified encoding
/// as well as to read bytes as required
/// </summary>
public
class
CustomBinaryReader
:
IDisposable
{
private
readonly
IBufferedStream
stream
;
private
readonly
Encoding
encoding
;
private
bool
disposed
;
public
byte
[]
Buffer
{
get
;
}
public
CustomBinaryReader
(
IBufferedStream
stream
,
int
bufferSize
)
{
this
.
stream
=
stream
;
Buffer
=
BufferPool
.
GetBuffer
(
bufferSize
);
//default to UTF-8
encoding
=
Encoding
.
UTF8
;
}
/// <summary>
/// Read a line from the byte stream
/// </summary>
/// <returns></returns>
public
async
Task
<
string
>
ReadLineAsync
()
{
byte
lastChar
=
default
(
byte
);
int
bufferDataLength
=
0
;
// try to use the thread static buffer, usually it is enough
var
buffer
=
Buffer
;
while
(
stream
.
DataAvailable
||
await
stream
.
FillBufferAsync
())
{
byte
newChar
=
stream
.
ReadByteFromBuffer
();
buffer
[
bufferDataLength
]
=
newChar
;
//if new line
if
(
lastChar
==
'\r'
&&
newChar
==
'\n'
)
{
return
encoding
.
GetString
(
buffer
,
0
,
bufferDataLength
-
1
);
}
//end of stream
if
(
newChar
==
'\0'
)
{
return
encoding
.
GetString
(
buffer
,
0
,
bufferDataLength
);
}
bufferDataLength
++;
//store last char for new line comparison
lastChar
=
newChar
;
if
(
bufferDataLength
==
buffer
.
Length
)
{
ResizeBuffer
(
ref
buffer
,
bufferDataLength
*
2
);
}
}
if
(
bufferDataLength
==
0
)
{
return
null
;
}
return
encoding
.
GetString
(
buffer
,
0
,
bufferDataLength
);
}
/// <summary>
/// Read until the last new line
/// </summary>
/// <returns></returns>
public
async
Task
<
List
<
string
>>
ReadAllLinesAsync
()
{
string
tmpLine
;
var
requestLines
=
new
List
<
string
>();
while
(!
string
.
IsNullOrEmpty
(
tmpLine
=
await
ReadLineAsync
()))
{
requestLines
.
Add
(
tmpLine
);
}
return
requestLines
;
}
/// <summary>
/// Read until the last new line, ignores the result
/// </summary>
/// <returns></returns>
public
async
Task
ReadAndIgnoreAllLinesAsync
()
{
while
(!
string
.
IsNullOrEmpty
(
await
ReadLineAsync
()))
{
}
}
/// <summary>
/// Read the specified number (or less) of raw bytes from the base stream to the given buffer
/// </summary>
/// <param name="buffer"></param>
/// <param name="bytesToRead"></param>
/// <returns>The number of bytes read</returns>
public
Task
<
int
>
ReadBytesAsync
(
byte
[]
buffer
,
int
bytesToRead
)
{
return
stream
.
ReadAsync
(
buffer
,
0
,
bytesToRead
);
}
public
void
Dispose
()
{
if
(!
disposed
)
{
disposed
=
true
;
BufferPool
.
ReturnBuffer
(
Buffer
);
}
}
/// <summary>
/// Increase size of buffer and copy existing content to new buffer
/// </summary>
/// <param name="buffer"></param>
/// <param name="size"></param>
private
void
ResizeBuffer
(
ref
byte
[]
buffer
,
long
size
)
{
var
newBuffer
=
new
byte
[
size
];
System
.
Buffer
.
BlockCopy
(
buffer
,
0
,
newBuffer
,
0
,
buffer
.
Length
);
buffer
=
newBuffer
;
}
}
}
Titanium.Web.Proxy/Network/CustomBufferedPeekStream.cs
0 → 100644
View file @
0d8dbf74
using
System.Threading.Tasks
;
namespace
Titanium.Web.Proxy.Network
{
public
class
CustomBufferedPeekStream
:
IBufferedStream
{
private
readonly
CustomBufferedStream
baseStream
;
internal
int
Position
{
get
;
private
set
;
}
public
CustomBufferedPeekStream
(
CustomBufferedStream
baseStream
,
int
startPosition
=
0
)
{
this
.
baseStream
=
baseStream
;
Position
=
startPosition
;
}
internal
int
Available
=>
baseStream
.
Available
-
Position
;
bool
IBufferedStream
.
DataAvailable
=>
Available
>
0
;
internal
async
Task
<
bool
>
EnsureBufferLength
(
int
length
)
{
var
val
=
await
baseStream
.
PeekByteAsync
(
Position
+
length
-
1
);
return
val
!=
-
1
;
}
internal
byte
ReadByte
()
{
return
baseStream
.
PeekByteFromBuffer
(
Position
++);
}
internal
int
ReadInt16
()
{
int
i1
=
ReadByte
();
int
i2
=
ReadByte
();
return
(
i1
<<
8
)
+
i2
;
}
internal
int
ReadInt24
()
{
int
i1
=
ReadByte
();
int
i2
=
ReadByte
();
int
i3
=
ReadByte
();
return
(
i1
<<
16
)
+
(
i2
<<
8
)
+
i3
;
}
internal
byte
[]
ReadBytes
(
int
length
)
{
var
buffer
=
new
byte
[
length
];
for
(
int
i
=
0
;
i
<
buffer
.
Length
;
i
++)
{
buffer
[
i
]
=
ReadByte
();
}
return
buffer
;
}
Task
<
bool
>
IBufferedStream
.
FillBufferAsync
()
{
return
baseStream
.
FillBufferAsync
();
}
byte
IBufferedStream
.
ReadByteFromBuffer
()
{
return
ReadByte
();
}
Task
<
int
>
IBufferedStream
.
ReadAsync
(
byte
[]
buffer
,
int
offset
,
int
count
)
{
return
baseStream
.
ReadAsync
(
buffer
,
offset
,
count
);
}
}
}
Titanium.Web.Proxy/Network/CustomBufferedStream.cs
0 → 100644
View file @
0d8dbf74
This diff is collapsed.
Click to expand it.
Titanium.Web.Proxy/Network/IBufferedStream.cs
0 → 100644
View file @
0d8dbf74
using
System.Threading.Tasks
;
namespace
Titanium.Web.Proxy.Network
{
public
interface
IBufferedStream
{
bool
DataAvailable
{
get
;
}
Task
<
bool
>
FillBufferAsync
();
byte
ReadByteFromBuffer
();
Task
<
int
>
ReadAsync
(
byte
[]
buffer
,
int
offset
,
int
count
);
}
}
\ No newline at end of file
Titanium.Web.Proxy/ProxyServer.cs
View file @
0d8dbf74
using
StreamExtended.Network
;
using
System
;
using
System
;
using
System.Collections.Generic
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Linq
;
using
System.Net
;
using
System.Net
;
...
...
Titanium.Web.Proxy/RequestHandler.cs
View file @
0d8dbf74
using
StreamExtended
;
using
System
;
using
StreamExtended.Network
;
using
System
;
using
System.IO
;
using
System.IO
;
using
System.Linq
;
using
System.Linq
;
using
System.Net
;
using
System.Net
;
...
@@ -8,12 +6,14 @@ using System.Net.Security;
...
@@ -8,12 +6,14 @@ using System.Net.Security;
using
System.Net.Sockets
;
using
System.Net.Sockets
;
using
System.Security.Authentication
;
using
System.Security.Authentication
;
using
System.Threading.Tasks
;
using
System.Threading.Tasks
;
using
StreamExtended.Helpers
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Exceptions
;
using
Titanium.Web.Proxy.Exceptions
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Network
;
using
Titanium.Web.Proxy.Network.Tcp
;
using
Titanium.Web.Proxy.Network.Tcp
;
namespace
Titanium.Web.Proxy
namespace
Titanium.Web.Proxy
...
@@ -233,7 +233,12 @@ namespace Titanium.Web.Proxy
...
@@ -233,7 +233,12 @@ namespace Titanium.Web.Proxy
var
sslStream
=
new
SslStream
(
clientStream
);
var
sslStream
=
new
SslStream
(
clientStream
);
clientStream
=
new
CustomBufferedStream
(
sslStream
,
BufferSize
);
clientStream
=
new
CustomBufferedStream
(
sslStream
,
BufferSize
);
string
sniHostName
=
clientSslHelloInfo
.
Extensions
?.
FirstOrDefault
(
x
=>
x
.
Name
==
"server_name"
)?.
Data
;
string
sniHostName
=
null
;
if
(
clientSslHelloInfo
.
Extensions
!=
null
&&
clientSslHelloInfo
.
Extensions
.
TryGetValue
(
"server_name"
,
out
var
serverNameExtension
))
{
sniHostName
=
serverNameExtension
.
Data
;
}
string
certName
=
HttpHelper
.
GetWildCardDomainName
(
sniHostName
??
endPoint
.
GenericCertificateName
);
string
certName
=
HttpHelper
.
GetWildCardDomainName
(
sniHostName
??
endPoint
.
GenericCertificateName
);
var
certificate
=
CertificateManager
.
CreateCertificate
(
certName
,
false
);
var
certificate
=
CertificateManager
.
CreateCertificate
(
certName
,
false
);
...
@@ -628,9 +633,25 @@ namespace Titanium.Web.Proxy
...
@@ -628,9 +633,25 @@ namespace Titanium.Web.Proxy
if
(
args
.
HasMulipartEventSubscribers
&&
request
.
IsMultipartFormData
)
if
(
args
.
HasMulipartEventSubscribers
&&
request
.
IsMultipartFormData
)
{
{
string
boundary
=
HttpHelper
.
GetBoundaryFromContentType
(
request
.
ContentType
);
string
boundary
=
HttpHelper
.
GetBoundaryFromContentType
(
request
.
ContentType
);
args
.
OnMultipartRequestPartSent
(
boundary
);
// todo
long
bytesToSend
=
args
.
WebSession
.
Request
.
ContentLength
;
await
args
.
ProxyClient
.
ClientStreamReader
.
CopyBytesToStream
(
postStream
,
args
.
WebSession
.
Request
.
ContentLength
);
while
(
bytesToSend
>
0
)
{
long
read
=
await
SendUntilBoundaryAsync
(
args
.
ProxyClient
.
ClientStream
,
postStream
,
bytesToSend
,
boundary
);
if
(
read
==
0
)
{
break
;
}
bytesToSend
-=
read
;
if
(
bytesToSend
>
0
)
{
var
headers
=
new
HeaderCollection
();
var
stream
=
new
CustomBufferedPeekStream
(
args
.
ProxyClient
.
ClientStream
);
await
HeaderParser
.
ReadHeaders
(
new
CustomBinaryReader
(
stream
,
BufferSize
),
headers
);
args
.
OnMultipartRequestPartSent
(
boundary
,
headers
);
}
}
}
}
else
else
{
{
...
@@ -644,5 +665,68 @@ namespace Titanium.Web.Proxy
...
@@ -644,5 +665,68 @@ namespace Titanium.Web.Proxy
await
args
.
ProxyClient
.
ClientStreamReader
.
CopyBytesToStreamChunked
(
postStream
);
await
args
.
ProxyClient
.
ClientStreamReader
.
CopyBytesToStreamChunked
(
postStream
);
}
}
}
}
/// <summary>
/// Read a line from the byte stream
/// </summary>
/// <returns></returns>
public
async
Task
<
long
>
SendUntilBoundaryAsync
(
CustomBufferedStream
inputStream
,
CustomBufferedStream
outputStream
,
long
totalBytesToRead
,
string
boundary
)
{
int
bufferDataLength
=
0
;
// try to use the thread static buffer
var
buffer
=
BufferPool
.
GetBuffer
(
BufferSize
);
int
boundaryLength
=
boundary
.
Length
+
4
;
long
bytesRead
=
0
;
while
(
bytesRead
<
totalBytesToRead
&&
(
inputStream
.
DataAvailable
||
await
inputStream
.
FillBufferAsync
()))
{
byte
newChar
=
inputStream
.
ReadByteFromBuffer
();
buffer
[
bufferDataLength
]
=
newChar
;
bufferDataLength
++;
bytesRead
++;
if
(
bufferDataLength
>=
boundaryLength
)
{
int
startIdx
=
bufferDataLength
-
boundaryLength
;
if
(
buffer
[
startIdx
]
==
'-'
&&
buffer
[
startIdx
+
1
]
==
'-'
&&
buffer
[
bufferDataLength
-
2
]
==
'\r'
&&
buffer
[
bufferDataLength
-
1
]
==
'\n'
)
{
startIdx
+=
2
;
bool
ok
=
true
;
for
(
int
i
=
0
;
i
<
boundary
.
Length
;
i
++)
{
if
(
buffer
[
startIdx
+
i
]
!=
boundary
[
i
])
{
ok
=
false
;
break
;
}
}
if
(
ok
)
{
break
;
}
}
}
if
(
bufferDataLength
==
buffer
.
Length
)
{
//boundary is not longer than 70 bytes accounrding to the specification, so keeping the last 100 (minimum 74) bytes is enough
const
int
bytesToKeep
=
100
;
await
outputStream
.
WriteAsync
(
buffer
,
0
,
buffer
.
Length
-
bytesToKeep
);
Buffer
.
BlockCopy
(
buffer
,
buffer
.
Length
-
bytesToKeep
,
buffer
,
0
,
bytesToKeep
);
bufferDataLength
=
bytesToKeep
;
}
}
if
(
bytesRead
>
0
)
{
await
outputStream
.
WriteAsync
(
buffer
,
0
,
bufferDataLength
);
}
return
bytesRead
;
}
}
}
}
}
Titanium.Web.Proxy/ServerHelloInfo.cs
0 → 100644
View file @
0d8dbf74
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
Titanium.Web.Proxy.Models
;
namespace
Titanium.Web.Proxy
{
public
class
ServerHelloInfo
{
private
static
readonly
string
[]
compressions
=
{
"null"
,
"DEFLATE"
};
public
int
MajorVersion
{
get
;
set
;
}
public
int
MinorVersion
{
get
;
set
;
}
public
byte
[]
Random
{
get
;
set
;
}
public
DateTime
Time
{
get
{
DateTime
time
=
DateTime
.
MinValue
;
if
(
Random
.
Length
>
3
)
{
time
=
new
DateTime
(
1970
,
1
,
1
,
0
,
0
,
0
,
0
,
DateTimeKind
.
Utc
)
.
AddSeconds
(((
uint
)
Random
[
3
]
<<
24
)
+
((
uint
)
Random
[
2
]
<<
16
)
+
((
uint
)
Random
[
1
]
<<
8
)
+
(
uint
)
Random
[
0
]).
ToLocalTime
();
}
return
time
;
}
}
public
byte
[]
SessionId
{
get
;
set
;
}
public
int
CipherSuite
{
get
;
set
;
}
public
byte
CompressionMethod
{
get
;
set
;
}
internal
int
ServerHelloLength
{
get
;
set
;
}
internal
int
EntensionsStartPosition
{
get
;
set
;
}
public
Dictionary
<
string
,
SslExtension
>
Extensions
{
get
;
set
;
}
private
static
string
SslVersionToString
(
int
major
,
int
minor
)
{
string
str
=
"Unknown"
;
if
(
major
==
3
&&
minor
==
3
)
str
=
"TLS/1.2"
;
else
if
(
major
==
3
&&
minor
==
2
)
str
=
"TLS/1.1"
;
else
if
(
major
==
3
&&
minor
==
1
)
str
=
"TLS/1.0"
;
else
if
(
major
==
3
&&
minor
==
0
)
str
=
"SSL/3.0"
;
else
if
(
major
==
2
&&
minor
==
0
)
str
=
"SSL/2.0"
;
return
$"
{
major
}
.
{
minor
}
(
{
str
}
)"
;
}
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public
override
string
ToString
()
{
var
sb
=
new
StringBuilder
();
sb
.
AppendLine
(
"A SSLv3-compatible ServerHello handshake was found. Titanium extracted the parameters below."
);
sb
.
AppendLine
();
sb
.
AppendLine
(
$"Version:
{
SslVersionToString
(
MajorVersion
,
MinorVersion
)}
"
);
sb
.
AppendLine
(
$"Random:
{
string
.
Join
(
" "
,
Random
.
Select
(
x
=>
x
.
ToString
(
"X2"
)))}
"
);
sb
.
AppendLine
(
$"\"Time\":
{
Time
}
"
);
sb
.
AppendLine
(
$"SessionID:
{
string
.
Join
(
" "
,
SessionId
.
Select
(
x
=>
x
.
ToString
(
"X2"
)))}
"
);
if
(
Extensions
!=
null
)
{
sb
.
AppendLine
(
"Extensions:"
);
foreach
(
var
extension
in
Extensions
.
Values
.
OrderBy
(
x
=>
x
.
Position
))
{
sb
.
AppendLine
(
$"
{
extension
.
Name
}
:
{
extension
.
Data
}
"
);
}
}
string
compression
=
compressions
.
Length
>
CompressionMethod
?
compressions
[
CompressionMethod
]
:
$"unknown [0x
{
CompressionMethod
:
X2
}
]"
;
sb
.
AppendLine
(
$"Compression:
{
compression
}
"
);
sb
.
Append
(
"Cipher:"
);
if
(!
SslCiphers
.
Ciphers
.
TryGetValue
(
CipherSuite
,
out
string
cipherStr
))
{
cipherStr
=
"unknown"
;
}
sb
.
AppendLine
(
$"[0x
{
CipherSuite
:
X4
}
]
{
cipherStr
}
"
);
return
sb
.
ToString
();
}
}
}
\ No newline at end of file
Titanium.Web.Proxy/SslExtensions.cs
0 → 100644
View file @
0d8dbf74
This diff is collapsed.
Click to expand it.
Titanium.Web.Proxy/SslTools.cs
0 → 100644
View file @
0d8dbf74
using
System.Collections.Generic
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Network
;
namespace
Titanium.Web.Proxy
{
public
class
SslTools
{
public
static
async
Task
<
bool
>
IsClientHello
(
CustomBufferedStream
stream
)
{
var
clientHello
=
await
PeekClientHello
(
stream
);
return
clientHello
!=
null
;
}
public
static
async
Task
<
ClientHelloInfo
>
PeekClientHello
(
CustomBufferedStream
clientStream
)
{
//detects the HTTPS ClientHello message as it is described in the following url:
//https://stackoverflow.com/questions/3897883/how-to-detect-an-incoming-ssl-https-handshake-ssl-wire-format
int
recordType
=
await
clientStream
.
PeekByteAsync
(
0
);
if
(
recordType
==
0x80
)
{
var
peekStream
=
new
CustomBufferedPeekStream
(
clientStream
,
1
);
//SSL 2
int
length
=
peekStream
.
ReadByte
();
if
(
length
<
9
)
{
// Message body too short.
return
null
;
}
if
(
peekStream
.
ReadByte
()
!=
0x01
)
{
// should be ClientHello
return
null
;
}
int
majorVersion
=
clientStream
.
ReadByte
();
int
minorVersion
=
clientStream
.
ReadByte
();
return
new
ClientHelloInfo
();
}
else
if
(
recordType
==
0x16
)
{
var
peekStream
=
new
CustomBufferedPeekStream
(
clientStream
,
1
);
//should contain at least 43 bytes
// 2 version + 2 length + 1 type + 3 length(?) + 2 version + 32 random + 1 sessionid length
if
(!
await
peekStream
.
EnsureBufferLength
(
43
))
{
return
null
;
}
//SSL 3.0 or TLS 1.0, 1.1 and 1.2
int
majorVersion
=
peekStream
.
ReadByte
();
int
minorVersion
=
peekStream
.
ReadByte
();
int
length
=
peekStream
.
ReadInt16
();
if
(
peekStream
.
ReadByte
()
!=
0x01
)
{
// should be ClientHello
return
null
;
}
length
=
peekStream
.
ReadInt24
();
majorVersion
=
peekStream
.
ReadByte
();
minorVersion
=
peekStream
.
ReadByte
();
byte
[]
random
=
peekStream
.
ReadBytes
(
32
);
length
=
peekStream
.
ReadByte
();
// sessionid + 2 ciphersData length
if
(!
await
peekStream
.
EnsureBufferLength
(
length
+
2
))
{
return
null
;
}
byte
[]
sessionId
=
peekStream
.
ReadBytes
(
length
);
length
=
peekStream
.
ReadInt16
();
// ciphersData + compressionData length
if
(!
await
peekStream
.
EnsureBufferLength
(
length
+
1
))
{
return
null
;
}
byte
[]
ciphersData
=
peekStream
.
ReadBytes
(
length
);
int
[]
ciphers
=
new
int
[
ciphersData
.
Length
/
2
];
for
(
int
i
=
0
;
i
<
ciphers
.
Length
;
i
++)
{
ciphers
[
i
]
=
(
ciphersData
[
2
*
i
]
<<
8
)
+
ciphersData
[
2
*
i
+
1
];
}
length
=
peekStream
.
ReadByte
();
if
(
length
<
1
)
{
return
null
;
}
// compressionData
if
(!
await
peekStream
.
EnsureBufferLength
(
length
))
{
return
null
;
}
byte
[]
compressionData
=
peekStream
.
ReadBytes
(
length
);
int
extenstionsStartPosition
=
peekStream
.
Position
;
var
extensions
=
await
ReadExtensions
(
majorVersion
,
minorVersion
,
peekStream
);
var
clientHelloInfo
=
new
ClientHelloInfo
{
MajorVersion
=
majorVersion
,
MinorVersion
=
minorVersion
,
Random
=
random
,
SessionId
=
sessionId
,
Ciphers
=
ciphers
,
CompressionData
=
compressionData
,
ClientHelloLength
=
peekStream
.
Position
,
EntensionsStartPosition
=
extenstionsStartPosition
,
Extensions
=
extensions
,
};
return
clientHelloInfo
;
}
return
null
;
}
private
static
async
Task
<
Dictionary
<
string
,
SslExtension
>>
ReadExtensions
(
int
majorVersion
,
int
minorVersion
,
CustomBufferedPeekStream
peekStream
)
{
Dictionary
<
string
,
SslExtension
>
extensions
=
null
;
if
(
majorVersion
>
3
||
majorVersion
==
3
&&
minorVersion
>=
1
)
{
if
(
await
peekStream
.
EnsureBufferLength
(
2
))
{
int
extensionsLength
=
peekStream
.
ReadInt16
();
if
(
await
peekStream
.
EnsureBufferLength
(
extensionsLength
))
{
extensions
=
new
Dictionary
<
string
,
SslExtension
>();
int
idx
=
0
;
while
(
extensionsLength
>
3
)
{
int
id
=
peekStream
.
ReadInt16
();
int
length
=
peekStream
.
ReadInt16
();
byte
[]
data
=
peekStream
.
ReadBytes
(
length
);
var
extension
=
SslExtensions
.
GetExtension
(
id
,
data
,
idx
++);
extensions
[
extension
.
Name
]
=
extension
;
extensionsLength
-=
4
+
length
;
}
}
}
}
return
extensions
;
}
public
static
async
Task
<
bool
>
IsServerHello
(
CustomBufferedStream
stream
)
{
var
serverHello
=
await
PeekServerHello
(
stream
);
return
serverHello
!=
null
;
}
public
static
async
Task
<
ServerHelloInfo
>
PeekServerHello
(
CustomBufferedStream
serverStream
)
{
//detects the HTTPS ClientHello message as it is described in the following url:
//https://stackoverflow.com/questions/3897883/how-to-detect-an-incoming-ssl-https-handshake-ssl-wire-format
int
recordType
=
await
serverStream
.
PeekByteAsync
(
0
);
if
(
recordType
==
0x80
)
{
// copied from client hello, not tested. SSL2 is deprecated
var
peekStream
=
new
CustomBufferedPeekStream
(
serverStream
,
1
);
//SSL 2
int
length
=
peekStream
.
ReadByte
();
if
(
length
<
9
)
{
// Message body too short.
return
null
;
}
if
(
peekStream
.
ReadByte
()
!=
0x02
)
{
// should be ServerHello
return
null
;
}
int
majorVersion
=
serverStream
.
ReadByte
();
int
minorVersion
=
serverStream
.
ReadByte
();
return
new
ServerHelloInfo
();
}
else
if
(
recordType
==
0x16
)
{
var
peekStream
=
new
CustomBufferedPeekStream
(
serverStream
,
1
);
//should contain at least 43 bytes
// 2 version + 2 length + 1 type + 3 length(?) + 2 version + 32 random + 1 sessionid length
if
(!
await
peekStream
.
EnsureBufferLength
(
43
))
{
return
null
;
}
//SSL 3.0 or TLS 1.0, 1.1 and 1.2
int
majorVersion
=
peekStream
.
ReadByte
();
int
minorVersion
=
peekStream
.
ReadByte
();
int
length
=
peekStream
.
ReadInt16
();
if
(
peekStream
.
ReadByte
()
!=
0x02
)
{
// should be ServerHello
return
null
;
}
length
=
peekStream
.
ReadInt24
();
majorVersion
=
peekStream
.
ReadByte
();
minorVersion
=
peekStream
.
ReadByte
();
byte
[]
random
=
peekStream
.
ReadBytes
(
32
);
length
=
peekStream
.
ReadByte
();
// sessionid + cipherSuite + compressionMethod
if
(!
await
peekStream
.
EnsureBufferLength
(
length
+
2
+
1
))
{
return
null
;
}
byte
[]
sessionId
=
peekStream
.
ReadBytes
(
length
);
int
cipherSuite
=
peekStream
.
ReadInt16
();
byte
compressionMethod
=
peekStream
.
ReadByte
();
int
extenstionsStartPosition
=
peekStream
.
Position
;
var
extensions
=
await
ReadExtensions
(
majorVersion
,
minorVersion
,
peekStream
);
//var rawBytes = new CustomBufferedPeekStream(serverStream).ReadBytes(peekStream.Position);
var
serverHelloInfo
=
new
ServerHelloInfo
{
MajorVersion
=
majorVersion
,
MinorVersion
=
minorVersion
,
Random
=
random
,
SessionId
=
sessionId
,
CipherSuite
=
cipherSuite
,
CompressionMethod
=
compressionMethod
,
ServerHelloLength
=
peekStream
.
Position
,
EntensionsStartPosition
=
extenstionsStartPosition
,
Extensions
=
extensions
,
};
return
serverHelloInfo
;
}
return
null
;
}
}
}
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