mirror of
https://github.com/MatsuriDayo/NekoBoxForAndroid.git
synced 2025-12-18 22:20:06 +08:00
84 lines
2.0 KiB
Go
84 lines
2.0 KiB
Go
package ech
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"encoding/base64"
|
|
"net"
|
|
"os"
|
|
|
|
mDNS "github.com/miekg/dns"
|
|
"github.com/sagernet/sing-box/adapter"
|
|
"github.com/sagernet/sing-box/dns"
|
|
"github.com/sagernet/sing/common/exceptions"
|
|
)
|
|
|
|
type ECHClientConfig struct {
|
|
*tls.Config
|
|
domain string
|
|
localDnsTransport adapter.DNSTransport
|
|
}
|
|
|
|
func NewECHClientConfig(domain string, tlsConfig *tls.Config, localDnsTransport adapter.DNSTransport) *ECHClientConfig {
|
|
config := tlsConfig.Clone()
|
|
config.ServerName = domain
|
|
return &ECHClientConfig{
|
|
Config: config,
|
|
domain: domain,
|
|
localDnsTransport: localDnsTransport,
|
|
}
|
|
}
|
|
|
|
// ClientHandshake 封装 TLS 握手
|
|
func (s *ECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (*tls.Conn, error) {
|
|
tlsConn, err := s.fetchAndHandshake(ctx, conn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = tlsConn.HandshakeContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tlsConn, nil
|
|
}
|
|
|
|
// fetchAndHandshake 查询 ECHConfigList 并完成 TLS 连接
|
|
func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (*tls.Conn, error) {
|
|
message := &mDNS.Msg{
|
|
MsgHdr: mDNS.MsgHdr{
|
|
RecursionDesired: true,
|
|
},
|
|
Question: []mDNS.Question{
|
|
{
|
|
Name: mDNS.Fqdn(s.domain),
|
|
Qtype: mDNS.TypeHTTPS,
|
|
Qclass: mDNS.ClassINET,
|
|
},
|
|
},
|
|
}
|
|
if s.localDnsTransport == nil {
|
|
return nil, os.ErrInvalid
|
|
}
|
|
response, err := s.localDnsTransport.Exchange(ctx, message)
|
|
if err != nil {
|
|
return nil, exceptions.Cause(err, "fetch ECH config list")
|
|
}
|
|
if response.Rcode != mDNS.RcodeSuccess {
|
|
return nil, exceptions.Cause(dns.RcodeError(response.Rcode), "fetch ECH config list")
|
|
}
|
|
for _, rr := range response.Answer {
|
|
switch resource := rr.(type) {
|
|
case *mDNS.HTTPS:
|
|
for _, value := range resource.Value {
|
|
if value.Key().String() == "ech" {
|
|
echConfigList, err := base64.StdEncoding.DecodeString(value.String())
|
|
if err == nil {
|
|
s.Config.EncryptedClientHelloConfigList = echConfigList
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return tls.Client(conn, s.Config), nil
|
|
}
|