TLS客户端首帧连接TLS服务器时,会发送 ClientHello 消息请求服务器进行SSL/TLS握手,早年的SSL/TLS协议是不提供SNI扩展的,但后续因为服务器上需求SNI区分域的必要性,SSL/TLS协议标准制定了SNI扩展标准,发展到今天没有SNI的SSL/TLS连接几乎都不能被大多数的SSL/TLS服务器受理处理。
从TLS握手消息中提取SNI域,我们可以实现CDN(内容分发网络/内容定义网络)或SNI正向代理服务器,如果不替换源域SSL/TLS证书则为渗透性TLS正向代理,不替换SSL/TLS证书非渗透性TLS正向通常就称为基于SSL/TLS SNI的正向代理。
C#
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 5)]
private struct tls_hdr
{
public byte ContentType;
public ushort Version;
public ushort Length;
};
#if NETCOREAPP
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static string GetHostNameByTlsClientHelloHandshake(BufferSegment messages)
{
if (messages == null ||
messages.Offset < 0 ||
messages.Length < sizeof(tls_hdr) ||
messages.Buffer == null ||
messages.Buffer.Length < (messages.Offset + messages.Length))
{
return null;
}
try
{
fixed (byte* pinned = &messages.Buffer[messages.Offset])
{
tls_hdr* hdr = (tls_hdr*)pinned;
if (hdr->ContentType != 0x16) // Content Type: TLS handshake
{
return null;
}
int tls_payload = CheckSum.htons(hdr->Length);
if ((tls_payload + sizeof(tls_hdr)) != messages.Length)
{
return null;
}
byte* data = (byte*)(hdr + 1);
if (*data++ != 0x01) // Handshake Type: Client Hello (1)
{
return null;
}
int length = Math.Max(0, *data++ << 16 | *data++ << 8 | *data++);
if ((length + 4) != tls_payload)
{
return null;
}
// Skip Version
data += 2;
// Skip Random
data += 32;
// Skip Session ID
byte Session_ID_Length = Math.Max((byte)0, *data++);
data += Session_ID_Length;
// Skip Cipher Suites
int Cipher_Suites_Length = Math.Max(0, *data++ << 8 | *data++);
data += Cipher_Suites_Length;
// Skip Compression Methods Length
int Compression_Methods_Length = *data++;
data += Compression_Methods_Length;
// Extensions Length
int Extensions_Length = Math.Max(0, *data++ << 8 | *data++);
byte* Extensions_End = data + Extensions_Length;
while (data < Extensions_End)
{
int Extension_Type = *data++ << 8 | *data++;
int Extension_Length = Math.Max(0, *data++ << 8 | *data++);
if (Extension_Type == 0x0000) // RFC4366/6066(Server Name Indication extension)
{
int Server_Name_list_length = Math.Max(0, *data++ << 8 | *data++);
if ((data + Server_Name_list_length) >= Extensions_End)
{
break;
}
int Server_Name_Type = *data++;
if (Server_Name_Type != 0x00) // RFC6066 NameType::host_name(0)
{
data += 2;
continue;
}
int Server_Name_length = Math.Max(0, *data++ << 8 | *data++);
if ((data + Server_Name_length) > Extensions_End)
{
break;
}
return new string((sbyte*)data, 0, Server_Name_length);
}
else
{
data += Extension_Length;
}
}
}
}
catch { }
return null;
}
C++
#pragma pack(push, 1)
struct tls_hdr {
Byte Content_Type;
UInt16 Version;
UInt16 Length;
};
#pragma pack(pop)
inline UInt16 fetch_uint16(Byte*& data) {
Byte h_ = *data++;
Byte l_ = *data++;
return (h_ << 8) | (l_);
}
inline std::string fetch_sniaddr(size_t tls_payload) {
Byte* data = (Byte*)local_socket_buf_;
if (*data++ != 0x01) { // Handshake Type: Client Hello (1)
return "";
}
int Length = std::max<int>(0, data[0] << 16 | data[1] << 8 | data[2]);
data += 3;
if ((Length + 4) != tls_payload) {
return "";
}
// Skip Version
data += 2;
// Skip Random
data += 32;
// Skip Session ID
Byte Session_ID_Length = std::max<int>((Byte)0, *data++);
data += Session_ID_Length;
// Skip Cipher Suites
int Cipher_Suites_Length = std::max<int>(0, fetch_uint16(data));
data += Cipher_Suites_Length;
// Skip Compression Methods Length
int Compression_Methods_Length = *data++;
data += Compression_Methods_Length;
// Extensions Length
int Extensions_Length = std::max<int>(0, fetch_uint16(data));
Byte* Extensions_End = data + Extensions_Length;
while (data < Extensions_End) {
int Extension_Type = fetch_uint16(data);
int Extension_Length = std::max<int>(0, fetch_uint16(data));
if (Extension_Type == 0x0000) { // RFC4366/6066(Server Name Indication extension)
int Server_Name_list_length = std::max<int>(0, fetch_uint16(data));
if ((data + Server_Name_list_length) >= Extensions_End) {
break;
}
int Server_Name_Type = *data++;
if (Server_Name_Type != 0x00) { // RFC6066 NameType::host_name(0)
data += 2;
continue;
}
int Server_Name_length = std::max<int>(0, fetch_uint16(data));
if ((data + Server_Name_length) > Extensions_End) {
break;
}
return std::string((char*)data, 0, Server_Name_length);
}
else {
data += Extension_Length;
}
}
return "";
}