mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-18 05:24:01 +00:00
XDNS finalmask: Support DoH ("https://", tlsConfig, domain fronting, etc., quicConfig, Chrome Client Hello by default) with empty HTTP User-Agent (#7140)
https://github.com/XTLS/Xray-core/pull/7103#issuecomment-6118333973 https://github.com/XTLS/Xray-core/pull/7140#issuecomment-6161808702 https://github.com/XTLS/Xray-core/pull/7140#issuecomment-6167254749 https://github.com/XTLS/Xray-core/pull/7140#issuecomment-6185965142 https://github.com/XTLS/Xray-core/pull/7140#issuecomment-6200775375
This commit is contained in:
@@ -9,12 +9,14 @@ import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
googleuuid "github.com/google/uuid"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/fragment"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/custom"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm"
|
||||
@@ -28,6 +30,7 @@ import (
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/xdns"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/xicmp"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/xmc"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria/congestion/bbr"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
@@ -799,7 +802,10 @@ type XDNSDomain struct {
|
||||
}
|
||||
|
||||
type XDNSResolver struct {
|
||||
Addrs []string `json:"addrs"`
|
||||
Addrs []string `json:"addrs"`
|
||||
Worker int32 `json:"worker"`
|
||||
TlsConfig *TLSConfig `json:"tlsConfig"`
|
||||
QuicConfig *QuicParamsConfig `json:"quicConfig"`
|
||||
}
|
||||
|
||||
type XDNS struct {
|
||||
@@ -834,6 +840,26 @@ func (c *XDNS) Build() (proto.Message, error) {
|
||||
}
|
||||
}
|
||||
for i := range c.Resolvers {
|
||||
worker := c.Resolvers[i].Worker
|
||||
if worker == 0 {
|
||||
worker = 32
|
||||
}
|
||||
var tlsConfig *tls.Config
|
||||
var quicConfig *internet.QuicParams
|
||||
if c.Resolvers[i].TlsConfig != nil {
|
||||
proto, err := c.Resolvers[i].TlsConfig.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig = proto.(*tls.Config)
|
||||
}
|
||||
if c.Resolvers[i].QuicConfig != nil {
|
||||
proto, err := c.Resolvers[i].QuicConfig.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
quicConfig = proto.(*internet.QuicParams)
|
||||
}
|
||||
for j := range c.Resolvers[i].Addrs {
|
||||
var u *url.URL
|
||||
var e error
|
||||
@@ -845,18 +871,21 @@ func (c *XDNS) Build() (proto.Message, error) {
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
var addr string
|
||||
switch u.Scheme {
|
||||
case "tcp", "udp":
|
||||
host := u.Hostname()
|
||||
port := u.Port()
|
||||
if port == "" {
|
||||
port = "53"
|
||||
}
|
||||
addr = net.JoinHostPort(host, port)
|
||||
case "https":
|
||||
addr = u.String()
|
||||
default:
|
||||
return nil, errors.New("invalid protocol")
|
||||
}
|
||||
var host, port string
|
||||
host = u.Hostname()
|
||||
port = u.Port()
|
||||
if port == "" {
|
||||
port = "53"
|
||||
}
|
||||
resolvers = append(resolvers, &xdns.ResolverProto{Type: u.Scheme, Addr: net.JoinHostPort(host, port)})
|
||||
resolvers = append(resolvers, &xdns.ResolverProto{Type: u.Scheme, Addr: addr, Worker: worker, TlsConfig: tlsConfig, QuicConfig: quicConfig})
|
||||
}
|
||||
}
|
||||
if c.ExtraPoll < 0 || c.ExtraPoll > 3 {
|
||||
@@ -1149,6 +1178,91 @@ type QuicParamsConfig struct {
|
||||
DisableStatelessReset bool `json:"disableStatelessReset"`
|
||||
}
|
||||
|
||||
func (config *QuicParamsConfig) Build() (proto.Message, error) {
|
||||
profile := strings.ToLower(config.BbrProfile)
|
||||
switch profile {
|
||||
case "", string(bbr.ProfileConservative), string(bbr.ProfileStandard), string(bbr.ProfileAggressive):
|
||||
if profile == "" {
|
||||
profile = string(bbr.ProfileStandard)
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("unknown bbr profile")
|
||||
}
|
||||
|
||||
up, err := config.BrutalUp.Bps()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
down, err := config.BrutalDown.Bps()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if up > 0 && up < 65536 {
|
||||
return nil, errors.New("BrutalUp must be at least 65536 bytes per second")
|
||||
}
|
||||
if down > 0 && down < 65536 {
|
||||
return nil, errors.New("BrutalDown must be at least 65536 bytes per second")
|
||||
}
|
||||
|
||||
config.Congestion = strings.ToLower(config.Congestion)
|
||||
switch config.Congestion {
|
||||
case "", "brutal", "reno", "bbr":
|
||||
case "force-brutal":
|
||||
if up == 0 {
|
||||
return nil, errors.New("force-brutal requires up")
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("unknown congestion control: ", config.Congestion, ", valid values: reno, bbr, brutal, force-brutal")
|
||||
}
|
||||
|
||||
if config.InitStreamReceiveWindow > 0 && config.InitStreamReceiveWindow < 16384 {
|
||||
return nil, errors.New("InitStreamReceiveWindow must be at least 16384")
|
||||
}
|
||||
if config.MaxStreamReceiveWindow > 0 && config.MaxStreamReceiveWindow < 16384 {
|
||||
return nil, errors.New("MaxStreamReceiveWindow must be at least 16384")
|
||||
}
|
||||
if config.InitConnectionReceiveWindow > 0 && config.InitConnectionReceiveWindow < 16384 {
|
||||
return nil, errors.New("InitConnectionReceiveWindow must be at least 16384")
|
||||
}
|
||||
if config.MaxConnectionReceiveWindow > 0 && config.MaxConnectionReceiveWindow < 16384 {
|
||||
return nil, errors.New("MaxConnectionReceiveWindow must be at least 16384")
|
||||
}
|
||||
if config.MaxIdleTimeout != 0 && (config.MaxIdleTimeout < 4 || config.MaxIdleTimeout > 120) {
|
||||
return nil, errors.New("MaxIdleTimeout must be between 4 and 120")
|
||||
}
|
||||
if config.KeepAlivePeriod != 0 && (config.KeepAlivePeriod < 2 || config.KeepAlivePeriod > 60) {
|
||||
return nil, errors.New("KeepAlivePeriod must be between 2 and 60")
|
||||
}
|
||||
if config.MaxIncomingStreams != 0 && config.MaxIncomingStreams < 8 {
|
||||
return nil, errors.New("MaxIncomingStreams must be at least 8")
|
||||
}
|
||||
|
||||
if config.Debug {
|
||||
os.Setenv("HYSTERIA_BBR_DEBUG", "true")
|
||||
os.Setenv("HYSTERIA_BRUTAL_DEBUG", "true")
|
||||
}
|
||||
|
||||
return &internet.QuicParams{
|
||||
Congestion: config.Congestion,
|
||||
BbrProfile: profile,
|
||||
BrutalUp: up,
|
||||
BrutalDown: down,
|
||||
BrutalDisableLossCompensation: config.BrutalDisableLossCompensation,
|
||||
InitStreamReceiveWindow: config.InitStreamReceiveWindow,
|
||||
MaxStreamReceiveWindow: config.MaxStreamReceiveWindow,
|
||||
InitConnReceiveWindow: config.InitConnectionReceiveWindow,
|
||||
MaxConnReceiveWindow: config.MaxConnectionReceiveWindow,
|
||||
MaxIdleTimeout: config.MaxIdleTimeout,
|
||||
KeepAlivePeriod: config.KeepAlivePeriod,
|
||||
DisablePathMtuDiscovery: config.DisablePathMTUDiscovery,
|
||||
DisableChromeParrot: config.DisableChromeParrot,
|
||||
DisableGSO: config.DisableGSO,
|
||||
MaxIncomingStreams: config.MaxIncomingStreams,
|
||||
DisableStatelessReset: config.DisableStatelessReset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type FinalMask struct {
|
||||
Tcp []Mask `json:"tcp"`
|
||||
Udp []Mask `json:"udp"`
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria/congestion/bbr"
|
||||
)
|
||||
|
||||
type TransportProtocol string
|
||||
@@ -228,102 +226,25 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
||||
|
||||
if c.FinalMask != nil {
|
||||
for _, mask := range c.FinalMask.Tcp {
|
||||
u, err := mask.Build(true)
|
||||
proto, err := mask.Build(true)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to build mask with type ", mask.Type).Base(err)
|
||||
}
|
||||
config.Tcpmasks = append(config.Tcpmasks, serial.ToTypedMessage(u))
|
||||
config.Tcpmasks = append(config.Tcpmasks, serial.ToTypedMessage(proto))
|
||||
}
|
||||
for _, mask := range c.FinalMask.Udp {
|
||||
u, err := mask.Build(false)
|
||||
proto, err := mask.Build(false)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to build mask with type ", mask.Type).Base(err)
|
||||
}
|
||||
config.Udpmasks = append(config.Udpmasks, serial.ToTypedMessage(u))
|
||||
config.Udpmasks = append(config.Udpmasks, serial.ToTypedMessage(proto))
|
||||
}
|
||||
if c.FinalMask.QuicParams != nil {
|
||||
profile := strings.ToLower(c.FinalMask.QuicParams.BbrProfile)
|
||||
switch profile {
|
||||
case "", string(bbr.ProfileConservative), string(bbr.ProfileStandard), string(bbr.ProfileAggressive):
|
||||
if profile == "" {
|
||||
profile = string(bbr.ProfileStandard)
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("unknown bbr profile")
|
||||
}
|
||||
|
||||
up, err := c.FinalMask.QuicParams.BrutalUp.Bps()
|
||||
proto, err := c.FinalMask.QuicParams.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
down, err := c.FinalMask.QuicParams.BrutalDown.Bps()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if up > 0 && up < 65536 {
|
||||
return nil, errors.New("BrutalUp must be at least 65536 bytes per second")
|
||||
}
|
||||
if down > 0 && down < 65536 {
|
||||
return nil, errors.New("BrutalDown must be at least 65536 bytes per second")
|
||||
}
|
||||
|
||||
c.FinalMask.QuicParams.Congestion = strings.ToLower(c.FinalMask.QuicParams.Congestion)
|
||||
switch c.FinalMask.QuicParams.Congestion {
|
||||
case "", "brutal", "reno", "bbr":
|
||||
case "force-brutal":
|
||||
if up == 0 {
|
||||
return nil, errors.New("force-brutal requires up")
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("unknown congestion control: ", c.FinalMask.QuicParams.Congestion, ", valid values: reno, bbr, brutal, force-brutal")
|
||||
}
|
||||
|
||||
if c.FinalMask.QuicParams.InitStreamReceiveWindow > 0 && c.FinalMask.QuicParams.InitStreamReceiveWindow < 16384 {
|
||||
return nil, errors.New("InitStreamReceiveWindow must be at least 16384")
|
||||
}
|
||||
if c.FinalMask.QuicParams.MaxStreamReceiveWindow > 0 && c.FinalMask.QuicParams.MaxStreamReceiveWindow < 16384 {
|
||||
return nil, errors.New("MaxStreamReceiveWindow must be at least 16384")
|
||||
}
|
||||
if c.FinalMask.QuicParams.InitConnectionReceiveWindow > 0 && c.FinalMask.QuicParams.InitConnectionReceiveWindow < 16384 {
|
||||
return nil, errors.New("InitConnectionReceiveWindow must be at least 16384")
|
||||
}
|
||||
if c.FinalMask.QuicParams.MaxConnectionReceiveWindow > 0 && c.FinalMask.QuicParams.MaxConnectionReceiveWindow < 16384 {
|
||||
return nil, errors.New("MaxConnectionReceiveWindow must be at least 16384")
|
||||
}
|
||||
if c.FinalMask.QuicParams.MaxIdleTimeout != 0 && (c.FinalMask.QuicParams.MaxIdleTimeout < 4 || c.FinalMask.QuicParams.MaxIdleTimeout > 120) {
|
||||
return nil, errors.New("MaxIdleTimeout must be between 4 and 120")
|
||||
}
|
||||
if c.FinalMask.QuicParams.KeepAlivePeriod != 0 && (c.FinalMask.QuicParams.KeepAlivePeriod < 2 || c.FinalMask.QuicParams.KeepAlivePeriod > 60) {
|
||||
return nil, errors.New("KeepAlivePeriod must be between 2 and 60")
|
||||
}
|
||||
if c.FinalMask.QuicParams.MaxIncomingStreams != 0 && c.FinalMask.QuicParams.MaxIncomingStreams < 8 {
|
||||
return nil, errors.New("MaxIncomingStreams must be at least 8")
|
||||
}
|
||||
|
||||
if c.FinalMask.QuicParams.Debug {
|
||||
os.Setenv("HYSTERIA_BBR_DEBUG", "true")
|
||||
os.Setenv("HYSTERIA_BRUTAL_DEBUG", "true")
|
||||
}
|
||||
|
||||
config.QuicParams = &internet.QuicParams{
|
||||
Congestion: c.FinalMask.QuicParams.Congestion,
|
||||
BbrProfile: profile,
|
||||
BrutalUp: up,
|
||||
BrutalDown: down,
|
||||
BrutalDisableLossCompensation: c.FinalMask.QuicParams.BrutalDisableLossCompensation,
|
||||
InitStreamReceiveWindow: c.FinalMask.QuicParams.InitStreamReceiveWindow,
|
||||
MaxStreamReceiveWindow: c.FinalMask.QuicParams.MaxStreamReceiveWindow,
|
||||
InitConnReceiveWindow: c.FinalMask.QuicParams.InitConnectionReceiveWindow,
|
||||
MaxConnReceiveWindow: c.FinalMask.QuicParams.MaxConnectionReceiveWindow,
|
||||
MaxIdleTimeout: c.FinalMask.QuicParams.MaxIdleTimeout,
|
||||
KeepAlivePeriod: c.FinalMask.QuicParams.KeepAlivePeriod,
|
||||
DisablePathMtuDiscovery: c.FinalMask.QuicParams.DisablePathMTUDiscovery,
|
||||
DisableChromeParrot: c.FinalMask.QuicParams.DisableChromeParrot,
|
||||
DisableGSO: c.FinalMask.QuicParams.DisableGSO,
|
||||
MaxIncomingStreams: c.FinalMask.QuicParams.MaxIncomingStreams,
|
||||
DisableStatelessReset: c.FinalMask.QuicParams.DisableStatelessReset,
|
||||
}
|
||||
config.QuicParams = proto.(*internet.QuicParams)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user