mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-23 01:56:48 +08:00
Compare commits
1 Commits
dev-ECH-in
...
dialTimeou
Author | SHA1 | Date | |
---|---|---|---|
![]() |
43b57825fa |
@@ -210,34 +210,6 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) {
|
|||||||
return nil, errors.New("returning nil for domain ", domain).Base(errors.Combine(errs...))
|
return nil, errors.New("returning nil for domain ", domain).Base(errors.Combine(errs...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DNS) LookupHTTPS(domain string) (map[string]string, error) {
|
|
||||||
errs := []error{}
|
|
||||||
ctx := session.ContextWithInbound(s.ctx, &session.Inbound{Tag: s.tag})
|
|
||||||
for _, client := range s.sortClients(domain) {
|
|
||||||
if strings.EqualFold(client.Name(), "FakeDNS") {
|
|
||||||
errors.LogDebug(s.ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
EnhancedServer, ok := client.server.(EnhancedServer)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
HTTPSRecord, err := EnhancedServer.QueryHTTPS(ctx, domain, s.disableCache)
|
|
||||||
if len(HTTPSRecord) > 0 {
|
|
||||||
return HTTPSRecord, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
errors.LogInfoInner(s.ctx, err, "failed to lookup HTTPS for domain ", domain, " at server ", client.Name())
|
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
|
||||||
// 5 for RcodeRefused in miekg/dns, hardcode to reduce binary size
|
|
||||||
if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch && err != dns.ErrEmptyResponse && dns.RCodeFromError(err) != 5 {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, errors.New("returning nil for domain ", domain).Base(errors.Combine(errs...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// LookupHosts implements dns.HostsLookup.
|
// LookupHosts implements dns.HostsLookup.
|
||||||
func (s *DNS) LookupHosts(domain string) *net.Address {
|
func (s *DNS) LookupHosts(domain string) *net.Address {
|
||||||
domain = strings.TrimSuffix(domain, ".")
|
domain = strings.TrimSuffix(domain, ".")
|
||||||
|
@@ -29,12 +29,6 @@ type record struct {
|
|||||||
AAAA *IPRecord
|
AAAA *IPRecord
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTPSRecord struct {
|
|
||||||
keypair map[string]string
|
|
||||||
Expire time.Time
|
|
||||||
RCode dnsmessage.RCode
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPRecord is a cacheable item for a resolved domain
|
// IPRecord is a cacheable item for a resolved domain
|
||||||
type IPRecord struct {
|
type IPRecord struct {
|
||||||
ReqID uint16
|
ReqID uint16
|
||||||
|
@@ -23,13 +23,6 @@ type Server interface {
|
|||||||
QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, disableCache bool) ([]net.IP, error)
|
QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, disableCache bool) ([]net.IP, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server is the interface for Enhanced Name Server.
|
|
||||||
type EnhancedServer interface {
|
|
||||||
Server
|
|
||||||
// QueryHTTPS sends HTTPS queries to its configured server.
|
|
||||||
QueryHTTPS(ctx context.Context, domain string, disableCache bool) (map[string]string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client is the interface for DNS client.
|
// Client is the interface for DNS client.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
server Server
|
server Server
|
||||||
|
@@ -12,7 +12,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdns "github.com/miekg/dns"
|
|
||||||
utls "github.com/refraction-networking/utls"
|
utls "github.com/refraction-networking/utls"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/crypto"
|
"github.com/xtls/xray-core/common/crypto"
|
||||||
@@ -43,8 +42,6 @@ type DoHNameServer struct {
|
|||||||
dohURL string
|
dohURL string
|
||||||
name string
|
name string
|
||||||
queryStrategy QueryStrategy
|
queryStrategy QueryStrategy
|
||||||
|
|
||||||
HTTPSCache map[string]*HTTPSRecord
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDoHNameServer creates DOH/DOHL client object for remote/local resolving.
|
// NewDoHNameServer creates DOH/DOHL client object for remote/local resolving.
|
||||||
@@ -61,7 +58,6 @@ func NewDoHNameServer(url *url.URL, queryStrategy QueryStrategy, dispatcher rout
|
|||||||
name: mode + "//" + url.Host,
|
name: mode + "//" + url.Host,
|
||||||
dohURL: url.String(),
|
dohURL: url.String(),
|
||||||
queryStrategy: queryStrategy,
|
queryStrategy: queryStrategy,
|
||||||
HTTPSCache: make(map[string]*HTTPSRecord),
|
|
||||||
}
|
}
|
||||||
s.cleanup = &task.Periodic{
|
s.cleanup = &task.Periodic{
|
||||||
Interval: time.Minute,
|
Interval: time.Minute,
|
||||||
@@ -211,21 +207,6 @@ func (s *DoHNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) {
|
|||||||
common.Must(s.cleanup.Start())
|
common.Must(s.cleanup.Start())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DoHNameServer) updateHTTPS(domain string, HTTPSRec *HTTPSRecord) {
|
|
||||||
s.Lock()
|
|
||||||
rec, found := s.HTTPSCache[domain]
|
|
||||||
if !found {
|
|
||||||
s.HTTPSCache[domain] = HTTPSRec
|
|
||||||
}
|
|
||||||
if found && rec.Expire.Before(time.Now()) {
|
|
||||||
s.HTTPSCache[domain] = HTTPSRec
|
|
||||||
}
|
|
||||||
errors.LogInfo(context.Background(), s.name, " got answer: ", domain, " ", "HTTPS", " -> ", HTTPSRec.keypair)
|
|
||||||
|
|
||||||
s.pub.Publish(domain+"HTTPS", nil)
|
|
||||||
s.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DoHNameServer) newReqID() uint16 {
|
func (s *DoHNameServer) newReqID() uint16 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -290,59 +271,6 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DoHNameServer) sendHTTPSQuery(ctx context.Context, domain string) {
|
|
||||||
errors.LogInfo(ctx, s.name, " querying HTTPS record for: ", domain)
|
|
||||||
var deadline time.Time
|
|
||||||
if d, ok := ctx.Deadline(); ok {
|
|
||||||
deadline = d
|
|
||||||
} else {
|
|
||||||
deadline = time.Now().Add(time.Second * 5)
|
|
||||||
}
|
|
||||||
dnsCtx := ctx
|
|
||||||
// reserve internal dns server requested Inbound
|
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
|
||||||
dnsCtx = session.ContextWithInbound(dnsCtx, inbound)
|
|
||||||
}
|
|
||||||
dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
|
|
||||||
Protocol: "https",
|
|
||||||
SkipDNSResolve: true,
|
|
||||||
})
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
m := new(mdns.Msg)
|
|
||||||
m.SetQuestion(mdns.Fqdn(domain), mdns.TypeHTTPS)
|
|
||||||
m.Id = 0
|
|
||||||
msg, _ := m.Pack()
|
|
||||||
response, err := s.dohHTTPSContext(dnsCtx, msg)
|
|
||||||
if err != nil {
|
|
||||||
errors.LogError(ctx, err, "failed to retrieve HTTPS query response for ", domain)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
respMsg := new(mdns.Msg)
|
|
||||||
err = respMsg.Unpack(response)
|
|
||||||
if err != nil {
|
|
||||||
errors.LogError(ctx, err, "failed to parse HTTPS query response for ", domain)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var Record = HTTPSRecord{
|
|
||||||
keypair: map[string]string{},
|
|
||||||
}
|
|
||||||
if len(respMsg.Answer) > 0 {
|
|
||||||
for _, answer := range respMsg.Answer {
|
|
||||||
if https, ok := answer.(*mdns.HTTPS); ok && https.Hdr.Name == mdns.Fqdn(domain) {
|
|
||||||
for _, value := range https.Value {
|
|
||||||
Record.keypair[value.Key().String()] = value.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Record.Expire = time.Now().Add(time.Duration(respMsg.Answer[0].Header().Ttl) * time.Second)
|
|
||||||
|
|
||||||
s.updateHTTPS(domain, &Record)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte, error) {
|
func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte, error) {
|
||||||
body := bytes.NewBuffer(b)
|
body := bytes.NewBuffer(b)
|
||||||
req, err := http.NewRequest("POST", s.dohURL, body)
|
req, err := http.NewRequest("POST", s.dohURL, body)
|
||||||
@@ -413,27 +341,6 @@ func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOpt
|
|||||||
return nil, errRecordNotFound
|
return nil, errRecordNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DoHNameServer) findRecordsForDomain(domain string, Querytype string) (any, error) {
|
|
||||||
switch Querytype {
|
|
||||||
case "HTTPS":
|
|
||||||
s.RLock()
|
|
||||||
record, found := s.HTTPSCache[domain]
|
|
||||||
s.RUnlock()
|
|
||||||
if !found {
|
|
||||||
return nil, errRecordNotFound
|
|
||||||
}
|
|
||||||
if len(record.keypair) == 0 {
|
|
||||||
return nil, dns_feature.ErrEmptyResponse
|
|
||||||
}
|
|
||||||
if record.Expire.Before(time.Now()) {
|
|
||||||
return nil, errRecordNotFound
|
|
||||||
}
|
|
||||||
return record, nil
|
|
||||||
default:
|
|
||||||
return nil, errors.New("unsupported query type: " + Querytype)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryIP implements Server.
|
// QueryIP implements Server.
|
||||||
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { // nolint: dupl
|
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { // nolint: dupl
|
||||||
fqdn := Fqdn(domain)
|
fqdn := Fqdn(domain)
|
||||||
@@ -496,44 +403,3 @@ func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryHTTPS implements EnhancedServer.
|
|
||||||
func (s *DoHNameServer) QueryHTTPS(ctx context.Context, domain string, disableCache bool) (map[string]string, error) { // nolint: dupl
|
|
||||||
fqdn := Fqdn(domain)
|
|
||||||
|
|
||||||
if disableCache {
|
|
||||||
errors.LogDebug(ctx, "DNS cache is disabled. Querying HTTPS for ", domain, " at ", s.name)
|
|
||||||
} else {
|
|
||||||
Record, err := s.findRecordsForDomain(fqdn, "HTTPS")
|
|
||||||
if err == nil || err == dns_feature.ErrEmptyResponse {
|
|
||||||
errors.LogDebugInner(ctx, err, s.name, " cache HIT ", domain, " -> ", Record.(HTTPSRecord).keypair)
|
|
||||||
return Record.(HTTPSRecord).keypair, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sub := s.pub.Subscribe(fqdn + "HTTPS")
|
|
||||||
defer sub.Close()
|
|
||||||
done := make(chan interface{})
|
|
||||||
go func() {
|
|
||||||
if sub != nil {
|
|
||||||
select {
|
|
||||||
case <-sub.Wait():
|
|
||||||
case <-ctx.Done():
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
s.sendHTTPSQuery(ctx, fqdn)
|
|
||||||
|
|
||||||
for {
|
|
||||||
Record, err := s.findRecordsForDomain(fqdn, "HTTPS")
|
|
||||||
if err != errRecordNotFound {
|
|
||||||
return Record.(*HTTPSRecord).keypair, err
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, ctx.Err()
|
|
||||||
case <-done:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -24,13 +24,6 @@ type Client interface {
|
|||||||
LookupIP(domain string, option IPOption) ([]net.IP, error)
|
LookupIP(domain string, option IPOption) ([]net.IP, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type EnhancedClient interface {
|
|
||||||
Client
|
|
||||||
|
|
||||||
// LookupHTTPS returns HTTPS records for the given domain.
|
|
||||||
LookupHTTPS(domain string) (map[string]string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type HostsLookup interface {
|
type HostsLookup interface {
|
||||||
LookupHosts(domain string) *net.Address
|
LookupHosts(domain string) *net.Address
|
||||||
}
|
}
|
||||||
|
2
go.mod
2
go.mod
@@ -3,7 +3,7 @@ module github.com/xtls/xray-core
|
|||||||
go 1.24
|
go 1.24
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/OmarTariq612/goech v0.0.1
|
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0
|
||||||
github.com/cloudflare/circl v1.6.0
|
github.com/cloudflare/circl v1.6.0
|
||||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
|
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
|
||||||
github.com/golang/mock v1.7.0-rc.1
|
github.com/golang/mock v1.7.0-rc.1
|
||||||
|
4
go.sum
4
go.sum
@@ -1,5 +1,5 @@
|
|||||||
github.com/OmarTariq612/goech v0.0.1 h1:/0c918Bk1ik65GXDj2k7SOK78DyZr30Jmq9euy1/HXg=
|
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJSGpevP+8Pk5bANX7fJacO2w04aqLiC5I=
|
||||||
github.com/OmarTariq612/goech v0.0.1/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
|
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
|
||||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||||
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
|
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
|
||||||
|
@@ -412,8 +412,6 @@ type TLSConfig struct {
|
|||||||
MasterKeyLog string `json:"masterKeyLog"`
|
MasterKeyLog string `json:"masterKeyLog"`
|
||||||
ServerNameToVerify string `json:"serverNameToVerify"`
|
ServerNameToVerify string `json:"serverNameToVerify"`
|
||||||
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
||||||
ECHConfigList string `json:"echConfigList"`
|
|
||||||
EchKeySets string `json:"echKeySets"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -485,16 +483,6 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
config.VerifyPeerCertInNames = c.VerifyPeerCertInNames
|
config.VerifyPeerCertInNames = c.VerifyPeerCertInNames
|
||||||
|
|
||||||
config.EchConfigList = c.ECHConfigList
|
|
||||||
|
|
||||||
if c.EchKeySets != "" {
|
|
||||||
EchPrivateKey, err := base64.StdEncoding.DecodeString(c.EchKeySets)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("invalid ECH Config", c.EchKeySets)
|
|
||||||
}
|
|
||||||
config.EchKeySets = EchPrivateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -728,6 +716,7 @@ type SocketConfig struct {
|
|||||||
TcpMptcp bool `json:"tcpMptcp"`
|
TcpMptcp bool `json:"tcpMptcp"`
|
||||||
CustomSockopt []*CustomSockoptConfig `json:"customSockopt"`
|
CustomSockopt []*CustomSockoptConfig `json:"customSockopt"`
|
||||||
AddressPortStrategy string `json:"addressPortStrategy"`
|
AddressPortStrategy string `json:"addressPortStrategy"`
|
||||||
|
DialTimeout int32 `json:"dialTimeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -836,6 +825,7 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
|||||||
TcpMptcp: c.TcpMptcp,
|
TcpMptcp: c.TcpMptcp,
|
||||||
CustomSockopt: customSockopts,
|
CustomSockopt: customSockopts,
|
||||||
AddressPortStrategy: addressPortStrategy,
|
AddressPortStrategy: addressPortStrategy,
|
||||||
|
DialTimeout: c.DialTimeout,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
package tls
|
package tls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/OmarTariq612/goech"
|
"github.com/OmarTariq612/goech"
|
||||||
"github.com/cloudflare/circl/hpke"
|
"github.com/cloudflare/circl/hpke"
|
||||||
@@ -11,13 +13,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var cmdECH = &base.Command{
|
var cmdECH = &base.Command{
|
||||||
UsageLine: `{{.Exec}} tls ech [--serverName (string)] [--pem]`,
|
UsageLine: `{{.Exec}} tls ech [--serverName (string)] [--json]`,
|
||||||
Short: `Generate TLS-ECH certificates`,
|
Short: `Generate TLS-ECH certificates`,
|
||||||
Long: `
|
Long: `
|
||||||
Generate TLS-ECH certificates.
|
Generate TLS-ECH certificates.
|
||||||
|
|
||||||
Set serverName to your custom string: {{.Exec}} tls ech --serverName (string)
|
Set serverName to your custom string: {{.Exec}} tls ech --serverName (string)
|
||||||
Generate into pem format: {{.Exec}} tls ech --pem
|
Generate into json format: {{.Exec}} tls ech --json
|
||||||
`, // Enable PQ signature schemes: {{.Exec}} tls ech --pq-signature-schemes-enabled
|
`, // Enable PQ signature schemes: {{.Exec}} tls ech --pq-signature-schemes-enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +29,7 @@ func init() {
|
|||||||
|
|
||||||
var input_pqSignatureSchemesEnabled = cmdECH.Flag.Bool("pqSignatureSchemesEnabled", false, "")
|
var input_pqSignatureSchemesEnabled = cmdECH.Flag.Bool("pqSignatureSchemesEnabled", false, "")
|
||||||
var input_serverName = cmdECH.Flag.String("serverName", "cloudflare-ech.com", "")
|
var input_serverName = cmdECH.Flag.String("serverName", "cloudflare-ech.com", "")
|
||||||
var input_pem = cmdECH.Flag.Bool("pem", false, "True == turn on pem output")
|
var input_json = cmdECH.Flag.Bool("json", false, "True == turn on json output")
|
||||||
|
|
||||||
func executeECH(cmd *base.Command, args []string) {
|
func executeECH(cmd *base.Command, args []string) {
|
||||||
var kem hpke.KEM
|
var kem hpke.KEM
|
||||||
@@ -38,26 +40,30 @@ func executeECH(cmd *base.Command, args []string) {
|
|||||||
kem = hpke.KEM_X25519_HKDF_SHA256
|
kem = hpke.KEM_X25519_HKDF_SHA256
|
||||||
}
|
}
|
||||||
|
|
||||||
echKeySet, err := goech.GenerateECHKeySet(0, *input_serverName, kem, nil)
|
echKeySet, err := goech.GenerateECHKeySet(0, *input_serverName, kem)
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
// Make single key set to a list with only one element
|
configBuffer, _ := echKeySet.ECHConfig.MarshalBinary()
|
||||||
ECHConfigList := make(goech.ECHConfigList, 1)
|
keyBuffer, _ := echKeySet.MarshalBinary()
|
||||||
ECHConfigList[0] = echKeySet.ECHConfig
|
|
||||||
ECHKeySetList := make(goech.ECHKeySetList, 1)
|
|
||||||
ECHKeySetList[0] = echKeySet
|
|
||||||
configBuffer, _ := ECHConfigList.MarshalBinary()
|
|
||||||
keyBuffer, _ := ECHKeySetList.MarshalBinary()
|
|
||||||
configStr, _ := ECHConfigList.ToBase64()
|
|
||||||
keySetStr, _ := ECHKeySetList.ToBase64()
|
|
||||||
|
|
||||||
configPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBuffer}))
|
configPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBuffer}))
|
||||||
keyPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBuffer}))
|
keyPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBuffer}))
|
||||||
if *input_pem {
|
if *input_json {
|
||||||
|
jECHConfigs := map[string]interface{}{
|
||||||
|
"configs": strings.Split(strings.TrimSpace(string(configPEM)), "\n"),
|
||||||
|
}
|
||||||
|
jECHKey := map[string]interface{}{
|
||||||
|
"key": strings.Split(strings.TrimSpace(string(keyPEM)), "\n"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, i := range []map[string]interface{}{jECHConfigs, jECHKey} {
|
||||||
|
content, err := json.MarshalIndent(i, "", " ")
|
||||||
|
common.Must(err)
|
||||||
|
os.Stdout.Write(content)
|
||||||
|
os.Stdout.WriteString("\n")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
os.Stdout.WriteString(configPEM)
|
os.Stdout.WriteString(configPEM)
|
||||||
os.Stdout.WriteString(keyPEM)
|
os.Stdout.WriteString(keyPEM)
|
||||||
} else {
|
|
||||||
os.Stdout.WriteString("ECH config list: \n" + configStr + "\n")
|
|
||||||
os.Stdout.WriteString("ECH Key sets: \n" + keySetStr + "\n")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -513,6 +513,7 @@ type SocketConfig struct {
|
|||||||
TcpMptcp bool `protobuf:"varint,19,opt,name=tcp_mptcp,json=tcpMptcp,proto3" json:"tcp_mptcp,omitempty"`
|
TcpMptcp bool `protobuf:"varint,19,opt,name=tcp_mptcp,json=tcpMptcp,proto3" json:"tcp_mptcp,omitempty"`
|
||||||
CustomSockopt []*CustomSockopt `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"`
|
CustomSockopt []*CustomSockopt `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"`
|
||||||
AddressPortStrategy AddressPortStrategy `protobuf:"varint,21,opt,name=address_port_strategy,json=addressPortStrategy,proto3,enum=xray.transport.internet.AddressPortStrategy" json:"address_port_strategy,omitempty"`
|
AddressPortStrategy AddressPortStrategy `protobuf:"varint,21,opt,name=address_port_strategy,json=addressPortStrategy,proto3,enum=xray.transport.internet.AddressPortStrategy" json:"address_port_strategy,omitempty"`
|
||||||
|
DialTimeout int32 `protobuf:"varint,22,opt,name=dial_timeout,json=dialTimeout,proto3" json:"dial_timeout,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SocketConfig) Reset() {
|
func (x *SocketConfig) Reset() {
|
||||||
@@ -692,6 +693,13 @@ func (x *SocketConfig) GetAddressPortStrategy() AddressPortStrategy {
|
|||||||
return AddressPortStrategy_None
|
return AddressPortStrategy_None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *SocketConfig) GetDialTimeout() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.DialTimeout
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
var File_transport_internet_config_proto protoreflect.FileDescriptor
|
var File_transport_internet_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_transport_internet_config_proto_rawDesc = []byte{
|
var file_transport_internet_config_proto_rawDesc = []byte{
|
||||||
@@ -747,7 +755,7 @@ var file_transport_internet_config_proto_rawDesc = []byte{
|
|||||||
0x28, 0x09, 0x52, 0x03, 0x6f, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
0x28, 0x09, 0x52, 0x03, 0x6f, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a,
|
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a,
|
||||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70,
|
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70,
|
||||||
0x65, 0x22, 0xfd, 0x07, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
0x65, 0x22, 0xa0, 0x08, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
|
0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
|
||||||
0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02, 0x20,
|
0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02, 0x20,
|
||||||
0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, 0x70, 0x72, 0x6f,
|
0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, 0x70, 0x72, 0x6f,
|
||||||
@@ -808,37 +816,39 @@ var file_transport_internet_config_proto_rawDesc = []byte{
|
|||||||
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50,
|
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50,
|
||||||
0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x13, 0x61, 0x64, 0x64,
|
0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x13, 0x61, 0x64, 0x64,
|
||||||
0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
||||||
0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07,
|
0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
|
||||||
0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78,
|
0x18, 0x16, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65,
|
||||||
0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10,
|
0x6f, 0x75, 0x74, 0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64,
|
||||||
0x02, 0x2a, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61,
|
0x65, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50,
|
||||||
0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12,
|
0x72, 0x6f, 0x78, 0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65,
|
||||||
0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55,
|
0x63, 0x74, 0x10, 0x02, 0x2a, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53,
|
||||||
0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f,
|
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53,
|
||||||
0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34,
|
0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b,
|
||||||
0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10,
|
0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55,
|
||||||
0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12,
|
0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f,
|
||||||
0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d,
|
0x49, 0x50, 0x34, 0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50,
|
||||||
0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a,
|
0x36, 0x34, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50,
|
||||||
0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a,
|
0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10,
|
||||||
0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x2a, 0x97, 0x01,
|
0x07, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08,
|
||||||
0x0a, 0x13, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72,
|
0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09,
|
||||||
0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12,
|
0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a,
|
||||||
0x0f, 0x0a, 0x0b, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x01,
|
0x2a, 0x97, 0x01, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74,
|
||||||
0x12, 0x12, 0x0a, 0x0e, 0x53, 0x72, 0x76, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e,
|
0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65,
|
||||||
0x6c, 0x79, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x41,
|
0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c,
|
||||||
0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54,
|
0x79, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x72, 0x76, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||||
0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e,
|
0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x72, 0x76, 0x50, 0x6f,
|
||||||
0x54, 0x78, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x05,
|
0x72, 0x74, 0x41, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x03, 0x12, 0x0f,
|
||||||
0x12, 0x15, 0x0a, 0x11, 0x54, 0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x6e, 0x64, 0x41, 0x64,
|
0x0a, 0x0b, 0x54, 0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x04, 0x12,
|
||||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x06, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
|
0x12, 0x0a, 0x0e, 0x54, 0x78, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c,
|
||||||
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
|
0x79, 0x10, 0x05, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x6e,
|
||||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x06, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f,
|
||||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
|
0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
|
||||||
0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
|
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74,
|
||||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72,
|
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
|
||||||
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
|
||||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79,
|
||||||
|
0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72,
|
||||||
|
0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -128,4 +128,6 @@ message SocketConfig {
|
|||||||
repeated CustomSockopt customSockopt = 20;
|
repeated CustomSockopt customSockopt = 20;
|
||||||
|
|
||||||
AddressPortStrategy address_port_strategy = 21;
|
AddressPortStrategy address_port_strategy = 21;
|
||||||
|
|
||||||
|
int32 dial_timeout = 22;
|
||||||
}
|
}
|
||||||
|
@@ -106,14 +106,6 @@ func lookupIP(domain string, strategy DomainStrategy, localAddr net.Address) ([]
|
|||||||
return ips, err
|
return ips, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func LookupHTTPS(domain string) (map[string]string, error) {
|
|
||||||
if dnsClient == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
HTTPSRecord, err := dnsClient.(dns.EnhancedClient).LookupHTTPS(domain)
|
|
||||||
return HTTPSRecord, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func canLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig) bool {
|
func canLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig) bool {
|
||||||
if dst.Address.Family().IsIP() || dnsClient == nil {
|
if dst.Address.Family().IsIP() || dnsClient == nil {
|
||||||
return false
|
return false
|
||||||
|
@@ -87,8 +87,12 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne
|
|||||||
if sockopt != nil && (sockopt.TcpKeepAliveInterval != 0 || sockopt.TcpKeepAliveIdle != 0) {
|
if sockopt != nil && (sockopt.TcpKeepAliveInterval != 0 || sockopt.TcpKeepAliveIdle != 0) {
|
||||||
goStdKeepAlive = time.Duration(-1)
|
goStdKeepAlive = time.Duration(-1)
|
||||||
}
|
}
|
||||||
|
dialTimeout := time.Second * 16
|
||||||
|
if sockopt != nil && sockopt.DialTimeout > 0 {
|
||||||
|
dialTimeout = time.Second * time.Duration(sockopt.DialTimeout)
|
||||||
|
}
|
||||||
dialer := &net.Dialer{
|
dialer := &net.Dialer{
|
||||||
Timeout: time.Second * 16,
|
Timeout: dialTimeout,
|
||||||
LocalAddr: resolveSrcAddr(dest.Network, src),
|
LocalAddr: resolveSrcAddr(dest.Network, src),
|
||||||
KeepAlive: goStdKeepAlive,
|
KeepAlive: goStdKeepAlive,
|
||||||
}
|
}
|
||||||
|
@@ -444,12 +444,6 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
|||||||
config.KeyLogWriter = writer
|
config.KeyLogWriter = writer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(c.EchConfigList) > 0 || len(c.EchKeySets) > 0 {
|
|
||||||
err := ApplyECH(c, config)
|
|
||||||
if err != nil {
|
|
||||||
errors.LogError(context.Background(), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
@@ -217,8 +217,6 @@ type Config struct {
|
|||||||
// @Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
|
// @Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
|
||||||
// @Critical
|
// @Critical
|
||||||
VerifyPeerCertInNames []string `protobuf:"bytes,17,rep,name=verify_peer_cert_in_names,json=verifyPeerCertInNames,proto3" json:"verify_peer_cert_in_names,omitempty"`
|
VerifyPeerCertInNames []string `protobuf:"bytes,17,rep,name=verify_peer_cert_in_names,json=verifyPeerCertInNames,proto3" json:"verify_peer_cert_in_names,omitempty"`
|
||||||
EchConfigList string `protobuf:"bytes,18,opt,name=ech_config_list,json=echConfigList,proto3" json:"ech_config_list,omitempty"`
|
|
||||||
EchKeySets []byte `protobuf:"bytes,19,opt,name=ech_key_sets,json=echKeySets,proto3" json:"ech_key_sets,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -363,20 +361,6 @@ func (x *Config) GetVerifyPeerCertInNames() []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetEchConfigList() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.EchConfigList
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) GetEchKeySets() []byte {
|
|
||||||
if x != nil {
|
|
||||||
return x.EchKeySets
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
|
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||||
@@ -408,7 +392,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
|||||||
0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a,
|
0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a,
|
||||||
0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46,
|
0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46,
|
||||||
0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59,
|
0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59,
|
||||||
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xe4, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
|
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0x9a, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
|
||||||
0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73,
|
0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73,
|
||||||
0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c,
|
0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c,
|
||||||
0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65,
|
0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65,
|
||||||
@@ -458,19 +442,15 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
|||||||
0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f,
|
0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f,
|
||||||
0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15,
|
0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15,
|
||||||
0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x49, 0x6e,
|
0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x49, 0x6e,
|
||||||
0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x6e,
|
0x4e, 0x61, 0x6d, 0x65, 0x73, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61,
|
||||||
0x66, 0x69, 0x67, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
|
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
||||||
0x65, 0x63, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x20, 0x0a,
|
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68,
|
||||||
0x0c, 0x65, 0x63, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x73, 0x18, 0x13, 0x20,
|
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
||||||
0x01, 0x28, 0x0c, 0x52, 0x0a, 0x65, 0x63, 0x68, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x74, 0x73, 0x42,
|
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
|
||||||
0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58,
|
||||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74,
|
0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e,
|
||||||
0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||||
0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f,
|
0x6f, 0x33,
|
||||||
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
|
||||||
0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72,
|
|
||||||
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
|
||||||
0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -91,8 +91,4 @@ message Config {
|
|||||||
@Critical
|
@Critical
|
||||||
*/
|
*/
|
||||||
repeated string verify_peer_cert_in_names = 17;
|
repeated string verify_peer_cert_in_names = 17;
|
||||||
|
|
||||||
string ech_config_list = 18;
|
|
||||||
|
|
||||||
bytes ech_key_sets = 19;
|
|
||||||
}
|
}
|
||||||
|
@@ -1,255 +0,0 @@
|
|||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/OmarTariq612/goech"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ApplyECH(c *Config, config *tls.Config) error {
|
|
||||||
var ECHConfig []byte
|
|
||||||
var err error
|
|
||||||
|
|
||||||
nameToQuery := c.ServerName
|
|
||||||
var DNSServer string
|
|
||||||
|
|
||||||
// for client
|
|
||||||
if len(c.EchConfigList) != 0 {
|
|
||||||
// direct base64 config
|
|
||||||
if strings.HasPrefix(c.EchConfigList, "base64") {
|
|
||||||
Base64ECHConfigList := c.EchConfigList[len("base64://"):]
|
|
||||||
ECHConfigList, err := goech.ECHConfigListFromBase64(Base64ECHConfigList)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("Failed to unmarshal ECHConfigList: ", err)
|
|
||||||
}
|
|
||||||
ECHConfig, _ = ECHConfigList.MarshalBinary()
|
|
||||||
} else { // query config from dns
|
|
||||||
parts := strings.Split(c.EchConfigList, "+")
|
|
||||||
if len(parts) == 2 {
|
|
||||||
// parse ECH DNS server in format of "example.com+https://1.1.1.1/dns-query"
|
|
||||||
nameToQuery = parts[0]
|
|
||||||
DNSServer = parts[1]
|
|
||||||
} else if len(parts) == 1 {
|
|
||||||
// normal format
|
|
||||||
DNSServer = parts[0]
|
|
||||||
} else {
|
|
||||||
return errors.New("Invalid ECH DNS server format: ", c.EchConfigList)
|
|
||||||
}
|
|
||||||
if nameToQuery == "" {
|
|
||||||
return errors.New("Using DNS for ECH Config needs serverName or use Server format example.com+https://1.1.1.1/dns-query")
|
|
||||||
}
|
|
||||||
ECHConfig, err = QueryRecord(nameToQuery, DNSServer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.EncryptedClientHelloConfigList = ECHConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
// for server
|
|
||||||
if len(c.EchKeySets) != 0 {
|
|
||||||
var keys []tls.EncryptedClientHelloKey
|
|
||||||
KeySets, err := goech.UnmarshalECHKeySetList(c.EchKeySets)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("Failed to unmarshal ECHKeySetList: ", err)
|
|
||||||
}
|
|
||||||
for idx, keySet := range KeySets {
|
|
||||||
ECHConfig, err := keySet.ECHConfig.MarshalBinary()
|
|
||||||
ECHPrivateKey, err := keySet.PrivateKey.MarshalBinary()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("Failed to marshal ECHKey in index: ", idx, "with err: ", err)
|
|
||||||
}
|
|
||||||
keys = append(keys, tls.EncryptedClientHelloKey{
|
|
||||||
Config: ECHConfig,
|
|
||||||
PrivateKey: ECHPrivateKey})
|
|
||||||
}
|
|
||||||
config.EncryptedClientHelloKeys = keys
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type record struct {
|
|
||||||
echConfig []byte
|
|
||||||
expire time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
dnsCache sync.Map
|
|
||||||
// global Lock? I'm not sure if this needs finer grained locks.
|
|
||||||
// If we do this, we will need to nest another layer of struct
|
|
||||||
updating sync.Mutex
|
|
||||||
)
|
|
||||||
|
|
||||||
// QueryRecord returns the ECH config for given domain.
|
|
||||||
// If the record is not in cache or expired, it will query the DNS server and update the cache.
|
|
||||||
func QueryRecord(domain string, server string) ([]byte, error) {
|
|
||||||
val, found := dnsCache.Load(domain)
|
|
||||||
rec, _ := val.(record)
|
|
||||||
if found && rec.expire.After(time.Now()) {
|
|
||||||
errors.LogDebug(context.Background(), "Cache hit for domain: ", domain)
|
|
||||||
return rec.echConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
updating.Lock()
|
|
||||||
defer updating.Unlock()
|
|
||||||
// Try to get cache again after lock, in case another goroutine has updated it
|
|
||||||
// This might happen when the core tring is just stared and multiple goroutines are trying to query the same domain
|
|
||||||
val, found = dnsCache.Load(domain)
|
|
||||||
rec, _ = val.(record)
|
|
||||||
if found && rec.expire.After(time.Now()) {
|
|
||||||
errors.LogDebug(context.Background(), "ECH Config cache hit for domain: ", domain, " after trying to get update lock")
|
|
||||||
return rec.echConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query ECH config from DNS server
|
|
||||||
errors.LogDebug(context.Background(), "Trying to query ECH config for domain: ", domain, " with ECH server: ", server)
|
|
||||||
echConfig, ttl, err := dnsQuery(server, domain)
|
|
||||||
if err != nil {
|
|
||||||
return []byte{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set minimum TTL to 600 seconds
|
|
||||||
if ttl < 600 {
|
|
||||||
ttl = 600
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update cache
|
|
||||||
newRecored := record{
|
|
||||||
echConfig: echConfig,
|
|
||||||
expire: time.Now().Add(time.Second * time.Duration(ttl)),
|
|
||||||
}
|
|
||||||
dnsCache.Store(domain, newRecored)
|
|
||||||
return echConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// dnsQuery is the real func for sending type65 query for given domain to given DNS server.
|
|
||||||
// return ECH config, TTL and error
|
|
||||||
func dnsQuery(server string, domain string) ([]byte, uint32, error) {
|
|
||||||
if server == "xray" {
|
|
||||||
HTTPSRecord, err := internet.LookupHTTPS(domain)
|
|
||||||
if err !=nil {
|
|
||||||
return []byte{}, 0, errors.New("failed to lookup HTTPS record with xray internal DNS: ", err)
|
|
||||||
}
|
|
||||||
ECH := HTTPSRecord["ech"]
|
|
||||||
if ECH == "" {
|
|
||||||
return []byte{}, 0, errors.New("no ech record found")
|
|
||||||
}
|
|
||||||
Base64echConfigList, err := goech.ECHConfigListFromBase64(ECH)
|
|
||||||
if err != nil {
|
|
||||||
return []byte{}, 0, errors.New("failed to unmarshal ECHConfigList: ", err)
|
|
||||||
}
|
|
||||||
echConfigList, _ := Base64echConfigList.MarshalBinary()
|
|
||||||
return echConfigList, 600, nil
|
|
||||||
}
|
|
||||||
m := new(dns.Msg)
|
|
||||||
var dnsResolve []byte
|
|
||||||
m.SetQuestion(dns.Fqdn(domain), dns.TypeHTTPS)
|
|
||||||
// for DOH server
|
|
||||||
if strings.HasPrefix(server, "https://") {
|
|
||||||
// always 0 in DOH
|
|
||||||
m.Id = 0
|
|
||||||
msg, err := m.Pack()
|
|
||||||
if err != nil {
|
|
||||||
return []byte{}, 0, err
|
|
||||||
}
|
|
||||||
// All traffic sent by core should via xray's internet.DialSystem
|
|
||||||
// This involves the behavior of some Android VPN GUI clients
|
|
||||||
tr := &http.Transport{
|
|
||||||
IdleConnTimeout: 90 * time.Second,
|
|
||||||
ForceAttemptHTTP2: true,
|
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
||||||
dest, err := net.ParseDestination(network + ":" + addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn, err := internet.DialSystem(ctx, dest, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
client := &http.Client{
|
|
||||||
Timeout: 5 * time.Second,
|
|
||||||
Transport: tr,
|
|
||||||
}
|
|
||||||
req, err := http.NewRequest("POST", server, bytes.NewReader(msg))
|
|
||||||
if err != nil {
|
|
||||||
return []byte{}, 0, err
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", "application/dns-message")
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return []byte{}, 0, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
respBody, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return []byte{}, 0, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return []byte{}, 0, errors.New("query failed with response code:", resp.StatusCode)
|
|
||||||
}
|
|
||||||
dnsResolve = respBody
|
|
||||||
} else if strings.HasPrefix(server, "udp://") { // for classic udp dns server
|
|
||||||
udpServerAddr := server[len("udp://"):]
|
|
||||||
// default port 53 if not specified
|
|
||||||
if !strings.Contains(udpServerAddr, ":") {
|
|
||||||
udpServerAddr = udpServerAddr + ":53"
|
|
||||||
}
|
|
||||||
dest, err := net.ParseDestination("udp" + ":" + udpServerAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, errors.New("failed to parse udp dns server ", udpServerAddr, " for ECH: ", err)
|
|
||||||
}
|
|
||||||
dnsTimeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
// use xray's internet.DialSystem as mentioned above
|
|
||||||
conn, err := internet.DialSystem(dnsTimeoutCtx, dest, nil)
|
|
||||||
defer conn.Close()
|
|
||||||
if err != nil {
|
|
||||||
return []byte{}, 0, err
|
|
||||||
}
|
|
||||||
msg, err := m.Pack()
|
|
||||||
if err != nil {
|
|
||||||
return []byte{}, 0, err
|
|
||||||
}
|
|
||||||
conn.Write(msg)
|
|
||||||
udpResponse := make([]byte, 512)
|
|
||||||
_, err = conn.Read(udpResponse)
|
|
||||||
if err != nil {
|
|
||||||
return []byte{}, 0, err
|
|
||||||
}
|
|
||||||
dnsResolve = udpResponse
|
|
||||||
}
|
|
||||||
respMsg := new(dns.Msg)
|
|
||||||
err := respMsg.Unpack(dnsResolve)
|
|
||||||
if err != nil {
|
|
||||||
return []byte{}, 0, errors.New("failed to unpack dns response for ECH: ", err)
|
|
||||||
}
|
|
||||||
if len(respMsg.Answer) > 0 {
|
|
||||||
for _, answer := range respMsg.Answer {
|
|
||||||
if https, ok := answer.(*dns.HTTPS); ok && https.Hdr.Name == dns.Fqdn(domain) {
|
|
||||||
for _, v := range https.Value {
|
|
||||||
if echConfig, ok := v.(*dns.SVCBECHConfig); ok {
|
|
||||||
errors.LogDebug(context.Background(), "Get ECH config:", echConfig.String(), " TTL:", respMsg.Answer[0].Header().Ttl)
|
|
||||||
return echConfig.ECH, answer.Header().Ttl, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return []byte{}, 0, errors.New("no ech record found")
|
|
||||||
}
|
|
Reference in New Issue
Block a user