mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-05-06 20:28:40 +00:00
Add classic UDP DNS support for ECH Config
This commit is contained in:
parent
8e5be20307
commit
22463f3256
5 changed files with 91 additions and 57 deletions
|
@ -25,8 +25,8 @@ func ApplyECH(c *Config, config *tls.Config) error {
|
|||
nameToQuery := c.ServerName
|
||||
var DOHServer string
|
||||
|
||||
if len(c.EchConfig) != 0 || len(c.Ech_DOHserver) != 0 {
|
||||
parts := strings.Split(c.Ech_DOHserver, "+")
|
||||
if len(c.EchConfig) != 0 || len(c.Ech_DNSserver) != 0 {
|
||||
parts := strings.Split(c.Ech_DNSserver, "+")
|
||||
if len(parts) == 2 {
|
||||
// parse ECH DOH server in format of "example.com+https://1.1.1.1/dns-query"
|
||||
nameToQuery = parts[0]
|
||||
|
@ -35,7 +35,7 @@ func ApplyECH(c *Config, config *tls.Config) error {
|
|||
// normal format
|
||||
DOHServer = parts[0]
|
||||
} else {
|
||||
return errors.New("Invalid ECH DOH server format: ", c.Ech_DOHserver)
|
||||
return errors.New("Invalid ECH DOH server format: ", c.Ech_DNSserver)
|
||||
}
|
||||
|
||||
if len(c.EchConfig) > 0 {
|
||||
|
@ -133,55 +133,89 @@ func QueryRecord(domain string, server string) ([]byte, error) {
|
|||
// return ECH config, TTL and error
|
||||
func dohQuery(server string, domain string) ([]byte, uint32, error) {
|
||||
m := new(dns.Msg)
|
||||
var dnsResolve []byte
|
||||
m.SetQuestion(dns.Fqdn(domain), dns.TypeHTTPS)
|
||||
// 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)
|
||||
// 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(respBody)
|
||||
err := respMsg.Unpack(dnsResolve)
|
||||
if err != nil {
|
||||
return []byte{}, 0, err
|
||||
return []byte{}, 0, errors.New("failed to unpack dns response for ECH: ", err)
|
||||
}
|
||||
if len(respMsg.Answer) > 0 {
|
||||
for _, answer := range respMsg.Answer {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue