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  }