C++ and C# 从TLS握手二进制中获取SNI(服务器名称指示)域名

95 篇文章 2 订阅

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 "";
    }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值