识别TLS协议:Client Hello (1)
RFC4366/6066(Server Name Indication extension)
获取SNI域名信息,这是有必要的,无论是实现域名审查还是 sni_proxy,我们都需要感知它,另外网站CDN,也是依赖于遥感SNI域名来呈现不同网站资源的。
[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 = *data++ << 24 | *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 = *data++;
data += Session_ID_Length;
// Skip Cipher Suites
int Cipher_Suites_Length = *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 = *data++ << 8 | *data++;
byte* Extensions_End = data + Extensions_Length;
while (data < Extensions_End)
{
int Extension_Type = *data++ << 8 | *data++;
int Extension_Length = *data++ << 8 | *data++;
if (Extension_Type == 0x0000) // RFC4366/6066(Server Name Indication extension)
{
int Server_Name_list_length = *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 = *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 (Exception) { }
return null;
}