github.com/imannamdari/v2ray-core/v5@v5.0.5/transport/internet/tls/engine.go (about) 1 package tls 2 3 import ( 4 "errors" 5 "fmt" 6 "time" 7 8 "github.com/miekg/dns" 9 10 "github.com/imannamdari/v2ray-core/v5/common/net" 11 "github.com/imannamdari/v2ray-core/v5/transport/internet/security" 12 ) 13 14 type Engine struct { 15 config *Config 16 } 17 18 // ech key for ech enabled config. tls config use this for tls handshake. 19 var ECH string 20 21 func (e *Engine) updateECHEvery(d time.Duration) { 22 for range time.Tick(d) { 23 ech, err := e.fetchECH() 24 fmt.Printf("new ech = %s\n", ech) 25 if err != nil { 26 fmt.Println("failed to get ech") 27 newError("failed to get ech").Base(err).AtError().WriteToLog() 28 } else { 29 ECH = ech 30 } 31 } 32 } 33 34 // only support cloudflare ech 35 func (e *Engine) fetchECH() (string, error) { 36 c := dns.Client{Timeout: 10 * time.Second} 37 38 d := dns.Fqdn("crypto.cloudflare.com") 39 q := dns.Question{ 40 Name: d, 41 Qtype: dns.TypeHTTPS, 42 Qclass: dns.ClassINET, 43 } 44 45 dnsAddr := "127.0.0.1:53" 46 if e.config.EchSetting != nil && e.config.EchSetting.DnsAddr != "" { 47 dnsAddr = e.config.EchSetting.DnsAddr 48 } 49 50 r, _, err := c.Exchange(&dns.Msg{ 51 MsgHdr: dns.MsgHdr{ 52 Id: dns.Id(), 53 RecursionDesired: true, 54 }, 55 Question: []dns.Question{q}, 56 }, dnsAddr) 57 if err != nil { 58 return "", err 59 } 60 61 for _, v := range r.Answer { 62 if vv, ok := v.(*dns.HTTPS); ok { 63 for _, vvv := range vv.SVCB.Value { 64 if vvv.Key().String() == "ech" { 65 return vvv.String(), nil 66 } 67 } 68 } 69 } 70 71 return "", errors.New("failed to find ech in response") 72 } 73 74 func (e *Engine) Client(conn net.Conn, opts ...security.Option) (security.Conn, error) { 75 var options []Option 76 for _, v := range opts { 77 switch s := v.(type) { 78 case security.OptionWithALPN: 79 options = append(options, WithNextProto(s.ALPNs...)) 80 case security.OptionWithDestination: 81 options = append(options, WithDestination(s.Dest)) 82 default: 83 return nil, newError("unknown option") 84 } 85 } 86 tlsConn := Client(conn, e.config.GetTLSConfig(options...)) 87 return tlsConn, nil 88 } 89 90 func NewTLSSecurityEngineFromConfig(config *Config) (security.Engine, error) { 91 e := &Engine{config: config} 92 93 // handle ech 94 if config.EnableEch { 95 if config.EchSetting != nil && config.EchSetting.InitEchKey != "" { 96 ECH = config.EchSetting.InitEchKey 97 } else { 98 ech, err := e.fetchECH() 99 if err != nil { 100 fmt.Println("failed to get first ech") 101 newError("failed to get first ech").Base(err).AtError().WriteToLog() 102 } else { 103 ECH = ech 104 } 105 } 106 go e.updateECHEvery(15 * time.Minute) 107 } 108 109 return e, nil 110 }